use crate::*;


pub struct NamedSwitchQuery<'a> {
    /// The borrowed switchboard instance.
    pub switchboard: &'a mut Switchboard,
    /// Long switch name.
    pub name: String,
    /// Short switch name.
    pub short: Option<char>,
    /// The default value if the switch has been provided with no value.
    pub quick: Option<String>,
    /// Documentation string.
    pub doc: Option<String>,
    /// True if this query has already been committed.
    pub committed: bool,
}

impl NamedSwitchQuery<'_> {
    pub fn doc(mut self, doc: &str) -> Self {
        self.doc = Some(doc.to_string());
        self
    }

    pub fn short(mut self, short: char) -> Self {
        if !short.is_ascii_alphanumeric() {
            fatal!("Invalid short name for argument: {short:?}");
        }
        self.short = Some(short);
        self
    }

    pub fn quick(mut self, value: &str) -> Self {
        self.quick = Some(value.to_string());
        self
    }

    pub fn optional(&mut self) {
        let value = self.get_value();
        self.insert(value);
    }

    pub fn default(&mut self, default: &str) {
        match self.get_value() {
            Some(value) => self.insert(Some(value)),
            None => self.insert(Some(Some(default.to_string()))),
        };
    }

    pub fn required(&mut self) {
        match self.get_value() {
            Some(value) => self.insert(Some(value)),
            None => {
                let error = SwitchboardError::MissingNamed(self.debug_name());
                self.switchboard.errors.push(error);
            }
        }
    }

    fn insert(&mut self, value: Option<Option<String>>) {
        let queried = QueriedValue {
            doc: self.doc.clone(),
            name: self.name.clone(),
            variant: QueryVariant::Named(self.debug_name()),
            value,
        };
        if let Some(_) = self.switchboard.values.insert(self.name.clone(), queried) {
            error!("Duplicate query for name {:?}", self.name.clone());
        }
        self.committed = true;
    }

    fn get_value(&mut self) -> Option<Option<String>> {
        // Find all matches first.
        let mut matches = Vec::new();
        for (i, switch) in self.switchboard.switches.iter().enumerate() {
            match &switch.0 {
                SwitchName::Short(other) => if let Some(short) = self.short {
                    if short == *other { matches.push(i) }
                }
                SwitchName::Long(other) => if self.name == *other {
                    matches.push(i);
                },
            };
        }
        // Return a value.
        match matches.len() {
            0 => None,
            1 => {
                let found = self.switchboard.switches.remove(matches[0]).1;
                match found {
                    Some(string) => Some(Some(string)),
                    None => Some(self.quick.clone()),
                }
            }
            _ => {
                let error = SwitchboardError::Repeated(self.debug_name());
                self.switchboard.errors.push(error);
                None
            }
        }
    }

    fn debug_name(&self) -> String {
        let mut debug_name = format!("--{}", self.name);
        if let Some(short) = self.short {
            debug_name.push_str(&format!(" (-{})", short));
        }
        debug_name
    }
}

impl Drop for NamedSwitchQuery<'_> {
    fn drop(&mut self) {
        if !self.committed {
            self.optional();
        }
    }
}



pub struct PositionalSwitchQuery<'a> {
    /// The borrowed switchboard instance.
    pub switchboard: &'a mut Switchboard,
    /// The display name of this argument.
    pub name: String,
    /// Documentation string.
    pub doc: Option<String>,
    /// True if this query has already been committed.
    pub committed: bool,
}

impl PositionalSwitchQuery<'_> {
    pub fn doc(mut self, doc: &str) -> Self {
        self.doc = Some(doc.to_string());
        self
    }

    pub fn optional(&mut self) {
        let value = self.switchboard.pop();
        self.insert(value);
    }

    pub fn default(&mut self, default: &str) {
        match self.switchboard.pop() {
            Some(value) => self.insert(Some(value)),
            None => self.insert(Some(default.to_string())),
        };
    }

    pub fn required(&mut self) {
        match self.switchboard.pop() {
            Some(value) => self.insert(Some(value)),
            None => {
                let error = SwitchboardError::MissingPositional(self.name.clone());
                self.switchboard.errors.push(error);
            }
        }
    }

    fn insert(&mut self, value: Option<String>) {
        let queried = QueriedValue {
            doc: self.doc.clone(),
            name: self.name.clone(),
            variant: QueryVariant::Positional(self.switchboard.i),
            value: Some(value),
        };
        if let Some(_) = self.switchboard.values.insert(self.name.clone(), queried) {
            error!("Duplicate query for name {:?}", self.name.clone());
        }
        self.committed = true;
    }
}

impl Drop for PositionalSwitchQuery<'_> {
    fn drop(&mut self) {
        if !self.committed {
            self.optional();
        }
    }
}