use std::borrow::Cow;
use bitflags::bitflags;
use bumpalo::boxed::Box;
use bumpalo::collections::String as BumpString;
use bumpalo::collections::Vec as BumpVec;
use bumpalo::Bump;
use hashbrown::DefaultHashBuilder;
use hashbrown::HashMap;
use super::frame::Frame;
use super::functions::FunctionContext;
use crate::parser::ast::{Ast, AstKind};
use crate::{Error, Result};
pub mod impls;
pub mod iterator;
mod range;
pub mod serialize;
use self::range::Range;
use self::serialize::{DumpFormatter, PrettyFormatter, Serializer};
pub use iterator::MemberIterator;
bitflags! {
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
    pub struct ArrayFlags: u8 {
        const SEQUENCE     = 0b00000001;
        const SINGLETON    = 0b00000010;
        const CONS         = 0b00000100;
        const WRAPPED      = 0b00001000;
        const TUPLE_STREAM = 0b00010000;
    }
}
pub const UNDEFINED: Value = Value::Undefined;
pub const TRUE: Value = Value::Bool(true);
pub const FALSE: Value = Value::Bool(false);
pub enum Value<'a> {
    Undefined,
    Null,
    Number(f64),
    Bool(bool),
    String(BumpString<'a>),
    Array(BumpVec<'a, &'a Value<'a>>, ArrayFlags),
    Object(HashMap<BumpString<'a>, &'a Value<'a>, DefaultHashBuilder, &'a Bump>),
    Range(Range<'a>),
    Lambda {
        ast: Box<'a, Ast>,
        input: &'a Value<'a>,
        frame: Frame<'a>,
    },
    NativeFn {
        name: String,
        arity: usize,
        func: fn(FunctionContext<'a, '_>, &[&'a Value<'a>]) -> Result<&'a Value<'a>>,
    },
    Transformer {
        pattern: std::boxed::Box<Ast>,
        update: std::boxed::Box<Ast>,
        delete: Option<std::boxed::Box<Ast>>,
    },
}
#[allow(clippy::mut_from_ref)]
impl<'a> Value<'a> {
    pub fn undefined() -> &'a Value<'a> {
        unsafe { std::mem::transmute::<&Value<'static>, &'a Value<'a>>(&UNDEFINED) }
    }
    pub fn null(arena: &Bump) -> &mut Value {
        arena.alloc(Value::Null)
    }
    pub fn bool(value: bool) -> &'a Value<'a> {
        if value {
            unsafe { std::mem::transmute::<&Value<'static>, &'a Value<'a>>(&TRUE) }
        } else {
            unsafe { std::mem::transmute::<&Value<'static>, &'a Value<'a>>(&FALSE) }
        }
    }
    pub fn number(arena: &Bump, value: impl Into<f64>) -> &mut Value {
        arena.alloc(Value::Number(value.into()))
    }
    pub fn string(arena: &'a Bump, value: &str) -> &'a mut Value<'a> {
        arena.alloc(Value::String(BumpString::from_str_in(value, arena)))
    }
    pub fn array(arena: &Bump, flags: ArrayFlags) -> &mut Value {
        let v = BumpVec::new_in(arena);
        arena.alloc(Value::Array(v, flags))
    }
    pub fn array_from(
        arena: &'a Bump,
        arr: BumpVec<'a, &'a Value<'a>>,
        flags: ArrayFlags,
    ) -> &'a mut Value<'a> {
        arena.alloc(Value::Array(arr, flags))
    }
    pub fn array_with_capacity(arena: &Bump, capacity: usize, flags: ArrayFlags) -> &mut Value {
        arena.alloc(Value::Array(
            BumpVec::with_capacity_in(capacity, arena),
            flags,
        ))
    }
    pub fn object(arena: &Bump) -> &mut Value {
        arena.alloc(Value::Object(HashMap::new_in(arena)))
    }
    pub fn object_from<H>(
        hash: &HashMap<BumpString<'a>, &'a Value<'a>, H, &'a Bump>,
        arena: &'a Bump,
    ) -> &'a mut Value<'a> {
        let result = Value::object_with_capacity(arena, hash.len());
        if let Value::Object(o) = result {
            o.extend(hash.iter().map(|(k, v)| (k.clone(), *v)));
        }
        result
    }
    pub fn object_with_capacity(arena: &Bump, capacity: usize) -> &mut Value {
        arena.alloc(Value::Object(HashMap::with_capacity_in(capacity, arena)))
    }
    pub fn lambda(
        arena: &'a Bump,
        node: &Ast,
        input: &'a Value<'a>,
        frame: Frame<'a>,
    ) -> &'a mut Value<'a> {
        arena.alloc(Value::Lambda {
            ast: Box::new_in(node.clone(), arena),
            input,
            frame,
        })
    }
    pub fn nativefn(
        arena: &'a Bump,
        name: &str,
        arity: usize,
        func: fn(FunctionContext<'a, '_>, &[&'a Value<'a>]) -> Result<&'a Value<'a>>,
    ) -> &'a mut Value<'a> {
        arena.alloc(Value::NativeFn {
            name: name.to_string(),
            arity,
            func,
        })
    }
    pub fn transformer(
        arena: &'a Bump,
        pattern: &std::boxed::Box<Ast>,
        update: &std::boxed::Box<Ast>,
        delete: &Option<std::boxed::Box<Ast>>,
    ) -> &'a mut Value<'a> {
        arena.alloc(Value::Transformer {
            pattern: pattern.clone(),
            update: update.clone(),
            delete: delete.clone(),
        })
    }
    pub fn range(arena: &'a Bump, start: isize, end: isize) -> &'a mut Value<'a> {
        arena.alloc(Value::Range(Range::new(arena, start, end)))
    }
    pub fn range_from(arena: &'a Bump, range: &'a Range) -> &'a mut Value<'a> {
        arena.alloc(Value::Range(range.clone()))
    }
    pub fn is_undefined(&self) -> bool {
        matches!(*self, Value::Undefined)
    }
    pub fn is_null(&self) -> bool {
        matches!(*self, Value::Null)
    }
    pub fn is_bool(&self) -> bool {
        matches!(&self, Value::Bool(..))
    }
    pub fn is_number(&self) -> bool {
        matches!(&self, Value::Number(..))
    }
    pub fn is_integer(&self) -> bool {
        match self {
            Value::Number(n) => match n.classify() {
                std::num::FpCategory::Nan
                | std::num::FpCategory::Infinite
                | std::num::FpCategory::Subnormal => false,
                _ => {
                    let mantissa = n.trunc();
                    n - mantissa == 0.0
                }
            },
            _ => false,
        }
    }
    pub fn is_array_of_valid_numbers(&self) -> Result<bool> {
        match self {
            Value::Array(ref a, _) => {
                for member in a.iter() {
                    if !member.is_valid_number()? {
                        return Ok(false);
                    }
                }
                Ok(true)
            }
            _ => Ok(false),
        }
    }
    pub fn is_array_of_strings(&self) -> bool {
        match self {
            Value::Array(ref a, _) => {
                for member in a.iter() {
                    if !member.is_string() {
                        return false;
                    }
                }
                true
            }
            _ => false,
        }
    }
    pub fn is_valid_number(&self) -> Result<bool> {
        match self {
            Value::Number(n) => {
                if n.is_nan() {
                    Ok(false)
                } else if n.is_infinite() {
                    Err(Error::D1001NumberOfOutRange(*n))
                } else {
                    Ok(true)
                }
            }
            _ => Ok(false),
        }
    }
    pub fn is_nan(&self) -> bool {
        matches!(*self, Value::Number(n) if n.is_nan())
    }
    pub fn is_finite(&self) -> bool {
        match self {
            Value::Number(n) => n.is_finite(),
            _ => false,
        }
    }
    pub fn is_string(&self) -> bool {
        matches!(*self, Value::String(..))
    }
    pub fn is_array(&self) -> bool {
        matches!(*self, Value::Array(..) | Value::Range(..))
    }
    pub fn is_object(&self) -> bool {
        matches!(*self, Value::Object(..))
    }
    pub fn is_function(&self) -> bool {
        matches!(
            *self,
            Value::Lambda { .. } | Value::NativeFn { .. } | Value::Transformer { .. }
        )
    }
    pub fn is_truthy(&'a self) -> bool {
        match *self {
            Value::Undefined => false,
            Value::Null => false,
            Value::Number(n) => n != 0.0,
            Value::Bool(ref b) => *b,
            Value::String(ref s) => !s.is_empty(),
            Value::Array(ref a, _) => match a.len() {
                0 => false,
                1 => self.get_member(0).is_truthy(),
                _ => {
                    for item in self.members() {
                        if item.is_truthy() {
                            return true;
                        }
                    }
                    false
                }
            },
            Value::Object(ref o) => !o.is_empty(),
            Value::Lambda { .. } | Value::NativeFn { .. } | Value::Transformer { .. } => false,
            Value::Range(ref r) => !r.is_empty(),
        }
    }
    pub fn get_member(&self, index: usize) -> &'a Value<'a> {
        match *self {
            Value::Array(ref array, _) => {
                array.get(index).copied().unwrap_or_else(Value::undefined)
            }
            Value::Range(ref range) => range.nth(index).unwrap_or_else(Value::undefined),
            _ => panic!("Not an array"),
        }
    }
    pub fn members(&'a self) -> MemberIterator<'a> {
        match self {
            Value::Array(..) | Value::Range(..) => MemberIterator::new(self),
            _ => panic!("Not an array"),
        }
    }
    pub fn entries(&self) -> hashbrown::hash_map::Iter<'_, BumpString<'a>, &'a Value> {
        match self {
            Value::Object(map) => map.iter(),
            _ => panic!("Not an object"),
        }
    }
    pub fn arity(&self) -> usize {
        match *self {
            Value::Lambda { ref ast, .. } => {
                if let AstKind::Lambda { ref args, .. } = ast.kind {
                    args.len()
                } else {
                    panic!("Not a lambda function")
                }
            }
            Value::NativeFn { arity, .. } => arity,
            Value::Transformer { .. } => 1,
            _ => panic!("Not a function"),
        }
    }
    pub fn as_bool(&self) -> bool {
        match *self {
            Value::Bool(ref b) => *b,
            _ => panic!("Not a bool"),
        }
    }
    pub fn as_f64(&self) -> f64 {
        match *self {
            Value::Number(n) => n,
            _ => panic!("Not a number"),
        }
    }
    pub fn as_usize(&self) -> usize {
        match *self {
            Value::Number(n) => n as usize,
            _ => panic!("Not a number"),
        }
    }
    pub fn as_isize(&self) -> isize {
        match *self {
            Value::Number(n) => n as isize,
            _ => panic!("Not a number"),
        }
    }
    pub fn as_str(&self) -> Cow<'_, str> {
        match *self {
            Value::String(ref s) => Cow::from(s.as_str()),
            _ => panic!("Not a string"),
        }
    }
    pub fn len(&self) -> usize {
        match *self {
            Value::Array(ref array, _) => array.len(),
            Value::Range(ref range) => range.len(),
            _ => panic!("Not an array"),
        }
    }
    pub fn is_empty(&self) -> bool {
        match *self {
            Value::Array(ref array, _) => array.is_empty(),
            Value::Range(ref range) => range.is_empty(),
            _ => panic!("Not an array"),
        }
    }
    pub fn get_entry(&self, key: &str) -> &'a Value<'a> {
        match *self {
            Value::Object(ref map) => match map.get(key) {
                Some(value) => value,
                None => Value::undefined(),
            },
            _ => panic!("Not an object"),
        }
    }
    pub fn remove_entry(&mut self, key: &str) {
        match *self {
            Value::Object(ref mut map) => map.remove(key),
            _ => panic!("Not an object"),
        };
    }
    pub fn push(&mut self, value: &'a Value<'a>) {
        match *self {
            Value::Array(ref mut array, _) => array.push(value),
            _ => panic!("Not an array"),
        }
    }
    pub fn insert(&mut self, key: &str, value: &'a Value<'a>) {
        match *self {
            Value::Object(ref mut map) => {
                map.insert(BumpString::from_str_in(key, map.allocator()), value);
            }
            _ => panic!("Not an object"),
        }
    }
    pub fn remove(&mut self, key: &str) {
        match *self {
            Value::Object(ref mut map) => map.remove(key),
            _ => panic!("Not an object"),
        };
    }
    pub fn flatten(&'a self, arena: &'a Bump) -> &'a mut Value<'a> {
        let flattened = Self::array(arena, ArrayFlags::empty());
        self._flatten(flattened)
    }
    fn _flatten(&'a self, flattened: &'a mut Value<'a>) -> &'a mut Value<'a> {
        let mut flattened = flattened;
        if self.is_array() {
            for member in self.members() {
                flattened = member._flatten(flattened);
            }
        } else {
            flattened.push(self)
        }
        flattened
    }
    pub fn wrap_in_array(
        arena: &'a Bump,
        value: &'a Value<'a>,
        flags: ArrayFlags,
    ) -> &'a mut Value<'a> {
        arena.alloc(Value::Array(bumpalo::vec![in arena; value], flags))
    }
    pub fn wrap_in_array_if_needed(
        arena: &'a Bump,
        value: &'a Value<'a>,
        flags: ArrayFlags,
    ) -> &'a Value<'a> {
        if value.is_array() {
            value
        } else {
            Value::wrap_in_array(arena, value, flags)
        }
    }
    pub fn get_flags(&self) -> ArrayFlags {
        match self {
            Value::Array(_, flags) => *flags,
            _ => panic!("Not an array"),
        }
    }
    pub fn has_flags(&self, check_flags: ArrayFlags) -> bool {
        match self {
            Value::Array(_, flags) => flags.contains(check_flags),
            _ => false,
        }
    }
    pub fn clone(&'a self, arena: &'a Bump) -> &'a mut Value<'a> {
        match self {
            Self::Undefined => arena.alloc(Value::Undefined),
            Self::Null => Value::null(arena),
            Self::Number(n) => Value::number(arena, *n),
            Self::Bool(b) => arena.alloc(Value::Bool(*b)),
            Self::String(s) => arena.alloc(Value::String(s.clone())),
            Self::Array(a, f) => Value::array_from(arena, a.clone(), *f),
            Self::Object(o) => Value::object_from(o, arena),
            Self::Lambda { ast, input, frame } => Value::lambda(arena, ast, input, frame.clone()),
            Self::NativeFn { name, arity, func } => Value::nativefn(arena, name, *arity, *func),
            Self::Transformer {
                pattern,
                update,
                delete,
            } => Value::transformer(arena, pattern, update, delete),
            Self::Range(range) => Value::range_from(arena, range),
        }
    }
    pub fn clone_array_with_flags(&self, arena: &'a Bump, flags: ArrayFlags) -> &'a mut Value<'a> {
        match *self {
            Value::Array(ref array, _) => arena.alloc(Value::Array(array.clone(), flags)),
            _ => panic!("Not an array"),
        }
    }
    pub fn serialize(&'a self, pretty: bool) -> String {
        if pretty {
            let serializer = Serializer::new(PrettyFormatter::default(), false);
            serializer.serialize(self).expect("Shouldn't fail")
        } else {
            let serializer = Serializer::new(DumpFormatter, false);
            serializer.serialize(self).expect("Shouldn't fail")
        }
    }
    #[allow(invalid_reference_casting)]
    pub fn __very_unsafe_make_mut(&'a self) -> &'a mut Value<'a> {
        unsafe {
            let const_ptr = self as *const Value<'a>;
            let mut_ptr = const_ptr as *mut Value<'a>;
            &mut *mut_ptr
        }
    }
}