use crate::DbError;
use crate::DbErrorType;
use crate::utilities::serialize::Serialize;
use crate::utilities::serialize::SerializeStatic;

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct DbValueIndex {
    value: [u8; 16],
}

impl DbValueIndex {
    pub fn new() -> Self {
        Self { value: [0_u8; 16] }
    }

    pub fn data(&self) -> [u8; 16] {
        self.value
    }

    pub fn get_type(&self) -> u8 {
        self.value[15] >> 4
    }

    pub fn index(&self) -> u64 {
        let mut bytes = [0_u8; 8];
        bytes.copy_from_slice(&self.value[0..std::mem::size_of::<u64>()]);
        u64::from_le_bytes(bytes)
    }

    pub fn is_value(&self) -> bool {
        self.size() != 0 || self.index() == 0
    }

    pub fn set_type(&mut self, value: u8) {
        let v = (value << 4) | self.size();
        self.value[15] = v;
    }

    pub fn set_index(&mut self, index: u64) {
        self.set_size(0);
        self.value[0..std::mem::size_of::<u64>()].copy_from_slice(&index.to_le_bytes());
    }

    pub fn set_value(&mut self, value: &[u8]) -> bool {
        if value.len() > 15 {
            return false;
        }

        self.set_size(value.len() as u8);
        self.value[0..(value.len())].copy_from_slice(value);

        true
    }

    pub fn size(&self) -> u8 {
        self.value[15] & 0b00001111
    }

    pub fn value(&self) -> &[u8] {
        let pos = self.size();
        &self.value[0..(pos as usize)]
    }

    fn set_size(&mut self, size: u8) {
        let v = (size & 0b00001111) | (self.value[15] & 0b11110000);
        self.value[15] = v;
    }
}

impl Serialize for DbValueIndex {
    fn serialize(&self) -> Vec<u8> {
        self.value.to_vec()
    }

    fn deserialize(bytes: &[u8]) -> Result<Self, DbError> {
        if bytes.len() < 16 {
            return Err(DbError::db(
                DbErrorType::NotEnoughData,
                format!(
                    "DbValueIndex deserialization error: got {} bytes, expected at least 16 bytes",
                    bytes.len()
                ),
            ));
        }

        let mut index = Self::default();
        index.value.copy_from_slice(&bytes[0..16]);
        Ok(index)
    }

    fn serialized_size(&self) -> u64 {
        Self::serialized_size_static()
    }
}

impl SerializeStatic for DbValueIndex {
    fn serialized_size_static() -> u64 {
        16
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn derived_from_debug() {
        let _ = format!("{:?}", DbValueIndex::new());
    }

    #[test]
    fn derived_from_clone_partial_eq() {
        let index = DbValueIndex::new();
        assert_eq!(index, index.clone());
    }

    #[test]
    fn value_type() {
        let mut index = DbValueIndex::default();
        index.set_type(3);

        assert_eq!(index.get_type(), 3);
    }

    #[test]
    fn value() {
        let mut index = DbValueIndex::default();
        let value = vec![1_u8, 2_u8, 3_u8];

        assert!(index.set_value(&value));
        assert_eq!(index.value(), value);
        assert_eq!(index.size(), 3);
        assert!(index.is_value());
    }

    #[test]
    fn value_type_size() {
        let mut index = DbValueIndex::default();
        let value = vec![1_u8, 2_u8, 3_u8];

        index.set_type(5);

        assert!(index.set_value(&value));
        assert_eq!(index.value(), value);
        assert_eq!(index.size(), 3);
        assert_eq!(index.get_type(), 5);
        assert!(index.is_value());
    }

    #[test]
    fn value_max() {
        let mut index = DbValueIndex::default();
        let value = vec![1_u8; 15];
        assert!(index.set_value(&value));

        assert_eq!(index.value(), value);
        assert_eq!(index.size(), 15);
        assert!(index.is_value());
    }

    #[test]
    fn value_too_large() {
        let mut index = DbValueIndex::default();
        let value = vec![1_u8; 16];
        assert!(!index.set_value(&value));

        assert_eq!(index.value(), Vec::<u8>::new());
        assert_eq!(index.size(), 0);
        assert!(index.is_value());
    }

    #[test]
    fn set_index() {
        let mut index = DbValueIndex::default();
        index.set_index(10_u64);

        assert_eq!(index.value(), Vec::<u8>::new());
        assert_eq!(index.size(), 0);
        assert_eq!(index.index(), 10_u64);
        assert!(!index.is_value());
    }

    #[test]
    fn serialize() {
        let mut index = DbValueIndex::new();
        assert!(index.set_value(&[1_u8, 2_u8, 3_u8]));

        let data = index.serialize();
        let other_index = DbValueIndex::deserialize(&data).unwrap();

        assert_eq!(index, other_index);
    }

    #[test]
    fn bad_deserialize() {
        let bad_data = vec![1_u8; 15];
        assert_eq!(
            DbValueIndex::deserialize(&bad_data)
                .unwrap_err()
                .description,
            "DbValueIndex deserialization error: got 15 bytes, expected at least 16 bytes"
        );
    }

    #[test]
    fn serialized_size() {
        assert_eq!(DbValueIndex::default().serialized_size(), 16);
    }
}
