use crate::errors::IndexedImageError;
use crate::errors::IndexedImageError::*;
use crate::palette::FilePalette::*;
use crate::IciColor;
pub(crate) const PAL_NO_DATA: u8 = 0;
pub(crate) const PAL_ID: u8 = 1;
pub(crate) const PAL_NAME: u8 = 2;
pub(crate) const PAL_COLORS: u8 = 3;
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum FilePalette {
    NoData,
    ID(u16),
    Name(String),
    Colors,
}
impl FilePalette {
    pub(crate) fn to_byte(&self) -> u8 {
        match self {
            NoData => PAL_NO_DATA,
            ID(_) => PAL_ID,
            Name(_) => PAL_NAME,
            Colors => PAL_COLORS,
        }
    }
}
pub(crate) fn write(
    palette: &FilePalette,
    colors: &[IciColor],
    output: &mut Vec<u8>,
) -> Result<(), IndexedImageError> {
    output.push(palette.to_byte());
    match palette {
        NoData => {}
        ID(id) => output.extend_from_slice(&id.to_be_bytes()),
        Name(name) => {
            let len = name.len();
            if len < 1 {
                return Err(PaletteNameTooShort);
            }
            if len > 255 {
                return Err(PaletteNameTooLong);
            }
            output.push(len as u8);
            output.extend_from_slice(name.as_bytes())
        }
        Colors => {
            output.push(colors.len() as u8);
            for color in colors {
                output.push(color.r);
                output.push(color.g);
                output.push(color.b);
                output.push(color.a);
            }
        }
    }
    Ok(())
}
pub(crate) fn read(
    mut start_idx: usize,
    bytes: &[u8],
) -> Result<(usize, FilePalette, Option<Vec<IciColor>>), IndexedImageError> {
    if bytes.len() <= start_idx {
        return Err(InvalidFileFormat(
            start_idx,
            "No data after header, expected palette format".to_string(),
        ));
    }
    let pal_type = bytes[start_idx];
    start_idx += 1;
    match pal_type {
        PAL_NO_DATA => Ok((1, NoData, None)),
        PAL_ID => {
            if bytes.len() < start_idx + 1 {
                return Err(InvalidFileFormat(
                    start_idx,
                    "No data after palette format, expected ID".to_string(),
                ));
            }
            let bytes = &bytes[start_idx..=start_idx + 1];
            let id = u16::from_be_bytes([bytes[0], bytes[1]]);
            Ok((3, ID(id), None))
        }
        PAL_NAME => {
            if bytes.len() < start_idx {
                return Err(InvalidFileFormat(
                    start_idx,
                    "No data after palette format, expected palette name length".to_string(),
                ));
            }
            let len = bytes[start_idx];
            start_idx += 1;
            let end = len as usize;
            if bytes.len() < start_idx + end {
                return Err(InvalidFileFormat(
                    start_idx,
                    "Incomplete data after palette name length, expected palette name".to_string(),
                ));
            }
            let name = String::from_utf8(bytes[start_idx..start_idx + end].to_vec())
                .map_err(PaletteNameNotUtf8)?;
            Ok((end + 2, Name(name), None))
        }
        PAL_COLORS => {
            if bytes.len() < start_idx {
                return Err(InvalidFileFormat(
                    start_idx,
                    "No data after palette format, expected color count".to_string(),
                ));
            }
            let count = bytes[start_idx];
            start_idx += 1;
            let end = count as usize * 4;
            if bytes.len() < start_idx + end {
                return Err(InvalidFileFormat(
                    start_idx,
                    format!("Incomplete data after palette color count, expected {count} colors"),
                ));
            }
            let mut colors = vec![];
            let color_bytes: Vec<&u8> = bytes.iter().skip(start_idx).take(end).collect();
            for color in color_bytes.chunks_exact(4) {
                colors.push(IciColor::new(*color[0], *color[1], *color[2], *color[3]));
            }
            Ok((end + 2, Colors, Some(colors)))
        }
        _ => Err(InvalidFileFormat(
            start_idx,
            format!("Unsupport palette type {pal_type}"),
        )),
    }
}
#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn write_no_data() {
        let mut output = vec![];
        write(&NoData, &[], &mut output).unwrap();
        assert_eq!(output, vec![PAL_NO_DATA]);
        let mut output = vec![];
        write(&NoData, &[IciColor::new(255, 45, 231, 2)], &mut output).unwrap();
        assert_eq!(output, vec![PAL_NO_DATA]);
    }
    #[test]
    fn write_id() {
        let mut output = vec![];
        write(&ID(5), &[], &mut output).unwrap();
        assert_eq!(output, vec![PAL_ID, 0, 5]);
        let mut output = vec![];
        write(&ID(256), &[IciColor::new(255, 45, 231, 2)], &mut output).unwrap();
        assert_eq!(output, vec![PAL_ID, 1, 0]);
    }
    #[test]
    fn write_name() {
        let mut output = vec![];
        write(&Name("test".to_string()), &[], &mut output).unwrap();
        assert_eq!(output, vec![PAL_NAME, 4, b't', b'e', b's', b't']);
        let mut output = vec![];
        write(
            &Name("😺".to_string()),
            &[IciColor::new(255, 45, 231, 2)],
            &mut output,
        )
        .unwrap();
        assert_eq!(output, vec![PAL_NAME, 4, 240, 159, 152, 186]);
    }
    #[test]
    fn write_colors() {
        let mut output = vec![];
        write(&Colors, &[IciColor::new(100, 101, 102, 103)], &mut output).unwrap();
        assert_eq!(output, vec![PAL_COLORS, 1, 100, 101, 102, 103]);
        let mut output = vec![];
        write(
            &Colors,
            &[
                IciColor::new(100, 101, 102, 103),
                IciColor::new(0, 0, 0, 255),
            ],
            &mut output,
        )
        .unwrap();
        assert_eq!(
            output,
            vec![PAL_COLORS, 2, 100, 101, 102, 103, 0, 0, 0, 255]
        );
    }
    #[test]
    fn read_no_data() {
        let (skip, pal_type, colors) = read(0, &[PAL_NO_DATA]).unwrap();
        assert_eq!(skip, 1);
        assert_eq!(pal_type, NoData);
        assert_eq!(colors, None);
    }
    #[test]
    fn read_id() {
        let (skip, pal_type, colors) = read(0, &[PAL_ID, 0, 5]).unwrap();
        assert_eq!(skip, 3);
        assert_eq!(pal_type, ID(5));
        assert_eq!(colors, None);
    }
    #[test]
    fn read_name() {
        let (skip, pal_type, colors) = read(0, &[PAL_NAME, 4, 240, 159, 152, 186]).unwrap();
        assert_eq!(skip, 6);
        assert_eq!(pal_type, Name("😺".to_string()));
        assert_eq!(colors, None);
    }
    #[test]
    fn read_colors() {
        let (skip, pal_type, colors) =
            read(0, &[PAL_COLORS, 2, 100, 101, 102, 103, 0, 0, 0, 255]).unwrap();
        assert_eq!(skip, 10);
        assert_eq!(pal_type, Colors);
        assert_eq!(
            colors,
            Some(vec![
                IciColor::new(100, 101, 102, 103),
                IciColor::new(0, 0, 0, 255)
            ])
        );
    }
    #[test]
    fn write_data_before() {
        let mut output = vec![1, 1, 1, 1];
        write(&ID(5), &[], &mut output).unwrap();
        assert_eq!(output, vec![1, 1, 1, 1, PAL_ID, 0, 5]);
    }
    #[test]
    fn read_data_either_side() {
        let bytes = [
            1, 1, 1, 1, PAL_COLORS, 2, 100, 101, 102, 103, 0, 0, 0, 255, 2, 2, 2, 2,
        ];
        let start = 4;
        let (skip, pal_type, colors) = read(start, &bytes).unwrap();
        assert_eq!(skip, 10);
        assert_eq!(pal_type, Colors);
        assert_eq!(
            colors,
            Some(vec![
                IciColor::new(100, 101, 102, 103),
                IciColor::new(0, 0, 0, 255)
            ])
        );
        assert_eq!(bytes[start + skip..], [2, 2, 2, 2]);
    }
}