mod non_interactive;
mod terminal;
use crate::{Confirm, Input, Password, Select};
use crate::{InteractionError, InteractiveContext, PromptType};
use dialoguer::theme;
use duplicate::duplicate_item;
use lazy_static::lazy_static;
use non_interactive::PseudoInteractive;
lazy_static! {
    static ref THEME: Box<dyn theme::Theme + Send + Sync> = {
        use dialoguer::theme;
        use pijul_config::{self as config, Choice};
        if let Ok((config, _)) = config::Global::load() {
            let color_choice = config.colors.unwrap_or_default();
            match color_choice {
                Choice::Auto | Choice::Always => Box::<theme::ColorfulTheme>::default(),
                Choice::Never => Box::new(theme::SimpleTheme),
            }
        } else {
            Box::<theme::ColorfulTheme>::default()
        }
    };
}
pub trait BasePrompt<T> {
    fn set_prompt(&mut self, prompt: String);
    fn interact(&mut self) -> Result<T, InteractionError>;
}
pub trait DefaultPrompt<T>: BasePrompt<T> {
    fn set_default(&mut self, value: T);
}
pub trait ValidationPrompt<T>: BasePrompt<T> {
    fn allow_empty(&mut self, empty: bool);
    fn set_validator(&mut self, validator: Box<dyn Fn(&T) -> Result<(), String>>);
}
pub trait PasswordPrompt<T>: ValidationPrompt<T> {
    fn set_confirmation(&mut self, confirm_prompt: String, mismatch_err: String);
}
pub trait TextPrompt<T>: ValidationPrompt<T> + DefaultPrompt<T> {
    fn set_inital_text(&mut self, text: String);
}
pub trait SelectionPrompt<T>: DefaultPrompt<T> {
    fn add_items(&mut self, items: &[String]);
}
#[duplicate_item(
    handler         prompt_type                 return_type;
    [Confirm]       [PromptType::Confirm]       [bool];
    [Input]         [PromptType::Input]         [String];
    [Select]        [PromptType::Select]        [usize];
    [Password]      [PromptType::Password]      [String];
)]
impl handler {
        pub fn new() -> Result<Self, InteractionError> {
        Ok(Self(match crate::get_context()? {
            InteractiveContext::Terminal => Box::new(terminal::handler::with_theme(THEME.as_ref())),
            InteractiveContext::NotInteractive => Box::new(PseudoInteractive::new(prompt_type)),
        }))
    }
        pub fn set_prompt(&mut self, prompt: String) {
        self.0.set_prompt(prompt);
    }
        pub fn with_prompt<S: ToString>(&mut self, prompt: S) -> &mut Self {
        self.set_prompt(prompt.to_string());
        self
    }
            pub fn interact(&mut self) -> Result<return_type, InteractionError> {
        self.0.interact()
    }
}
#[duplicate_item(
    handler         return_type;
    [Confirm]       [bool];
    [Input]         [String];
    [Select]   [usize];
)]
impl handler {
        pub fn set_default(&mut self, value: return_type) {
        self.0.set_default(value);
    }
        pub fn with_default<I: Into<return_type>>(&mut self, value: I) -> &mut Self {
        self.set_default(value.into());
        self
    }
}
impl Select {
        pub fn add_items<S: ToString>(&mut self, items: &[S]) {
        let string_items: Vec<String> = items.iter().map(ToString::to_string).collect();
        self.0.add_items(string_items.as_slice());
    }
                pub fn with_items<S: ToString>(&mut self, items: &[S]) -> &mut Self {
        self.add_items(items);
        self
    }
}
impl Password {
        pub fn set_confirmation<S: ToString>(&mut self, confirm_prompt: S, mismatch_err: S) {
        self.0
            .set_confirmation(confirm_prompt.to_string(), mismatch_err.to_string());
    }
        pub fn with_confirmation<S: ToString>(
        &mut self,
        confirm_prompt: S,
        mismatch_err: S,
    ) -> &mut Self {
        self.set_confirmation(confirm_prompt, mismatch_err);
        self
    }
}
#[duplicate_item(
    handler         prompt_type;
    [Input]         [PromptType::Input];
    [Password]      [PromptType::Password];
)]
impl handler {
        pub fn set_allow_empty(&mut self, empty: bool) {
        self.0.allow_empty(empty);
    }
        pub fn with_allow_empty(&mut self, empty: bool) -> &mut Self {
        self.set_allow_empty(empty);
        self
    }
            pub fn set_validator<V, E>(&mut self, validator: V)
    where
        V: Fn(&String) -> Result<(), E> + 'static,
        E: ToString,
    {
        self.0
            .set_validator(Box::new(move |input| match validator(input) {
                Ok(()) => Ok(()),
                Err(e) => Err(e.to_string()),
            }));
    }
        pub fn with_validator<V, E>(&mut self, validator: V) -> &mut Self
    where
        V: Fn(&String) -> Result<(), E> + 'static,
        E: ToString,
    {
        self.set_validator(validator);
        self
    }
}
impl Input {
    pub fn set_inital_text<S: ToString>(&mut self, text: S) {
        self.0.set_inital_text(text.to_string());
    }
    pub fn with_initial_text<S: ToString>(&mut self, text: S) -> &mut Self {
        self.set_inital_text(text);
        self
    }
}