use crate::Signature;
use androscalpel_serializer::{ReadSeek, Result, Serializable};
use std::io::{SeekFrom, Write};

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DataDescriptor {
    Zip32(DataDescriptor32),
    Zip64(DataDescriptor64),
}

/// DataDescriptor for 32bit zip format.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DataDescriptor32 {
    // signature: Option<Signature(0x08074b50)>
    pub crc_32: u32,
    pub compressed_size: u32,
    pub uncompressed_size: u32,
    /// For reproducibility. This should be true when writting a zip, but can be false when
    /// reading one. If we want to stick to the original, we need to keep this information.
    pub use_signature: bool,
}

/// DataDescriptor for 64bit zip format.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DataDescriptor64 {
    // signature: Option<Signature(0x08074b50)>
    pub crc_32: u32,
    pub compressed_size: u64,
    pub uncompressed_size: u64,
    /// For reproducibility. This should be true when writting a zip, but can be false when
    /// reading one. If we want to stick to the original, we need to keep this information.
    pub use_signature: bool,
}

impl DataDescriptor32 {
    const SIGNATURE: Signature = Signature(0x08074b50);
}

impl Serializable for DataDescriptor32 {
    fn serialize(&self, output: &mut dyn Write) -> Result<()> {
        if self.use_signature {
            Self::SIGNATURE.serialize(output)?;
        }
        self.crc_32.serialize(output)?;
        self.compressed_size.serialize(output)?;
        self.uncompressed_size.serialize(output)
    }

    fn deserialize(input: &mut dyn ReadSeek) -> Result<Self> {
        let pos = input.stream_position().unwrap(); //TODO
        let signature = Signature::deserialize(input)?;
        let use_signature = if signature != Self::SIGNATURE {
            input.seek(SeekFrom::Start(pos)).unwrap(); //TODO
            false
        } else {
            true
        };
        let crc_32 = u32::deserialize(input)?;
        let compressed_size = u32::deserialize(input)?;
        let uncompressed_size = u32::deserialize(input)?;
        Ok(Self {
            crc_32,
            compressed_size,
            uncompressed_size,
            use_signature,
        })
    }
    fn size(&self) -> usize {
        12 + if self.use_signature {
            Self::SIGNATURE.size()
        } else {
            0
        }
    }
}

impl DataDescriptor64 {
    const SIGNATURE: Signature = Signature(0x08074b50);
}

impl Serializable for DataDescriptor64 {
    fn serialize(&self, output: &mut dyn Write) -> Result<()> {
        if self.use_signature {
            Self::SIGNATURE.serialize(output)?;
        }
        self.crc_32.serialize(output)?;
        self.compressed_size.serialize(output)?;
        self.uncompressed_size.serialize(output)
    }

    fn deserialize(input: &mut dyn ReadSeek) -> Result<Self> {
        let pos = input.stream_position().unwrap(); //TODO
        let signature = Signature::deserialize(input)?;
        let use_signature = if signature != Self::SIGNATURE {
            input.seek(SeekFrom::Start(pos)).unwrap(); //TODO
            false
        } else {
            true
        };
        let crc_32 = u32::deserialize(input)?;
        let compressed_size = u64::deserialize(input)?;
        let uncompressed_size = u64::deserialize(input)?;
        Ok(Self {
            crc_32,
            compressed_size,
            uncompressed_size,
            use_signature,
        })
    }
    fn size(&self) -> usize {
        20 + if self.use_signature {
            Self::SIGNATURE.size()
        } else {
            0
        }
    }
}
