use std::io;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum SampleEncoding {
    
    Binary,
    
    Ascii,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum PNMSubtype {
    
    Bitmap(SampleEncoding),
    
    Graymap(SampleEncoding),
    
    Pixmap(SampleEncoding),
    
    ArbitraryMap,
}
pub struct PNMHeader {
    pub(crate) decoded: HeaderRecord,
    pub(crate) encoded: Option<Vec<u8>>,
}
pub(crate) enum HeaderRecord {
    Bitmap(BitmapHeader),
    Graymap(GraymapHeader),
    Pixmap(PixmapHeader),
    Arbitrary(ArbitraryHeader),
}
#[derive(Clone, Copy, Debug)]
pub struct BitmapHeader {
    
    pub encoding: SampleEncoding,
    
    pub height: u32,
    
    pub width: u32,
}
#[derive(Clone, Copy, Debug)]
pub struct GraymapHeader {
    
    pub encoding: SampleEncoding,
    
    pub height: u32,
    
    pub width: u32,
    
    pub maxwhite: u32,
}
#[derive(Clone, Copy, Debug)]
pub struct PixmapHeader {
    
    pub encoding: SampleEncoding,
    
    pub height: u32,
    
    pub width: u32,
    
    pub maxval: u32,
}
#[derive(Clone, Debug)]
pub struct ArbitraryHeader {
    
    pub height: u32,
    
    pub width: u32,
    
    pub depth: u32,
    
    pub maxval: u32,
    
    pub tupltype: Option<ArbitraryTuplType>,
}
#[derive(Clone, Debug)]
pub enum ArbitraryTuplType {
    
    BlackAndWhite,
    
    BlackAndWhiteAlpha,
    
    Grayscale,
    
    GrayscaleAlpha,
    
    RGB,
    
    RGBAlpha,
    
    Custom(String),
}
impl PNMSubtype {
    
    pub fn magic_constant(self) -> &'static [u8; 2] {
        match self {
            PNMSubtype::Bitmap(SampleEncoding::Ascii) => b"P1",
            PNMSubtype::Graymap(SampleEncoding::Ascii) => b"P2",
            PNMSubtype::Pixmap(SampleEncoding::Ascii) => b"P3",
            PNMSubtype::Bitmap(SampleEncoding::Binary) => b"P4",
            PNMSubtype::Graymap(SampleEncoding::Binary) => b"P5",
            PNMSubtype::Pixmap(SampleEncoding::Binary) => b"P6",
            PNMSubtype::ArbitraryMap => b"P7",
        }
    }
    
    pub fn sample_encoding(self) -> SampleEncoding {
        match self {
            PNMSubtype::ArbitraryMap => SampleEncoding::Binary,
            PNMSubtype::Bitmap(enc) => enc,
            PNMSubtype::Graymap(enc) => enc,
            PNMSubtype::Pixmap(enc) => enc,
        }
    }
}
impl PNMHeader {
    
    pub fn subtype(&self) -> PNMSubtype {
        match self.decoded {
            HeaderRecord::Bitmap(BitmapHeader { encoding, .. }) => PNMSubtype::Bitmap(encoding),
            HeaderRecord::Graymap(GraymapHeader { encoding, .. }) => PNMSubtype::Graymap(encoding),
            HeaderRecord::Pixmap(PixmapHeader { encoding, .. }) => PNMSubtype::Pixmap(encoding),
            HeaderRecord::Arbitrary(ArbitraryHeader { .. }) => PNMSubtype::ArbitraryMap,
        }
    }
    
    pub fn width(&self) -> u32 {
        match self.decoded {
            HeaderRecord::Bitmap(BitmapHeader { width, .. }) => width,
            HeaderRecord::Graymap(GraymapHeader { width, .. }) => width,
            HeaderRecord::Pixmap(PixmapHeader { width, .. }) => width,
            HeaderRecord::Arbitrary(ArbitraryHeader { width, .. }) => width,
        }
    }
    
    pub fn height(&self) -> u32 {
        match self.decoded {
            HeaderRecord::Bitmap(BitmapHeader { height, .. }) => height,
            HeaderRecord::Graymap(GraymapHeader { height, .. }) => height,
            HeaderRecord::Pixmap(PixmapHeader { height, .. }) => height,
            HeaderRecord::Arbitrary(ArbitraryHeader { height, .. }) => height,
        }
    }
    
    pub fn maximal_sample(&self) -> u32 {
        match self.decoded {
            HeaderRecord::Bitmap(BitmapHeader { .. }) => 1,
            HeaderRecord::Graymap(GraymapHeader { maxwhite, .. }) => maxwhite,
            HeaderRecord::Pixmap(PixmapHeader { maxval, .. }) => maxval,
            HeaderRecord::Arbitrary(ArbitraryHeader { maxval, .. }) => maxval,
        }
    }
    
    pub fn as_bitmap(&self) -> Option<&BitmapHeader> {
        match self.decoded {
            HeaderRecord::Bitmap(ref bitmap) => Some(bitmap),
            _ => None,
        }
    }
    
    pub fn as_graymap(&self) -> Option<&GraymapHeader> {
        match self.decoded {
            HeaderRecord::Graymap(ref graymap) => Some(graymap),
            _ => None,
        }
    }
    
    pub fn as_pixmap(&self) -> Option<&PixmapHeader> {
        match self.decoded {
            HeaderRecord::Pixmap(ref pixmap) => Some(pixmap),
            _ => None,
        }
    }
    
    pub fn as_arbitrary(&self) -> Option<&ArbitraryHeader> {
        match self.decoded {
            HeaderRecord::Arbitrary(ref arbitrary) => Some(arbitrary),
            _ => None,
        }
    }
    
    pub fn write(&self, writer: &mut io::Write) -> io::Result<()> {
        writer.write_all(self.subtype().magic_constant())?;
        match *self {
            PNMHeader {
                encoded: Some(ref content),
                ..
            } => writer.write_all(content),
            PNMHeader {
                decoded:
                    HeaderRecord::Bitmap(BitmapHeader {
                        encoding: _encoding,
                        width,
                        height,
                    }),
                ..
            } => writeln!(writer, "\n{} {}", width, height),
            PNMHeader {
                decoded:
                    HeaderRecord::Graymap(GraymapHeader {
                        encoding: _encoding,
                        width,
                        height,
                        maxwhite,
                    }),
                ..
            } => writeln!(writer, "\n{} {} {}", width, height, maxwhite),
            PNMHeader {
                decoded:
                    HeaderRecord::Pixmap(PixmapHeader {
                        encoding: _encoding,
                        width,
                        height,
                        maxval,
                    }),
                ..
            } => writeln!(writer, "\n{} {} {}", width, height, maxval),
            PNMHeader {
                decoded:
                    HeaderRecord::Arbitrary(ArbitraryHeader {
                        width,
                        height,
                        depth,
                        maxval,
                        ref tupltype,
                    }),
                ..
            } => {
                #[allow(unused_assignments)]
                
                
                
                
                
                let mut custom_fallback = String::new();
                let tupltype = match *tupltype {
                    None => "",
                    Some(ArbitraryTuplType::BlackAndWhite) => "TUPLTYPE BLACKANDWHITE\n",
                    Some(ArbitraryTuplType::BlackAndWhiteAlpha) => "TUPLTYPE BLACKANDWHITE_ALPHA\n",
                    Some(ArbitraryTuplType::Grayscale) => "TUPLTYPE GRAYSCALE\n",
                    Some(ArbitraryTuplType::GrayscaleAlpha) => "TUPLTYPE GRAYSCALE_ALPHA\n",
                    Some(ArbitraryTuplType::RGB) => "TUPLTYPE RGB\n",
                    Some(ArbitraryTuplType::RGBAlpha) => "TUPLTYPE RGB_ALPHA\n",
                    Some(ArbitraryTuplType::Custom(ref custom)) => {
                        custom_fallback = format!("TUPLTYPE {}", custom);
                        &custom_fallback
                    }
                };
                writeln!(
                    writer,
                    "\nWIDTH {}\nHEIGHT {}\nDEPTH {}\nMAXVAL {}\n{}ENDHDR",
                    width, height, depth, maxval, tupltype
                )
            }
        }
    }
}
impl From<BitmapHeader> for PNMHeader {
    fn from(header: BitmapHeader) -> Self {
        PNMHeader {
            decoded: HeaderRecord::Bitmap(header),
            encoded: None,
        }
    }
}
impl From<GraymapHeader> for PNMHeader {
    fn from(header: GraymapHeader) -> Self {
        PNMHeader {
            decoded: HeaderRecord::Graymap(header),
            encoded: None,
        }
    }
}
impl From<PixmapHeader> for PNMHeader {
    fn from(header: PixmapHeader) -> Self {
        PNMHeader {
            decoded: HeaderRecord::Pixmap(header),
            encoded: None,
        }
    }
}
impl From<ArbitraryHeader> for PNMHeader {
    fn from(header: ArbitraryHeader) -> Self {
        PNMHeader {
            decoded: HeaderRecord::Arbitrary(header),
            encoded: None,
        }
    }
}