use std::{
    cell::{Ref, RefCell},
    fmt, mem,
};

use crate::vm::value::{NumberValue, Value};

#[derive(Debug, PartialEq)]
pub enum VectorStorage {
    I8(Vec<i8>),
    I16(Vec<i16>),
    I32(Vec<i32>),
    I64(Vec<i64>),
    Any(Vec<Value>),
}

#[derive(Debug, PartialEq)]
pub struct Vector(RefCell<VectorStorage>);

impl Vector {
    pub fn is_empty(&self) -> bool {
        self.0.borrow().is_empty()
    }

    pub fn len(&self) -> usize {
        self.0.borrow().len()
    }

    pub fn borrow(&self) -> Ref<'_, VectorStorage> {
        self.0.borrow()
    }

    pub fn value_at(&self, index: usize) -> Option<Value> {
        self.0.borrow().value_at(index)
    }
    pub fn set_value_at(&self, index: usize, value: Value) {
        self.0.borrow_mut().set_value_at(index, value);
    }

    pub fn as_bytes(&self) -> Option<Ref<'_, [u8]>> {
        Ref::filter_map(self.borrow(), |r| match r {
            VectorStorage::I8(values) => {
                Some(unsafe { mem::transmute::<&[i8], &[u8]>(&values[..]) })
            }
            _ => None,
        })
        .ok()
    }
}

impl fmt::Display for Vector {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&*self.0.borrow(), f)
    }
}

impl<A> FromIterator<A> for Vector
where
    VectorStorage: FromIterator<A>,
{
    fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
        Self(RefCell::new(VectorStorage::from_iter(iter)))
    }
}

impl From<Vec<u8>> for Vector {
    fn from(value: Vec<u8>) -> Self {
        let (ptr, len, cap) = value.into_raw_parts();
        Self::from(unsafe { Vec::from_raw_parts(ptr.cast::<i8>(), len, cap) })
    }
}
impl From<Vec<i8>> for Vector {
    fn from(value: Vec<i8>) -> Self {
        Vector(RefCell::new(VectorStorage::I8(value)))
    }
}

macro_rules! static_dispatch {
    ([$input:expr] $value:ident => $body:expr) => {
        match $input {
            Self::I8($value) => $body,
            Self::I16($value) => $body,
            Self::I32($value) => $body,
            Self::I64($value) => $body,
            Self::Any($value) => $body,
        }
    };
}

macro_rules! dispatch_integer_convert {
    ($self:ident ($storage:ident, $value:ident) => $f:expr) => {
        match $self {
            Self::I8($storage) if let Ok($value) = i8::try_from($value) => {
                $f;
            }
            Self::I8($storage) if let Ok($value) = i16::try_from($value) => {
                let mut $storage = $storage.iter().copied().map(Into::into).collect::<Vec<_>>();
                $f;
                *$self = Self::I16($storage);
            }
            Self::I8($storage) if let Ok($value) = i32::try_from($value) => {
                let mut $storage = $storage.iter().copied().map(Into::into).collect::<Vec<_>>();
                $f;
                *$self = Self::I32($storage);
            }
            Self::I8($storage) => {
                let mut $storage = $storage.iter().copied().map(Into::into).collect::<Vec<_>>();
                $f;
                *$self = Self::I64($storage);
            }
            Self::I16($storage) if let Ok($value) = i16::try_from($value) => {
                $f;
            }
            Self::I16($storage) if let Ok($value) = i32::try_from($value) => {
                let mut $storage = $storage.iter().copied().map(Into::into).collect::<Vec<_>>();
                $f;
                *$self = Self::I32($storage);
            }
            Self::I16($storage) => {
                let mut $storage = $storage.iter().copied().map(Into::into).collect::<Vec<_>>();
                $f;
                *$self = Self::I64($storage);
            }
            Self::I32($storage) if let Ok($value) = i32::try_from($value) => {
                $f;
            }
            Self::I32($storage) => {
                let mut $storage = $storage.iter().copied().map(Into::into).collect::<Vec<_>>();
                $f;
                *$self = Self::I64($storage);
            }
            Self::I64($storage) => {
                $f;
            }
            Self::Any($storage) => {
                let $value = Value::Number(NumberValue::Int($value));
                $f;
            }
        }
    };
}

macro_rules! dispatch_any_convert {
    ($self:ident ($storage:ident) => $f:expr) => {
        match $self {
            Self::I8($storage) => {
                let mut $storage = $storage
                    .iter()
                    .copied()
                    .map(Into::into)
                    .map(Value::Number)
                    .collect::<Vec<_>>();
                $f;
                *$self = Self::Any($storage);
            }
            Self::I16($storage) => {
                let mut $storage = $storage
                    .iter()
                    .copied()
                    .map(Into::into)
                    .map(Value::Number)
                    .collect::<Vec<_>>();
                $f;
                *$self = Self::Any($storage);
            }
            Self::I32($storage) => {
                let mut $storage = $storage
                    .iter()
                    .copied()
                    .map(Into::into)
                    .map(Value::Number)
                    .collect::<Vec<_>>();
                $f;
                *$self = Self::Any($storage);
            }
            Self::I64($storage) => {
                let mut $storage = $storage
                    .iter()
                    .copied()
                    .map(Into::into)
                    .map(Value::Number)
                    .collect::<Vec<_>>();
                $f;
                *$self = Self::Any($storage);
            }
            Self::Any($storage) => {
                $f;
            }
        }
    };
}

impl VectorStorage {
    fn value_at(&self, index: usize) -> Option<Value> {
        match self {
            Self::I8(values) if let Some(value) = values.get(index).copied() => {
                Some(Value::Number(value.into()))
            }
            Self::I16(values) if let Some(value) = values.get(index).copied() => {
                Some(Value::Number(value.into()))
            }
            Self::I32(values) if let Some(value) = values.get(index).copied() => {
                Some(Value::Number(value.into()))
            }
            Self::I64(values) if let Some(value) = values.get(index).copied() => {
                Some(Value::Number(value.into()))
            }
            Self::Any(values) if let Some(value) = values.get(index).cloned() => Some(value),
            _ => None,
        }
    }

    fn len(&self) -> usize {
        static_dispatch!([self] vec => vec.len())
    }

    fn is_empty(&self) -> bool {
        self.len() == 0
    }

    fn push(&mut self, value: Value) {
        match value {
            Value::Number(NumberValue::Int(value)) => self.push_integer(value),
            value => match self {
                Self::Any(vec) => vec.push(value),
                _ => self.push_any(value),
            },
        }
    }

    fn set_value_at(&mut self, index: usize, value: Value) {
        if index >= self.len() {
            return;
        }
        match value {
            Value::Number(NumberValue::Int(value)) => self.set_integer(index, value),
            value => match self {
                Self::Any(vec) => vec[index] = value,
                _ => self.set_any(index, value),
            },
        }
    }

    fn push_any(&mut self, value: Value) {
        dispatch_any_convert!(self (w) => {
            w.push(value);
        });
    }

    fn set_any(&mut self, index: usize, value: Value) {
        dispatch_any_convert!(self (w) => {
            w[index] = value;
        });
    }

    fn push_integer(&mut self, value: i64) {
        dispatch_integer_convert!(self (w, value) => {
            w.push(value);
        });
    }

    fn set_integer(&mut self, index: usize, value: i64) {
        dispatch_integer_convert!(self (w, value) => {
            w[index] = value;
        });
    }
}

impl FromIterator<Value> for VectorStorage {
    fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
        let mut accumulator = Self::I8(vec![]);
        for item in iter {
            accumulator.push(item);
        }
        accumulator
    }
}

fn display_vector<T: fmt::Display>(value: &[T], f: &mut fmt::Formatter) -> fmt::Result {
    for (i, value) in value.iter().enumerate() {
        if i != 0 {
            write!(f, " ")?;
        }
        write!(f, "{value}")?;
    }
    Ok(())
}

impl fmt::Display for VectorStorage {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "[")?;
        match self {
            Self::I8(vec) => display_vector(vec, f),
            Self::I16(vec) => display_vector(vec, f),
            Self::I32(vec) => display_vector(vec, f),
            Self::I64(vec) => display_vector(vec, f),
            Self::Any(vec) => display_vector(vec, f),
        }?;
        write!(f, "]")
    }
}

macro_rules! impl_primitive_from_iter {
    ($signed_ty:ty, $unsigned_ty:ty => $variant:ident) => {
        impl FromIterator<$signed_ty> for VectorStorage {
            fn from_iter<T: IntoIterator<Item = $signed_ty>>(iter: T) -> Self {
                Self::$variant(iter.into_iter().collect())
            }
        }
        impl FromIterator<$unsigned_ty> for VectorStorage {
            fn from_iter<T: IntoIterator<Item = $unsigned_ty>>(iter: T) -> Self {
                Self::$variant(iter.into_iter().map(|x| x as $signed_ty).collect())
            }
        }
    };
}

impl_primitive_from_iter!(i8, u8 => I8);
impl_primitive_from_iter!(i16, u16 => I16);
impl_primitive_from_iter!(i32, u32 => I32);
impl_primitive_from_iter!(i64, u64 => I64);

#[cfg(test)]
mod tests {
    use crate::vm::value::{Value, vector::VectorStorage};

    #[test]
    fn test_vector_storage_from_iter() {
        let v = VectorStorage::from_iter([1i8, 2, 3, 4, 5, 6, 7]);
        assert_eq!(VectorStorage::I8(vec![1, 2, 3, 4, 5, 6, 7]), v);
        let v = VectorStorage::from_iter([1u8, 2, 3, 4, 5, 6, 7]);
        assert_eq!(VectorStorage::I8(vec![1, 2, 3, 4, 5, 6, 7]), v);
        let v = VectorStorage::from_iter([1i16, 2, 3, 4, 5, 6, 7]);
        assert_eq!(VectorStorage::I16(vec![1, 2, 3, 4, 5, 6, 7]), v);
        let v = VectorStorage::from_iter([1u16, 2, 3, 4, 5, 6, 7]);
        assert_eq!(VectorStorage::I16(vec![1, 2, 3, 4, 5, 6, 7]), v);
        let v = VectorStorage::from_iter([1i32, 2, 3, 4, 5, 6, 7]);
        assert_eq!(VectorStorage::I32(vec![1, 2, 3, 4, 5, 6, 7]), v);
        let v = VectorStorage::from_iter([1u32, 2, 3, 4, 5, 6, 7]);
        assert_eq!(VectorStorage::I32(vec![1, 2, 3, 4, 5, 6, 7]), v);
        let v = VectorStorage::from_iter([1i64, 2, 3, 4, 5, 6, 7]);
        assert_eq!(VectorStorage::I64(vec![1, 2, 3, 4, 5, 6, 7]), v);
        let v = VectorStorage::from_iter([1u64, 2, 3, 4, 5, 6, 7]);
        assert_eq!(VectorStorage::I64(vec![1, 2, 3, 4, 5, 6, 7]), v);
    }

    #[test]
    fn test_vector_unspecialize() {
        let mut v = VectorStorage::from_iter([1i8, 2, 3]);
        assert_eq!(VectorStorage::I8(vec![1, 2, 3]), v);
        v.push(Value::Number(1234.into()));
        assert_eq!(VectorStorage::I16(vec![1, 2, 3, 1234]), v);
        v.push(Value::Number(12341234.into()));
        assert_eq!(VectorStorage::I32(vec![1, 2, 3, 1234, 12341234]), v);
        v.push(Value::Number(1234123412341234i64.into()));
        assert_eq!(
            VectorStorage::I64(vec![1, 2, 3, 1234, 12341234, 1234123412341234]),
            v
        );
        v.push(Value::String("a".into()));
        assert_eq!(
            VectorStorage::Any(vec![
                Value::Number(1.into()),
                Value::Number(2.into()),
                Value::Number(3.into()),
                Value::Number(1234.into()),
                Value::Number(12341234.into()),
                Value::Number(1234123412341234i64.into()),
                Value::String("a".into())
            ]),
            v
        );
    }
}
