// u32 per MPHF slot: bits [31:7] = chunk_id (25 bits), bits [6:0] = rank (7 bits).

use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::Path;

use memmap2::Mmap;

use crate::error::{OLMError, OLMResult};

pub struct Evidence {
    mmap: Mmap,
}

impl Evidence {
    pub fn open(path: &Path) -> OLMResult<Self> {
        let f = File::open(path)?;
        let mmap = unsafe { Mmap::map(&f)? };
        Ok(Self { mmap })
    }

    #[inline]
    pub fn decode(&self, slot: usize) -> (u32, u8) {
        let off = slot * 4;
        let raw = u32::from_le_bytes(self.mmap[off..off + 4].try_into().unwrap());
        (raw >> 7, (raw & 0x7F) as u8)
    }

    pub fn len(&self) -> usize {
        self.mmap.len() / 4
    }
}

#[inline]
pub fn encode(chunk_id: u32, rank: u8) -> u32 {
    (chunk_id << 7) | (rank as u32 & 0x7F)
}

pub struct EvidenceWriter {
    buf: Vec<u32>,
}

impl EvidenceWriter {
    pub fn new(n_slots: usize) -> Self {
        Self { buf: vec![0u32; n_slots] }
    }

    #[inline]
    pub fn set(&mut self, slot: usize, chunk_id: u32, rank: u8) {
        self.buf[slot] = encode(chunk_id, rank);
    }

    pub fn write(self, path: &Path) -> OLMResult<()> {
        let mut f = BufWriter::new(File::create(path)?);
        for v in self.buf {
            f.write_all(&v.to_le_bytes()).map_err(OLMError::Io)?;
        }
        Ok(())
    }
}
