Documentation
use buffer_sv2::{Buffer, Slice};
use std::sync::{Arc, Mutex};

use core::{sync::atomic::Ordering, time::Duration};
use rand::Rng;

const FILE_LEN: usize = 5242880;
pub const DATA: &[u8; FILE_LEN] = include_bytes!("random");

#[inline(always)]
pub fn add_random_bytes(message_len: usize, buffer: &mut impl Buffer, input: &[u8]) {
    let rounds = message_len / 10;

    for i in 0..rounds {
        let writable: &mut [u8] = buffer.get_writable(10).as_mut();
        writable.copy_from_slice(&input[i..i + 10]);
    }
}

pub trait Load: AsMut<[u8]> {
    fn load(&mut self) -> usize;
}

impl Load for Vec<u8> {
    #[inline(always)]
    fn load(&mut self) -> usize {
        self.len()
    }
}

impl Load for Slice {
    #[inline(always)]
    fn load(&mut self) -> usize {
        self.shared_state.load(Ordering::SeqCst) as usize
    }
}

#[inline(always)]
pub fn keep_slice(mut slice: impl Load + Send + 'static, d: Duration) {
    std::thread::spawn(move || {
        let _time = (2_usize.pow(16) - slice.as_mut().len()) / 1000;
        std::thread::sleep(d);
        let _ = slice.load();
    });
}

#[inline(always)]
pub fn bench_no_thread(mut pool: impl Buffer, data: &[u8]) {
    let mut rng = rand::thread_rng();
    for i in 0..1000 {
        let message_length = 2_usize.pow(14) - rng.gen_range(0..12000);
        add_random_bytes(message_length, &mut pool, &data[i..]);

        pool.get_data_owned();

        add_random_bytes(message_length, &mut pool, &data[i..]);

        pool.get_data_owned();
    }
}

impl Load for SSlice {
    #[inline(always)]
    fn load(&mut self) -> usize {
        78
    }
}

impl Load for MaxESlice {
    #[inline(always)]
    fn load(&mut self) -> usize {
        78
    }
}

use std::collections::BTreeMap;
pub struct PPool {
    pool: BTreeMap<u8, Vec<u8>>,
    free_slots: Vec<u8>,
    to_free: Arc<Mutex<Vec<u8>>>,
}

pub struct SSlice {
    offset: *mut u8,
    len: usize,
    index: u8,
    to_free: Arc<Mutex<Vec<u8>>>,
}

impl From<SSlice> for Slice {
    fn from(_v: SSlice) -> Self {
        todo!()
    }
}

impl AsMut<[u8]> for SSlice {
    #[inline(always)]
    fn as_mut(&mut self) -> &mut [u8] {
        unsafe { core::slice::from_raw_parts_mut(self.offset, self.len) }
    }
}

impl AsRef<[u8]> for SSlice {
    #[inline(always)]
    fn as_ref(&self) -> &[u8] {
        unsafe { core::slice::from_raw_parts(self.offset, self.len) }
    }
}

impl Drop for SSlice {
    fn drop(&mut self) {
        let mut to_free = self.to_free.lock().unwrap();
        to_free.push(self.index);
    }
}

impl PPool {
    pub fn new(capacity: usize) -> Self {
        let mut free_slots: Vec<u8> = Vec::with_capacity(255);
        let mut map: BTreeMap<u8, Vec<u8>> = BTreeMap::new();
        for k in 0..255 {
            let mut b = vec![0; capacity];
            unsafe { b.set_len(0) };
            map.insert(k, b);
            free_slots.push(k);
        }
        let to_free = Arc::new(Mutex::new(Vec::new()));
        Self {
            pool: map,
            free_slots,
            to_free,
        }
    }

    #[inline(never)]
    pub fn free(&mut self) {
        let mut slots = self.to_free.lock().unwrap();
        for _ in 0..slots.len() {
            let slot = slots.pop().unwrap();
            self.free_slots.push(slot);
            let b = self.pool.get_mut(&slot).unwrap();
            unsafe { b.set_len(0) };
        }
    }
}

impl Buffer for PPool {
    type Slice = SSlice;

    #[inline(always)]
    fn get_writable(&mut self, len: usize) -> &mut [u8] {
        if self.free_slots.len() > 0 {
            let slot = self.free_slots[self.free_slots.len() - 1];

            let b = self.pool.get_mut(&slot).unwrap();
            let offset = b.len();

            if offset + len <= b.capacity() {
                unsafe { b.set_len(offset + len) };
                &mut b[offset..offset + len]
            } else {
                panic!()
            }
        } else {
            self.free();
            self.get_writable(len)
        }
    }

    #[inline(always)]
    fn get_data_owned(&mut self) -> Self::Slice {
        let slot = self.free_slots.pop().unwrap();

        let b = self.pool.get_mut(&slot).unwrap();

        let offset = b.as_mut_ptr();

        SSlice {
            offset,
            len: b.len(),
            index: slot,
            to_free: self.to_free.clone(),
        }
    }

    fn get_data_by_ref(&mut self, _len: usize) -> &mut [u8] {
        todo!()
    }

    fn len(&self) -> usize {
        todo!()
    }

    fn get_data_by_ref_(&self, _len: usize) -> &[u8] {
        todo!()
    }

    fn danger_set_start(&mut self, _index: usize) {
        todo!()
    }

    fn is_droppable(&self) -> bool {
        todo!()
    }
}

unsafe impl Send for SSlice {}

pub struct MaxEfficiency {
    inner: Vec<u8>,
}

impl MaxEfficiency {
    pub fn new(capacity: usize) -> Self {
        let inner = vec![0; capacity];
        Self { inner }
    }
}

pub struct MaxESlice {
    offset: *mut u8,
    len: usize,
}

impl From<MaxESlice> for Slice {
    fn from(_v: MaxESlice) -> Self {
        todo!()
    }
}

impl AsMut<[u8]> for MaxESlice {
    #[inline(always)]
    fn as_mut(&mut self) -> &mut [u8] {
        unsafe { core::slice::from_raw_parts_mut(self.offset, self.len) }
    }
}

impl AsRef<[u8]> for MaxESlice {
    #[inline(always)]
    fn as_ref(&self) -> &[u8] {
        unsafe { core::slice::from_raw_parts(self.offset, self.len) }
    }
}

unsafe impl Send for MaxESlice {}

impl Buffer for MaxEfficiency {
    type Slice = MaxESlice;

    #[inline(always)]
    fn get_writable(&mut self, len: usize) -> &mut [u8] {
        &mut self.inner[0..len]
    }

    #[inline(always)]
    fn get_data_owned(&mut self) -> Self::Slice {
        let offset = self.inner.as_mut_ptr();

        MaxESlice {
            offset,
            len: self.inner.len(),
        }
    }

    fn get_data_by_ref(&mut self, _len: usize) -> &mut [u8] {
        todo!()
    }

    fn len(&self) -> usize {
        todo!()
    }

    fn get_data_by_ref_(&self, _len: usize) -> &[u8] {
        todo!()
    }

    fn danger_set_start(&mut self, _index: usize) {
        todo!()
    }

    fn is_droppable(&self) -> bool {
        todo!()
    }
}