use crate::*;

/// Storage of variable length values (varval).
struct VarValStore {
    tv: (u64, u64),
}

impl VarValStore {
    pub fn new(tv: (u64, u64)) -> Self {
        Self { tv }
    }

    pub fn save(&self) -> (u64, u64) {
        self.tv
    }

    /// Store a new varval, returns addr.
    pub fn store(&mut self, user_data: &[u8], ps: &mut PageSet) -> u64 {
        let mut tv = TreeVec::new(self.tv, ps);
        let result = self.tv.1;
        tv.write(result, user_data);
        self.tv = tv.save();
        result
    }

    /// Reads a stored varval with given addr and len into user_data.
    ///
    /// Could have version that writes to any Writer ( struct implementing std::io::Write ).
    pub fn get(&self, addr: u64, len: usize, user_data: &mut [u8], ps: &mut PageSet) {
        assert!(user_data.len() >= len);
        let mut tv = TreeVec::new(self.tv, ps);
        tv.read(addr, user_data);
    }

    /// Delete a stored varval with given id and len.
    pub fn delete(&mut self, _addr: u64, _len: usize, _ps: &mut PageSet) {
        // ToDo : increase free space
    }

    /// Update a varval, returns new id.
    ///
    /// Will re-use addr if length does not change.
    pub fn update(&mut self, addr: u64, old_len: usize, user_data: &[u8], ps: &mut PageSet) -> u64 {
        let mut tv = TreeVec::new(self.tv, ps);
        if old_len == user_data.len() {
            tv.write(addr, user_data);
            addr
        } else {
            self.delete(addr, old_len, ps);
            self.store(user_data, ps)
        }
    }
}

// ************** End VarVal

/// Address of stored variable length value ( varval ).
#[derive(Debug, Eq, PartialEq, Copy, Clone, Ord, PartialOrd)] // Comparison should really just be done on id.
pub struct VarValAddr {
    /// This will eventually be more complicated, e,g, may be a TreeVec, or diferent VarValStores.
    addr: u64,
    len: u64,
}

impl VarValAddr {
    pub fn new(dt: &DataType, v: &Value, ps: &mut PageSet) -> Self {
        let bytes = dt.value_to_bytes(v);

        let len = bytes.len() as u64;

        let mut vv = VarValStore::new(ps.vvs); // Will be multiple stores eventually.
        let addr = vv.store(&bytes, ps);
        ps.vvs = vv.save();

        Self { addr, len }
    }

    pub fn get(&self, dt: &DataType, ps: &mut PageSet) -> Value {
        let vv = VarValStore::new(ps.vvs);
        let mut buf = vec![0; self.len as usize];
        vv.get(self.addr, self.len as usize, &mut buf, ps);

        dt.bytes_to_value(&buf)
    }

    pub fn update(&self, dt: &DataType, v: &Value, ps: &mut PageSet) -> Self {
        let mut vv = VarValStore::new(ps.vvs);

        let bytes = dt.value_to_bytes(v);
        let len = bytes.len() as u64;
        let addr = vv.update(self.addr, self.len as usize, &bytes, ps);
        ps.vvs = vv.save();

        Self { addr, len }
    }

    pub fn delete(&self, ps: &mut PageSet) {
        let mut vv = VarValStore::new(ps.vvs);
        vv.delete(self.addr, self.len as usize, ps);
        ps.vvs = vv.save();
    }
}

impl SmallFixed for VarValAddr {
    fn size() -> usize {
        2 * 8
    }
    fn load(bytes: &[u8]) -> Self {
        let addr = u64::from_le_bytes(bytes[0..8].try_into().unwrap());
        let len = u64::from_le_bytes(bytes[8..16].try_into().unwrap());
        Self { addr, len }
    }
    fn save(&self, bytes: &mut [u8]) {
        bytes[0..8].copy_from_slice(&self.addr.to_le_bytes());
        bytes[8..16].copy_from_slice(&self.len.to_le_bytes());
    }
}

// ************** End VarValAddr

#[cfg(test)]
pub fn test_varval(ps: &mut PageSet) {
    let tv = (ps.new_page(), 0);
    let mut vv = VarValStore { tv };

    let mut ids = Vec::new();

    let d1 = b"Hello there George";
    let d2 = b"Tuesday is lovely, so is Wednesday";
    //let d2 = b"Tuesday is lovely! So is Wednesday. .............................................Saturday also.";
    for _ in 0..1_000_000 {
        let id = vv.store(d1, ps);
        ids.push(id);
        // println!("test varval stored {}", id);
        // println!("");
    }

    //println!("ids={:?}", ids);

    let mut buf = vec![0; d1.len()];
    for id in &ids {
        vv.get(*id, d1.len(), &mut buf, ps);
        // println!("buf={}", tos(&buf));
        assert_eq!(buf, d1);
    }

    let mut nids = Vec::new();
    for id in &ids {
        let nid = vv.update(*id, d1.len(), d2, ps);
        nids.push(nid);
    }

    let mut buf = vec![0; d2.len()];
    for id in &nids {
        vv.get(*id, d2.len(), &mut buf, ps);
        assert_eq!(buf, d2);
    }
}
