use serde::Serialize;

use crate::{Result, ZipError};

pub(crate) const EOCD_SIG: u32 = 0x0605_4b50;
pub(crate) const CENTRAL_SIG: u32 = 0x0201_4b50;
pub(crate) const LOCAL_SIG: u32 = 0x0403_4b50;

#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Compression {
    Stored,
    Deflate,
}

impl Compression {
    pub(crate) fn from_u16(value: u16) -> Result<Self> {
        match value {
            0 => Ok(Self::Stored),
            8 => Ok(Self::Deflate),
            _ => Err(ZipError::Unsupported("compression method")),
        }
    }
}

#[derive(Clone, Debug, Serialize)]
pub struct Entry {
    pub name: String,
    pub compression: Compression,
    pub compressed_size: u64,
    pub uncompressed_size: u64,
    pub crc32: u32,
    pub is_directory: bool,
    pub external_attributes: u32,
    #[serde(skip)]
    pub(crate) flags: u16,
    #[serde(skip)]
    pub(crate) local_offset: usize,
    #[serde(skip)]
    pub(crate) raw_name: Vec<u8>,
}

#[derive(Clone, Copy, Debug)]
pub(crate) struct Eocd {
    pub entries: usize,
    pub central_offset: usize,
    pub central_size: usize,
}

#[derive(Clone, Copy, Debug)]
pub(crate) struct LocalHeader {
    pub data_offset: usize,
    pub method: Compression,
    pub flags: u16,
    pub name_offset: usize,
    pub name_len: usize,
}

pub(crate) fn u16le(data: &[u8], offset: usize) -> Result<u16> {
    let b = data
        .get(offset..offset + 2)
        .ok_or(ZipError::Invalid("truncated integer"))?;
    Ok(u16::from_le_bytes([b[0], b[1]]))
}

pub(crate) fn u32le(data: &[u8], offset: usize) -> Result<u32> {
    let b = data
        .get(offset..offset + 4)
        .ok_or(ZipError::Invalid("truncated integer"))?;
    Ok(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
}
