use std::{collections::HashMap, collections::VecDeque, num::ParseFloatError};

use crate::{
    context::Function,
    parser::{Command, CommandType},
};

#[derive(Clone, Debug)]
pub struct Scope<'a> {
    pub(crate) parent: Option<&'a Scope<'a>>,
    pub(crate) functions: HashMap<String, Function>,
    // TODO: Use a weakref here for less cloning.
    pub(crate) list: Vec<String>,
    pub(crate) commands: VecDeque<Command>,
}

impl Scope<'_> {
    pub(crate) fn child(&self, commands: Vec<Command>) -> Scope {
        let body;
        if let Some(command) = commands.first() {
            if let CommandType::Chain(c) = &command.kind {
                body = c
            } else {
                body = &commands
            }
        } else {
            body = &commands
        }
        Scope {
            parent: Some(self),
            functions: HashMap::new(),
            list: vec![],
            commands: VecDeque::from(body.clone()),
        }
    }

    pub(crate) fn as_numbers(&mut self) -> Result<Vec<f64>, String> {
        self.list
            .iter()
            .map(|a| a.parse::<f64>())
            .collect::<Result<Vec<f64>, ParseFloatError>>()
            // TODO: Add function name, parameter index and name to error.
            .map_err(|_| "Couldn't parse numbers".to_string())
    }

    pub(crate) fn get_arg(&mut self) -> Option<Command> {
        self.commands.pop_front()
    }

    pub(crate) fn get_func(&self, name: &str) -> Option<&Function> {
        self.functions.get(name)
    }

    pub fn new() -> Self {
        Scope {
            functions: HashMap::new(),
            list: Vec::new(),
            parent: None,
            commands: VecDeque::new(),
        }
    }
}

impl Default for Scope<'_> {
    fn default() -> Self {
        Scope::new()
    }
}