use crate::types::{Chars, Nil, Symbol};

pub trait StaticString {
    const VALUE: &'static str;
}

impl<T> StaticString for T
where
    T: StaticBytes,
{
    const VALUE: &'static str = const {
        match str::from_utf8(T::BYTES) {
            Ok(value) => value,
            Err(_) => panic!("error const decoding &[u8] to &str"),
        }
    };
}

trait StaticBytes {
    const BYTES: &'static [u8];
}

trait MaybeChars {
    const VALUE: Option<char>;

    type Next: MaybeChars;
}

impl<const LEN: usize, Chars> StaticBytes for Symbol<LEN, Chars>
where
    Chars: MaybeChars,
{
    const BYTES: &'static [u8] = &static_chars::<LEN, Chars>();
}

impl StaticBytes for Nil {
    const BYTES: &'static [u8] = &[];
}

impl<const CHAR: char, Tail> MaybeChars for Chars<CHAR, Tail>
where
    Tail: MaybeChars,
{
    const VALUE: Option<char> = Some(CHAR);

    type Next = Tail;
}

impl MaybeChars for Nil {
    const VALUE: Option<char> = None;

    type Next = Nil;
}

const fn static_chars<const LEN: usize, S: MaybeChars>() -> [u8; LEN] {
    let mut chars = [0; LEN];

    update_chars::<S>(&mut chars);

    chars
}

const fn update_chars<S: MaybeChars>(mut chars: &mut [u8]) {
    if let Some(value) = S::VALUE {
        value.encode_utf8(chars);

        let len = value.len_utf8();

        let mut j = 0;
        while j < len {
            chars = chars.split_first_mut().unwrap().1;
            j += 1;
        }

        update_chars::<S::Next>(chars);
    }
}
