use crate::*;

/// Describes type of Value.
#[derive(
    Clone,
    Debug,
    Default,
    Hash,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    serde::Serialize,
    serde::Deserialize,
)]
pub enum DataType {
    #[default]
    ///  Todo
    Empty,

    /// e.g. `( string, string, int )`
    Tuple(LVec<DataType>),

    /// e.g. `struct{ name: string, email: string, created: date }`
    Struct(LVec<(LString, DataType)>),

    /// e.g. `enum{ leaf: int, node: [int] }`
    Enum(LVec<(LString, DataType)>),

    /// e.g. `string`
    String,

    /// e.g. `binary`
    Binary,

    /// e.g. `int`
    Int,

    // Array of values.
    // Array(usize, LBox<DataType>),
    /// List of values.
    List(LBox<DataType>),

    // e.g. `[string->int]`
    // Map(LBox<DataType>, LBox<DataType>),
    /// List of 64-bit integers.
    IList,

    DataType,

    TabInfo,
}

impl DataType {
    /// Encode value (which must match DataType) as bytes. DataType will later be used to decode the bytes.
    pub fn value_to_bytes(&self, val: &Value) -> LVec<u8> {
        let mut w = LVec::new();
        self.value_to_writer(val, &mut w);
        w
    }

    /// Encode value (which must match DataType) as bytes. DataType will later be used to decode the bytes.
    pub fn value_to_writer<W: std::io::Write>(&self, val: &Value, w: &mut W) {
        match self {
            DataType::Tuple(types) => {
                let list = val.list();
                for (i, t) in types.into_iter().enumerate() {
                    t.value_to_writer(&list[i], w);
                }
            }
            DataType::Struct(fields) => {
                let list = val.list();
                for (i, f) in fields.into_iter().enumerate() {
                    f.1.value_to_writer(&list[i], w);
                }
            }
            DataType::Enum(variants) => {
                let (tag, val) = val.en();
                self.write_usize(*tag, w);
                variants[*tag].1.value_to_writer(val, w);
            }
            DataType::String => {
                let s = val.string();
                self.write_usize(s.len(), w);
                let _ = w.write(s.as_bytes());
            }
            DataType::Binary => {
                let b = val.binary();
                self.write_usize(b.len(), w);
                let _ = w.write(b);
            }
            DataType::Int => {
                let i = val.int();
                let _ = w.write(&i.to_le_bytes());
            }
            DataType::Empty => {}
            DataType::List(t) => {
                let list = val.list();
                self.write_usize(list.len(), w);
                for v in list {
                    t.value_to_writer(v, w);
                }
            }
            DataType::IList => {
                let list = val.ilist();
                self.write_usize(list.len(), w);
                for i in list {
                    self.write_int(*i, w);
                }
            }
            DataType::DataType => {
                let val = val.datatype().to_bytes();
                self.write_usize(val.len(), w);
                let _ = w.write(&val);
            }
            DataType::TabInfo => {
                let val = val.tabinfo().to_bytes();
                self.write_usize(val.len(), w);
                let _ = w.write(&val);
            }
        }
    }

    /// Decode bytes of this DataType. ix is advanced according to the bytes read from buf. Returns decoded Value.
    pub fn bytes_to_value(&self, buf: &[u8]) -> Value {
        let mut ix = 0;
        self.to_value(buf, &mut ix)
    }

    /// Decode bytes of this DataType. ix is advanced according to the bytes read from buf. Returns decoded Value.
    fn to_value(&self, buf: &[u8], ix: &mut usize) -> Value {
        match self {
            DataType::Tuple(types) => {
                let mut list = LVec::with_capacity(types.len());
                for t in types {
                    let v = t.to_value(buf, ix);
                    list.push(v);
                }
                Value::List(list)
            }
            DataType::Struct(fields) => {
                let mut list = LVec::with_capacity(fields.len());

                for f in fields {
                    let v = f.1.to_value(buf, ix);
                    list.push(v);
                }
                Value::List(list)
            }
            DataType::Enum(variants) => {
                let tag = self.read_usize(buf, ix);
                let val = variants[tag].1.to_value(buf, ix);
                Value::Enum(tag, LBox::new(val))
            }
            DataType::String => {
                let len = self.read_usize(buf, ix);
                let s = &buf[*ix..*ix + len];
                *ix += len;
                let s = str::from_utf8(s).unwrap();
                let s = LString::from(s);
                Value::String(s)
            }
            DataType::Binary => {
                let len = self.read_usize(buf, ix);
                let b = &buf[*ix..*ix + len];
                *ix += len;
                let b = LVec::<u8>::from(b);
                Value::Binary(b)
            }
            DataType::Int => {
                let i = self.read_int(buf, ix);
                Value::Int(i)
            }
            DataType::Empty => Value::Empty,
            DataType::List(t) => {
                let len = self.read_usize(buf, ix);
                let mut list = LVec::with_capacity(len);
                for _i in 0..len {
                    let v = t.to_value(buf, ix);
                    list.push(v);
                }
                Value::List(list)
            }
            DataType::IList => {
                let len = self.read_usize(buf, ix);
                let mut list = LVec::with_capacity(len);
                for _i in 0..len {
                    let i = self.read_int(buf, ix);
                    list.push(i);
                }
                Value::IList(list)
            }
            DataType::DataType => {
                let len = self.read_usize(buf, ix);
                let b = &buf[*ix..*ix + len];
                *ix += len;
                Value::DataType(DataType::from_bytes(b))
            }
            DataType::TabInfo => {
                let len = self.read_usize(buf, ix);
                let b = &buf[*ix..*ix + len];
                *ix += len;
                Value::TabInfo(TabInfo::from_bytes(b))
            }
        }
    }

    fn write_usize<W: std::io::Write>(&self, mut val: usize, w: &mut W) {
        /*
            // Use a variable length encoding to be efficient for small sizes.
            let val = val as u64;
            let _ = w.write(&val.to_le_bytes());
        */
        let mut buf = [0u8; 10]; // 7 * 10 = 70 bits, which is more than 64 bits.
        let mut ix = 0;
        loop {
            let b = (val % 128) as u8;
            val /= 128;
            if val == 0 {
                buf[ix] = b;
                ix += 1;
                break;
            } else {
                buf[ix] = b + 128;
                ix += 1;
            }
        }
        let _ = w.write(&buf[0..ix]);
    }

    fn read_usize(&self, buf: &[u8], ix: &mut usize) -> usize {
        // Use a variable length encoding to be efficient for small sizes.
        // let x = u64::from_le_bytes(buf[*ix..*ix + 8].try_into().unwrap());
        // *ix += 8;
        // x as usize

        // Last byte has 0 in top bit.
        let mut x: usize = 0;
        let mut i = 0;
        loop {
            let b = buf[*ix];
            *ix += 1;
            let f = b & 128;
            x += ((b & 127) as usize) << i;
            if f == 0 {
                break;
            }
            i += 7;
        }
        x
    }

    fn write_int<W: std::io::Write>(&self, val: i64, w: &mut W) {
        // Could use a variable length encoding to be efficient for small ints.
        let _ = w.write(&val.to_le_bytes());
    }

    fn read_int(&self, buf: &[u8], ix: &mut usize) -> i64 {
        // Could use a variable length encoding to be efficient for small ints.
        let x = i64::from_le_bytes(buf[*ix..*ix + 8].try_into().unwrap());
        *ix += 8;
        x
    }

    /// Convert ( serialize ) this datatype as bytes.
    pub fn to_bytes(&self) -> Vec<u8> {
        postcard::to_stdvec(self).unwrap()
    }

    /// Deserialise bytes to datatype.
    pub fn from_bytes(b: &[u8]) -> Self {
        postcard::from_bytes(b).unwrap()
    }
}
