use std::time::{SystemTime, UNIX_EPOCH};
use crate::impl_display_from_debug;
use crate::python_util::PythonVersion;
use crate::Str;
pub const fn get_magic_num_bytes(python_ver: u32) -> [u8; 4] {
    pub const PREFIX: u32 = 0xA0D0000;
    (PREFIX | python_ver).to_le_bytes()
}
pub const fn get_magic_num_from_bytes(bytes: &[u8; 4]) -> u32 {
    u32::from_le_bytes([bytes[0], bytes[1], 0, 0])
}
pub const fn get_ver_from_magic_num(magic_num: u32) -> PythonVersion {
    match magic_num {
        3360..=3379 => PythonVersion::new(3, Some(6), Some(0)),
        3390..=3394 => PythonVersion::new(3, Some(7), Some(0)),
        3400..=3413 => PythonVersion::new(3, Some(8), Some(0)),
        3420..=3425 => PythonVersion::new(3, Some(9), Some(0)),
        3430..=3439 => PythonVersion::new(3, Some(10), Some(0)),         3495 => PythonVersion::new(3, Some(11), Some(0)),
        _ => panic!("unknown magic number"),
    }
}
pub fn get_timestamp_bytes() -> [u8; 4] {
    let secs = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map(|dur| dur.as_secs() as u32)
        .unwrap_or(0);
    secs.to_le_bytes()
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum DataTypePrefix {
    
    Illegal = 0,
    Int32 = b'i',          Int64 = b'I',          Float = b'f',          BinFloat = b'g',       Complex = b'x',        BinComplex = b'y',     True = b'T',           False = b'F',          None = b'N',           StopIter = b'S',       Ref = b'r',
    
    Long = b'l',     Str = b's',      ShortAscii = b'z' + 0x80,     ShortAsciiInterned = b'Z' + 0x80,     Unicode = b'u',     Interned = b't',     SmallTuple = b')',     Tuple = b'(',     Code = b'c' + 0x80,     
    Builtin = b'b',     Nat = b'n',
}
impl_display_from_debug!(DataTypePrefix);
impl From<u8> for DataTypePrefix {
    fn from(item: u8) -> Self {
        match item as char {
            'i' | '\u{00E9}' => Self::Int32,
            'I' => Self::Int64,
            'l' => Self::Long,
            'f' => Self::Float,
            'g' => Self::BinFloat,
            'x' => Self::Complex,
            'y' => Self::BinComplex,
            'T' => Self::True,
            'F' => Self::False,
            'N' => Self::None,
            'S' => Self::StopIter,
            's' | '\u{00F3}' => Self::Str,
            'Z' | '\u{00DA}' => Self::ShortAsciiInterned,
            'z' | '\u{00FA}' => Self::ShortAscii,
            'u' => Self::Unicode,
            't' => Self::Interned,
            '(' | '\u{00A8}' => Self::Tuple,
            ')' | '\u{00A9}' => Self::SmallTuple,
            'c' | '\u{00E3}' => Self::Code,
            'b' => Self::Builtin,
            'n' => Self::Nat,
            
            _ => Self::Illegal,
        }
    }
}
impl DataTypePrefix {
    pub const fn is_sized(&self) -> bool {
        matches!(
            self,
            Self::Long
                | Self::Str
                | Self::ShortAscii
                | Self::ShortAsciiInterned
                | Self::Unicode
                | Self::Interned
                | Self::SmallTuple
                | Self::Tuple
                | Self::Code
                | Self::Builtin
        )
    }
}
pub fn strs_into_bytes(names: Vec<Str>) -> Vec<u8> {
    let mut tuple = vec![];
    if names.len() > u8::MAX as usize {
        tuple.push(DataTypePrefix::Tuple as u8);
        tuple.append(&mut (names.len() as u32).to_le_bytes().to_vec());
    } else {
        tuple.push(DataTypePrefix::SmallTuple as u8);
        tuple.push(names.len() as u8);
    }
    for name in names.into_iter() {
        tuple.append(&mut str_into_bytes(name, true));
    }
    tuple
}
pub fn str_into_bytes(cont: Str, is_interned: bool) -> Vec<u8> {
    let mut bytes = vec![];
    if cont.is_ascii() {
        if is_interned {
            bytes.push(DataTypePrefix::ShortAsciiInterned as u8);
        } else {
            bytes.push(DataTypePrefix::ShortAscii as u8);
        }
        bytes.push(cont.len() as u8);
    } else {
        bytes.push(DataTypePrefix::Unicode as u8);
        bytes.append(&mut (cont.len() as u32).to_le_bytes().to_vec());
    };
    bytes.append(&mut cont.as_bytes().to_vec());
    bytes
}
pub fn raw_string_into_bytes(mut cont: Vec<u8>) -> Vec<u8> {
    let mut tuple = vec![DataTypePrefix::Str as u8];
    tuple.append(&mut (cont.len() as u32).to_le_bytes().to_vec());
    tuple.append(&mut cont);
    tuple
}