#![no_std]
#![feature(
    linked_list_cursors,
    async_fn_traits,
    maybe_uninit_slice,
    allocator_api,
    let_chains,
    const_trait_impl,
    str_from_utf16_endian
)]
#![allow(clippy::new_without_default)]

extern crate alloc;

use core::{
    mem::MaybeUninit,
    ops::{Add, Deref, DerefMut, Mul, Range, Sub},
    panic,
};

pub mod bit;
pub mod event;
pub mod ext;
pub mod hash_table;
pub mod io;
pub mod lru_hash_table;
pub mod queue;
pub mod ring;
pub mod string;
pub mod sync;
pub mod waker;

// TODO make a crate for such utils?
pub use kernel_arch::util::OneTimeInit;

pub enum ConstAssert<const CHECK: bool> {}
pub trait IsTrue {}

impl IsTrue for ConstAssert<true> {}

pub trait RangeExt<N> {
    fn chunked_range(self, step: N) -> ChunkedRangeIter<N>;
    fn mul(self, mul: N) -> Self;
    fn add(self, add: N) -> Self;
}

pub struct ChunkedRangeIter<N> {
    pos: N,
    end: N,
    step: N,
}

impl<N> ChunkedRangeIter<N> {
    pub fn from_range(range: Range<N>, step: N) -> Self {
        Self {
            pos: range.start,
            end: range.end,
            step,
        }
    }
}

impl<N: Copy + Ord + Add<Output = N> + Sub<Output = N>> Iterator for ChunkedRangeIter<N> {
    type Item = Range<N>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.pos >= self.end {
            return None;
        }
        let step = core::cmp::min(self.end - self.pos, self.step);
        let range = self.pos..self.pos + step;
        self.pos = self.pos + step;

        Some(range)
    }
}

impl<N: Mul<Output = N> + Add<Output = N> + Copy> RangeExt<N> for Range<N> {
    fn chunked_range(self, step: N) -> ChunkedRangeIter<N> {
        ChunkedRangeIter::from_range(self, step)
    }

    fn mul(self, mul: N) -> Self {
        self.start * mul..self.end * mul
    }

    fn add(self, add: N) -> Self {
        self.start + add..self.end + add
    }
}

/// Statically-allocated "dynamic" vector
pub struct StaticVector<T, const N: usize> {
    data: [MaybeUninit<T>; N],
    len: usize,
}

impl<T, const N: usize> StaticVector<T, N> {
    /// Constructs an empty instance of [StaticVector]
    pub const fn new() -> Self {
        Self {
            data: [const { MaybeUninit::uninit() }; N],
            len: 0,
        }
    }

    /// Appends an item to the vector.
    ///
    /// # Panics
    ///
    /// Will panic if the vector is full.
    pub fn push(&mut self, value: T) {
        if self.len >= N {
            panic!("Static vector overflow: reached limit of {}", N);
        }

        self.data[self.len].write(value);
        self.len += 1;
    }

    /// Returns the number of items present in the vector
    pub fn len(&self) -> usize {
        self.len
    }

    /// Returns `true` if the vector is empty
    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    /// Clears the vector, dropping all elements
    pub fn clear(&mut self) {
        let old_len = self.len;
        self.len = 0;

        for i in 0..old_len {
            unsafe {
                self.data[i].assume_init_drop();
            }
        }
    }
}

impl<T, const N: usize> Deref for StaticVector<T, N> {
    type Target = [T];

    fn deref(&self) -> &Self::Target {
        unsafe { self.data[..self.len].assume_init_ref() }
    }
}

impl<T, const N: usize> DerefMut for StaticVector<T, N> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { self.data[..self.len].assume_init_mut() }
    }
}

pub fn get_le_u16(bytes: &[u8]) -> u16 {
    u16::from_le_bytes([bytes[0], bytes[1]])
}

pub fn get_le_u32(bytes: &[u8]) -> u32 {
    u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
}

#[cfg(test)]
mod tests {
    use crate::RangeExt;

    #[test]
    fn chunked_range() {
        let r = 0..30;
        let mut i = 0;
        for range in r.chunked_range(10) {
            assert_eq!(range, i..i + 10);
            i += 10;
        }

        let r = 0..28;
        let mut it = r.chunked_range(10);
        assert_eq!(it.next(), Some(0..10));
        assert_eq!(it.next(), Some(10..20));
        assert_eq!(it.next(), Some(20..28));
        assert_eq!(it.next(), None);
    }
}
