use std::rc::Rc;

use crate::{
    error::{MachineError, ValueConversionError},
    vm::{
        Value,
        env::Environment,
        machine::Machine,
        value::{
            BooleanValue, BytecodeFunction, ClosureValue, ConsCell, IdentifierValue,
            NativeFunction, NativeValue, NumberValue, StringValue, Vector,
        },
    },
};

pub trait TryFromValue<'a>: Sized {
    fn try_from_value(value: &'a Value) -> Result<Self, ValueConversionError>;
}

#[derive(Debug, Clone)]
pub enum AnyFunction {
    Native(NativeFunction),
    Function(Rc<BytecodeFunction>),
    Closure(ClosureValue),
}

macro_rules! impl_integer {
    ($($ty:ty : $bits:literal),+ $(,)?) => {
        $(
            impl TryFromValue<'_> for $ty {
                fn try_from_value(value: &Value) -> Result<Self, ValueConversionError> {
                    match value {
                        #[allow(irrefutable_let_patterns)]
                        Value::Number(value) if let Ok(value) = <$ty>::try_from(*value) => Ok(value),
                        _ => Err(ValueConversionError {
                            expected: format!("{}-bit integer", $bits),
                            got: value.clone()
                        })
                    }
                }
            }

            impl From<$ty> for Value {
                fn from(value: $ty) -> Value {
                    Value::Number(value.into())
                }
            }
        )+
    };
}

impl TryFromValue<'_> for bool {
    fn try_from_value(value: &'_ Value) -> Result<Self, ValueConversionError> {
        match value {
            Value::Boolean(BooleanValue(value)) => Ok(*value),
            _ => Err(ValueConversionError {
                expected: "#T or #F".into(),
                got: value.clone(),
            }),
        }
    }
}

impl From<bool> for Value {
    fn from(value: bool) -> Self {
        Value::Boolean(value.into())
    }
}

impl TryFromValue<'_> for NumberValue {
    fn try_from_value(value: &'_ Value) -> Result<Self, ValueConversionError> {
        match value {
            Value::Number(value) => Ok(*value),
            _ => Err(ValueConversionError {
                expected: "integer value".into(),
                got: value.clone(),
            }),
        }
    }
}

impl From<NumberValue> for Value {
    fn from(value: NumberValue) -> Self {
        Self::Number(value)
    }
}

impl<'a> TryFromValue<'a> for &'a ConsCell {
    fn try_from_value(value: &'a Value) -> Result<Self, ValueConversionError> {
        match value {
            Value::Cons(cons) => Ok(cons.as_ref()),
            _ => Err(ValueConversionError {
                expected: "cons cell".into(),
                got: value.clone(),
            }),
        }
    }
}

impl From<Rc<Vector>> for Value {
    fn from(value: Rc<Vector>) -> Self {
        Self::Vector(value)
    }
}

impl TryFromValue<'_> for StringValue {
    fn try_from_value(value: &'_ Value) -> Result<Self, ValueConversionError> {
        match value {
            Value::String(value) => Ok(value.clone()),
            _ => Err(ValueConversionError {
                expected: "string".into(),
                got: value.clone(),
            }),
        }
    }
}

impl From<NativeValue> for Value {
    fn from(value: NativeValue) -> Self {
        Self::NativeValue(value)
    }
}

impl From<Vec<u8>> for Value {
    fn from(value: Vec<u8>) -> Self {
        Value::Vector(Rc::new(Vector::from_iter(value)))
    }
}

impl From<IdentifierValue> for Value {
    fn from(value: IdentifierValue) -> Self {
        Value::Identifier(value)
    }
}

impl From<StringValue> for Value {
    fn from(value: StringValue) -> Self {
        Value::String(value)
    }
}

impl TryFromValue<'_> for IdentifierValue {
    fn try_from_value(value: &'_ Value) -> Result<Self, ValueConversionError> {
        match value {
            Value::Identifier(value) => Ok(value.clone()),
            _ => Err(ValueConversionError {
                expected: "identifier".into(),
                got: value.clone(),
            }),
        }
    }
}

impl TryFromValue<'_> for AnyFunction {
    fn try_from_value(value: &'_ Value) -> Result<Self, ValueConversionError> {
        match value {
            Value::NativeFunction(function) => Ok(Self::Native(function.clone())),
            Value::Closure(closure) => Ok(Self::Closure(closure.clone())),
            Value::Function(function) => Ok(Self::Function(function.clone())),
            _ => Err(ValueConversionError {
                expected: "function".into(),
                got: value.clone(),
            }),
        }
    }
}

impl AnyFunction {
    pub fn name(&self) -> IdentifierValue {
        match self {
            Self::Native(native) => native.name().clone(),
            Self::Closure(closure) => closure.function.name().into(),
            Self::Function(function) => function.name().into(),
        }
    }

    pub fn invoke(
        &self,
        vm: &mut Machine,
        env: &Rc<Environment>,
        args: &[Value],
    ) -> Result<Value, MachineError> {
        match self {
            Self::Native(function) => function.invoke(vm, env, args),
            Self::Closure(closure) => vm
                .evaluate_closure_args(env, closure.clone(), args)
                .map_err(|error| error.error),
            Self::Function(function) => {
                let closure = ClosureValue {
                    function: function.clone(),
                    upvalues: vec![],
                };
                vm.evaluate_closure_args(env, closure, args)
                    .map_err(|error| error.error)
            }
        }
    }
}

impl_integer!(
    i8: 8,
    i16: 16,
    i32: 32,
    i64: 64,
    usize: 64,
);
