use std::collections::HashMap;
use std::fmt;

use crate::error::CrimResult;

use crate::{Identifier,IdentifierValue};

pub type Context = HashMap<String,Value>;

pub trait MappedStructure<'a> {
    fn get_value(&'a self, id: &str) -> Option<MappedValue<'a>>;
}

impl<'a> MappedStructure<'a> for Context {
    fn get_value(&'a self, id: &str) -> Option<MappedValue<'a>> {
        self.get(id).map(|v|Into::<MappedValue<'a>>::into(v))
    }
}

pub trait MappedList<'a> {
    fn get_index(&'a self, id: usize) -> Option<MappedValue<'a>>;
    fn for_each(&'a self, func: &dyn Fn(&MappedValue<'a>, &mut String) -> CrimResult<()>) -> CrimResult<String>;
}
    
impl<'a, T: MappedStructure<'a>> MappedList<'a> for Vec<T> {
    fn get_index(&'a self, id: usize) -> Option<MappedValue<'a>> {
        if let Some(x) = self.get(id) {
            Some(MappedValue::<'a>::Struct(x))
        } else {
            None
        }
    }

    fn for_each(&'a self, func: &dyn Fn(&MappedValue<'a>, &mut String) -> CrimResult<()>) -> CrimResult<String> {
        let mut buf = String::new();
        for i in self {
            func( &MappedValue::Struct(i), &mut buf )?;
        }
        Ok(buf)
    }
}

impl<'a> MappedList<'a> for Vec<Value> {
    fn get_index(&'a self, id: usize) -> Option<MappedValue<'a>> {
        if let Some(x) = self.get(id) {
            Some(x.into())
        } else {
            None
        }
    }

    fn for_each(&'a self, func: &dyn Fn(&MappedValue<'a>, &mut String) -> CrimResult<()>) -> CrimResult<String> {
        let mut buf = String::new();
        for i in self {
            func( &Into::<MappedValue<'a>>::into( i ), &mut buf )?;
        }
        Ok(buf)
    }
}

impl<'a> MappedList<'a> for Vec<MappedValue<'a>> {
    fn get_index(&'a self, id: usize) -> Option<MappedValue<'a>> {
        if let Some(x) = self.get(id) {
            Some(*x)
        } else {
            None
        }
    }

    fn for_each(&'a self, func: &dyn Fn(&MappedValue<'a>, &mut String) -> CrimResult<()>) -> CrimResult<String> {
        let mut buf = String::new();
        for i in self {
            func( i, &mut buf )?;
        }
        Ok(buf)
    }
}

pub type MappedHash<'a> = HashMap<String, MappedValue<'a>>;

impl<'a> MappedStructure<'a> for MappedHash<'a> {
    fn get_value(&'a self, id: &str) -> Option<MappedValue<'a>> {
        self.get(id).copied()
    }
}

#[derive(Copy,Clone)]
pub enum MappedValue<'a> {
    Struct(&'a dyn MappedStructure<'a>),
    List(&'a dyn MappedList<'a>),
    
    Str(&'a str),
    String(&'a String),
    Int8(i8),
    Int16(i16),
    Int32(i32),
    Int64(i64),
    UInt8(u8),
    UInt16(u16),
    UInt32(u32),
    UInt64(u64),
    Float32(f32),
    Float64(f64),
    Bool(bool),
}

impl<'a> fmt::Debug for MappedValue<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            MappedValue::<'a>::Struct(_)    => write!(f, "MappedValue"),
            MappedValue::<'a>::List(_)      => write!(f, "MappedList"),
            MappedValue::<'a>::Str(v)       => write!(f, "{:?}", v), 
            MappedValue::<'a>::String(v)    => write!(f, "{:?}", v),
            MappedValue::<'a>::Int8(v)      => write!(f, "{:?}", v),
            MappedValue::<'a>::Int16(v)     => write!(f, "{:?}", v),
            MappedValue::<'a>::Int32(v)     => write!(f, "{:?}", v),
            MappedValue::<'a>::Int64(v)     => write!(f, "{:?}", v),
            MappedValue::<'a>::UInt8(v)     => write!(f, "{:?}", v),
            MappedValue::<'a>::UInt16(v)    => write!(f, "{:?}", v),
            MappedValue::<'a>::UInt32(v)    => write!(f, "{:?}", v),
            MappedValue::<'a>::UInt64(v)    => write!(f, "{:?}", v),
            MappedValue::<'a>::Float32(v)   => write!(f, "{:?}", v),
            MappedValue::<'a>::Float64(v)   => write!(f, "{:?}", v),
            MappedValue::<'a>::Bool(v)      => write!(f, "{:?}", v),
        }
    } 
}

macro_rules! basic_mapped_value_from {
    ( $x:ident, $y:ident ) => {
        impl<'a> From<&$x> for MappedValue<'a> {
            fn from(val: &$x) -> MappedValue<'a> {
                MappedValue::$y(*val)
            }
        }
    };
}

basic_mapped_value_from!(i8, Int8);
basic_mapped_value_from!(i16, Int16);
basic_mapped_value_from!(i32, Int32);
basic_mapped_value_from!(i64, Int64);
basic_mapped_value_from!(u8, UInt8);
basic_mapped_value_from!(u16, UInt16);
basic_mapped_value_from!(u32, UInt32);
basic_mapped_value_from!(u64, UInt64);
basic_mapped_value_from!(f32, Float32);
basic_mapped_value_from!(f64, Float64);
basic_mapped_value_from!(bool, Bool);

impl<'a> From<&'a String> for MappedValue<'a> {
    fn from(val: &'a String ) -> MappedValue<'a> {
        MappedValue::String(val)
    }
}

impl<'a, T: MappedStructure<'a>> From<&'a Vec<T>> for MappedValue<'a> {
    fn from(val: &'a Vec<T> ) -> MappedValue<'a> {
        MappedValue::List(val)
    }
}

impl<'a,T: MappedStructure<'a>> From<&'a T> for MappedValue<'a> {
    fn from(val: &'a T ) -> MappedValue<'a> {
        MappedValue::Struct(val)
    }
}

pub struct StatefulContext<'a> {
    root: &'a dyn MappedStructure<'a>,
    local: &'a dyn MappedStructure<'a>,
}

impl<'a> StatefulContext<'a> {
    pub fn root( root: &'a dyn MappedStructure<'a> ) -> Self {
        Self {
            root, local: root,
        }
    }

    pub fn local(&self, local: &'a dyn MappedStructure<'a> ) -> Self {
        Self {
            root: self.root,
            local
        }
    }

    pub fn full( root: &'a dyn MappedStructure<'a>, local: &'a dyn MappedStructure<'a> ) -> Self {
        Self {
            root, local
        }
    }

    pub fn get_value(&self, id: &[IdentifierValue]) -> Option<MappedValue<'a>> {
        let mut value : Option<MappedValue> =
            if id[0] == IdentifierValue::Root {
                Some(MappedValue::Struct(self.root))
            } else {
                Some(MappedValue::Struct(self.local))
            };
        for idpart in id {
            match idpart {
                IdentifierValue::Root => {
                    continue;
                }
                IdentifierValue::Name(name) => {
                    if let Some(MappedValue::Struct(s)) = value &&
                        let Some(nv) = s.get_value( &name ) {
                        value.replace( nv );
                    } else {
                        return None;
                    }
                }
                IdentifierValue::Index(_) => {
                    return None;
                }
            }
        }
        value
    }
}

impl<'a> From<&'a Value> for MappedValue<'a> {
    fn from(value: &'a Value) -> Self {
        match value {
            Value::Dictionary(ctx) => MappedValue::Struct(ctx),
            Value::List(list) => MappedValue::List(list),
            Value::String(s) => MappedValue::String(s),
            Value::Int(i) => MappedValue::Int64(*i),
            Value::Float(f) => MappedValue::Float64(*f),
            Value::Bool(b) => MappedValue::Bool(*b),
            Value::Identifier(_identifier) => panic!("Identifier!?"),
        }
    }
}

#[derive(Debug,Clone)]
pub enum Value {
    Dictionary(Context),
    List(Vec<Value>),
    String(String),
    Int(i64),
    Float(f64),
    Bool(bool),
    Identifier(Identifier),
}

impl From<&str> for Value {
    fn from(val: &str ) -> Value {
        Value::String(val.into())
    }
}

impl From<String> for Value {
    fn from(val: String) -> Value {
        Value::String(val)
    }
}

impl From<i64> for Value {
    fn from(val: i64) -> Value {
        Value::Int(val)
    }
}

impl From<f64> for Value {
    fn from(val: f64) -> Value {
        Value::Float(val)
    }
}

impl From<bool> for Value {
    fn from(val: bool) -> Value {
        Value::Bool(val)
    }
}

impl From<Vec<Value>> for Value {
    fn from(val: Vec<Value>) -> Value {
        Value::List(val)
    }
}

impl From<&[Value]> for Value {
    fn from(val: &[Value]) -> Value {
        Value::List(Vec::from(val))
    }
}

impl From<Context> for Value {
    fn from(val: Context) -> Value {
        Value::Dictionary(val)
    }
}

impl<const N: usize> From<[(String,Value); N]> for Value {
    fn from(val: [(String,Value); N]) -> Value {
        Value::Dictionary(HashMap::from(val))
    }
}

/*
impl MappedStructure for Value {
    fn get_value(&self, id: &Identifier) -> Option<Value> {
        if id.len() == 0 {
            return Some(self);
        }
        match Value {
            Value::Dictionary(dict) => {
            }
        }
        let mut cur_val : Option<&Value> = Some(&self);

        for idx in 0..id.len() {
            if let IdentifierValue::Name(s) = &id[idx] &&
                let Some(Value::Dictionary(d)) = &cur_val {
                cur_val.replace( if let Some(v) = d.get(s) {
                    v
                } else {
                    return None;
                });
            }
        }

        return cur_val.cloned();
    }
}
*/
