//! Decoding of netpbm image formats (pbm, pgm, ppm and pam).
//!
//! The formats pbm, pgm and ppm are fully supported. The pam decoder recognizes the tuple types
//! `BLACKANDWHITE`, `GRAYSCALE` and `RGB` and explicitly recognizes but rejects their `_ALPHA`
//! variants for now as alpha color types are unsupported.
use self::autobreak::AutoBreak;
pub use self::decoder::PnmDecoder;
pub use self::encoder::PnmEncoder;
use self::header::HeaderRecord;
pub use self::header::{
    ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader, PixmapHeader,
};
pub use self::header::{PnmHeader, PnmSubtype, SampleEncoding};

mod autobreak;
mod decoder;
mod encoder;
mod header;

#[cfg(test)]
mod tests {
    use super::*;
    use crate::color::ColorType;
    use crate::image::ImageDecoder;
    use byteorder::{ByteOrder, NativeEndian};

    fn execute_roundtrip_default(buffer: &[u8], width: u32, height: u32, color: ColorType) {
        let mut encoded_buffer = Vec::new();

        {
            let mut encoder = PnmEncoder::new(&mut encoded_buffer);
            encoder
                .encode(buffer, width, height, color)
                .expect("Failed to encode the image buffer");
        }

        let (header, loaded_color, loaded_image) = {
            let decoder = PnmDecoder::new(&encoded_buffer[..]).unwrap();
            let color_type = decoder.color_type();
            let mut image = vec![0; decoder.total_bytes() as usize];
            decoder
                .read_image(&mut image)
                .expect("Failed to decode the image");
            let (_, header) = PnmDecoder::new(&encoded_buffer[..]).unwrap().into_inner();
            (header, color_type, image)
        };

        assert_eq!(header.width(), width);
        assert_eq!(header.height(), height);
        assert_eq!(loaded_color, color);
        assert_eq!(loaded_image.as_slice(), buffer);
    }

    fn execute_roundtrip_with_subtype(
        buffer: &[u8],
        width: u32,
        height: u32,
        color: ColorType,
        subtype: PnmSubtype,
    ) {
        let mut encoded_buffer = Vec::new();

        {
            let mut encoder = PnmEncoder::new(&mut encoded_buffer).with_subtype(subtype);
            encoder
                .encode(buffer, width, height, color)
                .expect("Failed to encode the image buffer");
        }

        let (header, loaded_color, loaded_image) = {
            let decoder = PnmDecoder::new(&encoded_buffer[..]).unwrap();
            let color_type = decoder.color_type();
            let mut image = vec![0; decoder.total_bytes() as usize];
            decoder
                .read_image(&mut image)
                .expect("Failed to decode the image");
            let (_, header) = PnmDecoder::new(&encoded_buffer[..]).unwrap().into_inner();
            (header, color_type, image)
        };

        assert_eq!(header.width(), width);
        assert_eq!(header.height(), height);
        assert_eq!(header.subtype(), subtype);
        assert_eq!(loaded_color, color);
        assert_eq!(loaded_image.as_slice(), buffer);
    }

    fn execute_roundtrip_u16(buffer: &[u16], width: u32, height: u32, color: ColorType) {
        let mut encoded_buffer = Vec::new();

        {
            let mut encoder = PnmEncoder::new(&mut encoded_buffer);
            encoder
                .encode(buffer, width, height, color)
                .expect("Failed to encode the image buffer");
        }

        let (header, loaded_color, loaded_image) = {
            let decoder = PnmDecoder::new(&encoded_buffer[..]).unwrap();
            let color_type = decoder.color_type();
            let mut image = vec![0; decoder.total_bytes() as usize];
            decoder
                .read_image(&mut image)
                .expect("Failed to decode the image");
            let (_, header) = PnmDecoder::new(&encoded_buffer[..]).unwrap().into_inner();
            (header, color_type, image)
        };

        let mut buffer_u8 = vec![0; buffer.len() * 2];
        NativeEndian::write_u16_into(buffer, &mut buffer_u8[..]);

        assert_eq!(header.width(), width);
        assert_eq!(header.height(), height);
        assert_eq!(loaded_color, color);
        assert_eq!(loaded_image, buffer_u8);
    }

    #[test]
    fn roundtrip_gray() {
        #[rustfmt::skip]
        let buf: [u8; 16] = [
            0, 0, 0, 255,
            255, 255, 255, 255,
            255, 0, 255, 0,
            255, 0, 0, 0,
        ];

        execute_roundtrip_default(&buf, 4, 4, ColorType::L8);
        execute_roundtrip_with_subtype(&buf, 4, 4, ColorType::L8, PnmSubtype::ArbitraryMap);
        execute_roundtrip_with_subtype(
            &buf,
            4,
            4,
            ColorType::L8,
            PnmSubtype::Graymap(SampleEncoding::Ascii),
        );
        execute_roundtrip_with_subtype(
            &buf,
            4,
            4,
            ColorType::L8,
            PnmSubtype::Graymap(SampleEncoding::Binary),
        );
    }

    #[test]
    fn roundtrip_rgb() {
        #[rustfmt::skip]
        let buf: [u8; 27] = [
              0,   0,   0,
              0,   0, 255,
              0, 255,   0,
              0, 255, 255,
            255,   0,   0,
            255,   0, 255,
            255, 255,   0,
            255, 255, 255,
            255, 255, 255,
        ];
        execute_roundtrip_default(&buf, 3, 3, ColorType::Rgb8);
        execute_roundtrip_with_subtype(&buf, 3, 3, ColorType::Rgb8, PnmSubtype::ArbitraryMap);
        execute_roundtrip_with_subtype(
            &buf,
            3,
            3,
            ColorType::Rgb8,
            PnmSubtype::Pixmap(SampleEncoding::Binary),
        );
        execute_roundtrip_with_subtype(
            &buf,
            3,
            3,
            ColorType::Rgb8,
            PnmSubtype::Pixmap(SampleEncoding::Ascii),
        );
    }

    #[test]
    fn roundtrip_u16() {
        let buf: [u16; 6] = [0, 1, 0xFFFF, 0x1234, 0x3412, 0xBEAF];

        execute_roundtrip_u16(&buf, 6, 1, ColorType::L16);
    }
}