use super::super::{args::ColorType, types::{HeaderEncoder, set_member_value, Rect}};
use std::io::Write;
use tool_helper::{bits::BitRange, raw::RawConversion, Error};

#[repr(packed(1))]
pub struct Header {
    value: u32
}

impl Header {
    const TEX_WIDTH_BIT_RANGE:   BitRange = BitRange::from_to(0, 8);
    const TEX_HEIGHT_BIT_RANGE:  BitRange = BitRange::from_to(9, 16);
    const CLUT_WIDTH_BIT_RANGE:  BitRange = BitRange::from_to(17, 22);
    const CLUT_HEIGHT_BIT_RANGE: BitRange = BitRange::from_to(23, 31);
}

impl Default for Header {
    fn default() -> Self {
        Header{value: 0}
    }
}

impl HeaderEncoder for Header {
    fn encode_settings(&mut self, _color_type: ColorType, tex_rect: Rect, clut_rect: Rect) -> Result<(), Error> {
        let clut_width  = clut_rect.width;
        let clut_height = clut_rect.height;
        let tex_width   = tex_rect.width;
        let tex_height  = tex_rect.height;

        if tex_width & 1 == 1 || tex_height & 1 == 1 {
            Err(Error::from_text(format!("Image size (width: {}, height: {}) needs to be even", tex_width, tex_height)))
        }

        else {
            let value = set_member_value!(set_member_value!(set_member_value!(set_member_value!(0, 
                tex_width,   1, u32),
                tex_height,  1, u32),
                clut_width,  4, u32),
                clut_height, 0, u32);

            self.value = value;
            Ok(())
        }
    }

    fn write_header(&self, output: &mut dyn Write) -> Result<usize, Error> {
        Ok(output.write(&self.convert_to_raw())?)
    }

    fn write_clut_header(&self, _output: &mut dyn Write) -> Result<usize, Error> {
        Ok(0)
    }

    fn write_pixel_header(&self, _output: &mut dyn Write) -> Result<usize, Error> {
        Ok(0)
    }
}

impl RawConversion<4> for Header {
    fn convert_to_raw(&self) -> [u8; 4] {
        self.value.to_le_bytes()
    }
}