use std::convert::{TryFrom, TryInto};
use std::fmt::Debug;

pub const VERSION: u8 = 1;
pub const BITMAP_HEADER_SIZE: usize = 1 + 8 + 4 + 32;

#[derive(Clone, Debug)]
pub(crate) struct BitMap {
    header_and_bits: Vec<u8>,
}

impl BitMap {
    pub fn new(len_bytes: usize) -> Self {
        let mut header_and_bits = vec![0; BITMAP_HEADER_SIZE + len_bytes];
        let header = &mut header_and_bits[0..BITMAP_HEADER_SIZE];
        Self::set_version(header, VERSION);
        Self::set_len_bytes(header, len_bytes as u64);
        Self::set_k_num(header, 0);
        Self { header_and_bits }
    }

    #[inline]
    fn bits(&self) -> &[u8] {
        &self.header_and_bits[BITMAP_HEADER_SIZE..]
    }

    #[inline]
    fn bits_mut(&mut self) -> &mut [u8] {
        &mut self.header_and_bits[BITMAP_HEADER_SIZE..]
    }

    #[inline]
    pub fn header(&self) -> &[u8] {
        &self.header_and_bits[0..BITMAP_HEADER_SIZE]
    }

    #[inline]
    pub fn header_mut(&mut self) -> &mut [u8] {
        &mut self.header_and_bits[0..BITMAP_HEADER_SIZE]
    }

    #[inline]
    fn get_version(header: &[u8]) -> u8 {
        header[0]
    }

    #[inline]
    fn set_version(header: &mut [u8], version: u8) {
        header[0] = version;
    }

    #[inline]
    fn get_len_bytes(header: &[u8]) -> u64 {
        u64::from_le_bytes(header[1..][0..8].try_into().unwrap())
    }

    #[inline]
    fn set_len_bytes(header: &mut [u8], len_bytes: u64) {
        header[1..][0..8].copy_from_slice(&len_bytes.to_le_bytes());
    }

    #[inline]
    pub fn get_k_num(header: &[u8]) -> u32 {
        u32::from_le_bytes(header[9..][0..4].try_into().unwrap())
    }

    #[inline]
    pub fn set_k_num(header: &mut [u8], k_num: u32) {
        header[9..][0..4].copy_from_slice(&k_num.to_le_bytes());
    }

    #[inline]
    pub fn get_seed(header: &[u8]) -> [u8; 32] {
        header[13..][0..32].try_into().unwrap()
    }

    #[inline]
    pub fn set_seed(header: &mut [u8], seed: &[u8; 32]) {
        header[13..][0..32].copy_from_slice(seed);
    }

    pub fn from_bytes(bytes: Vec<u8>) -> Result<Self, &'static str> {
        if bytes.len() < BITMAP_HEADER_SIZE {
            return Err("Invalid size");
        }
        let header = &bytes[0..BITMAP_HEADER_SIZE];
        let bits = &bytes[BITMAP_HEADER_SIZE..];
        if Self::get_version(header) != VERSION {
            return Err("Version mismatch");
        }
        if Self::get_k_num(header) == 0 {
            return Err("Invalid number of keys");
        }
        let len_bytes_u64 = Self::get_len_bytes(header);
        let len_bytes: usize = len_bytes_u64.try_into().map_err(|_| "Too big")?;
        if bits.len() != len_bytes {
            return Err("Invalid size");
        }
        let res = Self {
            header_and_bits: bytes,
        };
        Ok(res)
    }

    pub fn from_slice(bytes: &[u8]) -> Result<Self, &'static str> {
        if bytes.len() < BITMAP_HEADER_SIZE {
            return Err("Invalid size");
        }
        let header = &bytes[0..BITMAP_HEADER_SIZE];
        let bits = &bytes[BITMAP_HEADER_SIZE..];
        if Self::get_version(header) != VERSION {
            return Err("Version mismatch");
        }
        if Self::get_k_num(header) == 0 {
            return Err("Invalid number of keys");
        }
        let len_bytes_u64 = Self::get_len_bytes(header);
        let len_bytes: usize = len_bytes_u64.try_into().map_err(|_| "Too big")?;
        if bits.len() != len_bytes {
            return Err("Invalid size");
        }
        let res = Self {
            header_and_bits: bytes.to_vec(),
        };
        Ok(res)
    }

    #[inline]
    pub fn as_slice(&self) -> &[u8] {
        &self.header_and_bits
    }

    #[inline]
    pub fn into_bytes(self) -> Vec<u8> {
        self.header_and_bits
    }

    #[inline]
    pub fn to_bytes(&self) -> Vec<u8> {
        self.header_and_bits.clone()
    }

    pub fn get(&self, bit_offset: usize) -> bool {
        let byte_offset = bit_offset / 8;
        let bit_shift = bit_offset % 8;
        (self.bits()[byte_offset] & (1 << bit_shift)) != 0
    }

    pub fn set(&mut self, bit_offset: usize) {
        let byte_offset = bit_offset / 8;
        let bit_shift = bit_offset % 8;
        self.bits_mut()[byte_offset] |= 1 << bit_shift;
    }

    pub fn clear(&mut self) {
        for byte in self.bits_mut().iter_mut() {
            *byte = 0;
        }
    }

    pub fn set_all(&mut self) {
        for byte in self.bits_mut().iter_mut() {
            *byte = !0;
        }
    }

    pub fn any(&self) -> bool {
        self.bits().iter().any(|&byte| byte != 0)
    }

    pub fn len_bits(&self) -> u64 {
        u64::try_from(self.bits().len())
            .unwrap()
            .checked_mul(8)
            .unwrap()
    }

    #[doc(hidden)]
    pub fn realloc_large_heap_allocated_objects(mut self, f: fn(Vec<u8>) -> Vec<u8>) -> Self {
        let previous_len = self.header_and_bits.len();
        self.header_and_bits = f(self.header_and_bits);
        assert_eq!(previous_len, self.header_and_bits.len());
        assert_eq!(Self::get_version(self.header()), VERSION);
        self
    }
}
