use crate::target::*;
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub enum Literal {
    String(String),
    Number(String),
    ForeignFunction(String),
}
impl<T: Target> Compile<T> for Literal {
    fn compile(self) -> Result<String, Error> {
        match self {
            Self::String(s) => Ok(T::push(T::string(T::quote(s)))),
            Self::Number(n) => Ok(T::push(T::number(n))),
            Self::ForeignFunction(f) => Ok(T::push(T::foreign_func(f))),
        }
    }
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub struct FnCall(pub Box<Value>, pub Vec<Value>);
impl<T: Target> Compile<T> for FnCall {
    fn compile(self) -> Result<String, Error> {
        let FnCall(function, arguments) = self;
        let compiled_args = arguments
            .iter()
            .rev()
            .map(|arg| {
                let value = arg.clone();
                Compile::<T>::compile(value).unwrap()
            })
            .map(T::copy)
            .collect::<String>();
        if let Value::Name(Name::DotName(head, idents)) = (*function).clone() {
            let actual_idents = idents[..idents.len() - 1].to_vec();
            let Identifier(method_name) = &idents[idents.len() - 1];
            Ok(compiled_args
                + &Compile::<T>::compile(Name::DotName(head, actual_idents))?
                + &T::method_call(T::string(T::quote(method_name))))
        } else {
            Ok(compiled_args + &T::call(Compile::<T>::compile((*function).clone())?))
        }
    }
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub struct Identifier(pub String);
impl<T: Target> Compile<T> for Identifier {
    fn compile(self) -> Result<String, Error> {
        let Identifier(name) = self;
        Ok(T::push(T::string(T::quote(name))))
    }
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub struct Function(pub Vec<Identifier>, pub Suite);
impl<T: Target> Compile<T> for Function {
    fn compile(self) -> Result<String, Error> {
        let Function(parameters, body) = self;
        let stores = parameters
            .iter()
            .map(|s| T::store(Compile::<T>::compile((*s).clone()).unwrap()))
            .collect::<String>();
        Ok(T::push(T::func(stores + &Compile::<T>::compile(body)?)))
    }
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub struct FunctionDef(pub Name, pub Function);
impl<T: Target> Compile<T> for FunctionDef {
    fn compile(self) -> Result<String, Error> {
        let FunctionDef(name, function) = self;
        Ok(Compile::<T>::compile(Expr::Assignment(name, Value::Function(function)))?)
    }
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub enum Value {
    Name(Name),
    Literal(Literal),
    FnCall(FnCall),
    Function(Function),
}
impl<T: Target> Compile<T> for Value {
    fn compile(self) -> Result<String, Error> {
        match self {
            Self::Name(name) => match name {
                Name::Name(n) => Compile::<T>::compile(n).and_then(|n| Ok(T::load(n))),
                otherwise => Compile::<T>::compile(otherwise),
            },
            Self::Literal(l) => Compile::<T>::compile(l),
            Self::FnCall(f) => Compile::<T>::compile(f),
            Self::Function(f) => Compile::<T>::compile(f),
        }
    }
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub enum Name {
    Name(Identifier),
    IndexName(Box<Value>, Vec<Value>),
    DotName(Box<Value>, Vec<Identifier>),
}
impl<T: Target> Compile<T> for Name {
    fn compile(self) -> Result<String, Error> {
        match self {
            Self::Name(n) => Compile::<T>::compile(n),
            Self::DotName(head, tail) => Ok(T::dotname((*head).clone(), tail)),
            Self::IndexName(head, tail) => Ok(T::indexname((*head).clone(), tail)),
        }
    }
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub enum Expr {
    Assignment(Name, Value),
    WhileLoop(Value, Suite),
    IfThenElse(Value, Suite, Suite),
    FunctionDef(FunctionDef),
    StructDef(StructDef),
    Value(Value),
}
impl<T: Target> Compile<T> for Expr {
    fn compile(self) -> Result<String, Error> {
        match self {
            Self::Assignment(name, value) => match name {
                Name::Name(n) => Ok(T::store(T::copy(Compile::<T>::compile(value)?) + &Compile::<T>::compile(n)?)),
                otherwise => Ok(T::assign(T::copy(Compile::<T>::compile(value)?) + &Compile::<T>::compile(otherwise)?)),
            },
            Self::WhileLoop(condition, body) => {
                Ok(T::while_loop(Compile::<T>::compile(condition)?, Compile::<T>::compile(body)?))
            }
            Self::IfThenElse(condition, then_body, else_body) => Ok(T::if_then_else(
                Compile::<T>::compile(condition)?,
                Compile::<T>::compile(then_body)?,
                Compile::<T>::compile(else_body)?,
            )),
            Self::FunctionDef(function_def) => Ok(Compile::<T>::compile(function_def)?),
            Self::StructDef(struct_def) => Ok(Compile::<T>::compile(struct_def)?),
            Self::Value(value) => Ok(Compile::<T>::compile(value)?),
        }
    }
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub struct Suite(pub Vec<Expr>);
impl<T: Target> Compile<T> for Suite {
    fn compile(self) -> Result<String, Error> {
        let Suite(exprs) = self;
        Ok(exprs
            .iter()
            .map(|c| Compile::<T>::compile(c.clone()).unwrap())
            .collect::<String>())
    }
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub struct StructDef(pub Name, pub Vec<FunctionDef>);
impl<T: Target> Compile<T> for StructDef {
    fn compile(self) -> Result<String, Error> {
        let StructDef(name, function_defs) = self;
        let mut exprs = vec![Expr::Assignment(
            Name::Name(Identifier("self".to_string())),
            Value::FnCall(FnCall(
                Box::new(Value::Name(Name::Name(Identifier("dict".to_string())))),
                vec![],
            )),
        )];
        let self_function_defs = function_defs
            .iter()
            .map(|f| {
                let FunctionDef(name, fun) = f;
                let result_name;
                match name {
                    Name::DotName(_, other_names) => result_name = other_names.clone(),
                    Name::Name(ident) => {
                        result_name = vec![ident.clone()];
                    }
                    _ => {
                        
                        result_name = vec![Identifier("BAD".to_string())];
                    }
                }
                FunctionDef(
                    Name::DotName(
                        Box::new(Value::Name(Name::Name(Identifier("self".to_string())))),
                        result_name,
                    ),
                    fun.clone(),
                )
            })
            .map(|f| Expr::FunctionDef(f.clone()))
            .collect::<Vec<Expr>>();
        exprs.extend(self_function_defs);
        exprs.push(Expr::Value(Value::Name(Name::Name(Identifier(
            "self".to_string(),
        )))));
        let body = Suite(exprs);
        let constructor: Function = Function(vec![], body);
        Ok(Compile::<T>::compile(Expr::Assignment(name, Value::Function(constructor)))?)
    }
}