use crate::filter;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ColorType {
    Grayscale = 0,
    RGB = 2,
    Indexed = 3,
    GrayscaleAlpha = 4,
    RGBA = 6
}
impl ColorType {
    
    pub fn samples(&self) -> usize {
        use self::ColorType::*;
        match *self {
            Grayscale | Indexed => 1,
            RGB => 3,
            GrayscaleAlpha => 2,
            RGBA => 4
        }
    }
    
    
    pub fn from_u8(n: u8) -> Option<ColorType> {
        match n {
            0 => Some(ColorType::Grayscale),
            2 => Some(ColorType::RGB),
            3 => Some(ColorType::Indexed),
            4 => Some(ColorType::GrayscaleAlpha),
            6 => Some(ColorType::RGBA),
            _ => None
        }
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum BitDepth {
    One     = 1,
    Two     = 2,
    Four    = 4,
    Eight   = 8,
    Sixteen = 16,
}
impl BitDepth {
    
    pub fn from_u8(n: u8) -> Option<BitDepth> {
        match n {
            1 => Some(BitDepth::One),
            2 => Some(BitDepth::Two),
            4 => Some(BitDepth::Four),
            8 => Some(BitDepth::Eight),
            16 => Some(BitDepth::Sixteen),
            _ => None
        }
    }
}
#[derive(Clone, Copy, Debug)]
pub struct PixelDimensions {
    
    pub xppu: u32,
    
    pub yppu: u32,
    
    pub unit: Unit,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Unit {
    Unspecified = 0,
    Meter = 1,
}
impl Unit {
    
    pub fn from_u8(n: u8) -> Option<Unit> {
        match n {
            0 => Some(Unit::Unspecified),
            1 => Some(Unit::Meter),
            _ => None
        }
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum DisposeOp {
    
    None       = 0,
    
    Background = 1,
    
    Previous   = 2,
}
impl DisposeOp {
    
    pub fn from_u8(n: u8) -> Option<DisposeOp> {
        match n {
            0 => Some(DisposeOp::None),
            1 => Some(DisposeOp::Background),
            2 => Some(DisposeOp::Previous),
            _ => None
        }
    }
}
impl fmt::Display for DisposeOp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = match *self {
            DisposeOp::None => "DISPOSE_OP_NONE",
            DisposeOp::Background => "DISPOSE_OP_BACKGROUND",
            DisposeOp::Previous => "DISPOSE_OP_PREVIOUS",
        };
        write!(f, "{}", name)
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum BlendOp {
    
    Source = 0,
    
    Over   = 1,
}
impl BlendOp {
    
    pub fn from_u8(n: u8) -> Option<BlendOp> {
        match n {
            0 => Some(BlendOp::Source),
            1 => Some(BlendOp::Over),
            _ => None
        }
    }
}
impl fmt::Display for BlendOp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = match *self {
            BlendOp::Source => "BLEND_OP_SOURCE",
            BlendOp::Over => "BLEND_OP_OVER",
        };
        write!(f, "{}", name)
    }
}
#[derive(Clone, Copy, Debug)]
pub struct FrameControl {
    
    pub sequence_number: u32,
    
    pub width: u32,
    
    pub height: u32,
    
    pub x_offset: u32,
    
    pub y_offset: u32,
    
    pub delay_num: u16,
    
    pub delay_den: u16,
    
    pub dispose_op: DisposeOp,
    
    pub blend_op: BlendOp,
}
impl Default for FrameControl {
    fn default() -> FrameControl {
        FrameControl {
            sequence_number: 0,
            width: 0,
            height: 0,
            x_offset: 0,
            y_offset: 0,
            delay_num: 1,
            delay_den: 30,
            dispose_op: DisposeOp::None,
            blend_op: BlendOp::Source,
        }
    }
}
impl FrameControl {
    pub fn set_seq_num(&mut self, s: u32) {
        self.sequence_number = s;
    }
    pub fn inc_seq_num(&mut self, i: u32) {
        self.sequence_number += i;
    }
}
#[derive(Clone, Copy, Debug)]
pub struct AnimationControl {
    
    pub num_frames: u32,
    
    pub num_plays: u32,
}
#[derive(Debug, Clone)]
pub enum Compression {
    
    Default,
    
    Fast,
    
    
    
    
    
    Best,
    Huffman,
    Rle,
}
#[derive(Debug)]
pub struct Info {
    pub width: u32,
    pub height: u32,
    pub bit_depth: BitDepth,
    pub color_type: ColorType,
    pub interlaced: bool,
    pub trns: Option<Vec<u8>>,
    pub pixel_dims: Option<PixelDimensions>,
    pub palette: Option<Vec<u8>>,
    pub frame_control: Option<FrameControl>,
    pub animation_control: Option<AnimationControl>,
    pub compression: Compression,
    pub filter: filter::FilterType,
}
impl Default for Info {
    fn default() -> Info {
        Info {
            width: 0,
            height: 0,
            bit_depth: BitDepth::Eight,
            color_type: ColorType::Grayscale,
            interlaced: false,
            palette: None,
            trns: None,
            pixel_dims: None,
            frame_control: None,
            animation_control: None,
            
            
            compression: Compression::Fast,
            filter: filter::FilterType::Sub,
        }
    }
}
impl Info {
    
    pub fn size(&self) -> (u32, u32) {
        (self.width, self.height)
    }
    
    
    pub fn is_animated(&self) -> bool {
        self.frame_control.is_some() && self.animation_control.is_some()
    }
    
    
    pub fn animation_control(&self) -> Option<&AnimationControl> {
        self.animation_control.as_ref()
    }
    
    
    pub fn frame_control(&self) -> Option<&FrameControl> {
        self.frame_control.as_ref()
    }
    
    
    pub fn bits_per_pixel(&self) -> usize {
        self.color_type.samples() * self.bit_depth as usize
    }
    
    
    pub fn bytes_per_pixel(&self) -> usize {
        self.color_type.samples() * ((self.bit_depth as usize + 7) >> 3)
    }
    
    
    pub fn raw_bytes(&self) -> usize {
        self.height as usize * self.raw_row_length()
    }
    
    
    pub fn raw_row_length(&self) -> usize {
        let bits = self.width as usize * self.color_type.samples() * self.bit_depth as usize;
        let extra = bits % 8;
        bits/8
        + match extra { 0 => 0, _ => 1 }
        + 1 
    }
    
    
    pub fn raw_row_length_from_width(&self, width: u32) -> usize {
        let bits = width as usize * self.color_type.samples() * self.bit_depth as usize;
        let extra = bits % 8;
        bits/8
        + match extra { 0 => 0, _ => 1 }
        + 1 
    }
}
bitflags! {
    
    
    
    pub struct Transformations: u32 {
        
        const IDENTITY            = 0x0000; 
        
        const STRIP_16            = 0x0001; 
        
        const STRIP_ALPHA         = 0x0002; 
        
        const PACKING             = 0x0004; 
        
        const PACKSWAP            = 0x0008; 
        
        
        
        const EXPAND              = 0x0010; 
        
        const INVERT_MONO         = 0x0020; 
        
        const SHIFT               = 0x0040; 
        
        const BGR                 = 0x0080; 
        
        const SWAP_ALPHA          = 0x0100; 
        
        const SWAP_ENDIAN         = 0x0200; 
        
        const INVERT_ALPHA        = 0x0400; 
        const STRIP_FILLER        = 0x0800; 
        const STRIP_FILLER_BEFORE = 0x0800; 
        const STRIP_FILLER_AFTER  = 0x1000; 
        const GRAY_TO_RGB         = 0x2000; 
        const EXPAND_16           = 0x4000; 
        const SCALE_16            = 0x8000; 
    }
}
#[cfg(feature = "png-encoding")]
mod deflate_convert {
    extern crate deflate;
    use super::Compression;
    impl From<deflate::Compression> for Compression {
        fn from(c: deflate::Compression) -> Self {
            match c {
                deflate::Compression::Default => Compression::Default,
                deflate::Compression::Fast => Compression::Fast,
                deflate::Compression::Best => Compression::Best,
            }
        }
    }
    impl From<Compression> for deflate::CompressionOptions {
        fn from(c: Compression) -> Self {
            match c {
                Compression::Default => deflate::CompressionOptions::default(),
                Compression::Fast => deflate::CompressionOptions::fast(),
                Compression::Best => deflate::CompressionOptions::high(),
                Compression::Huffman => deflate::CompressionOptions::huffman_only(),
                Compression::Rle => deflate::CompressionOptions::rle(),
            }
        }
    }
}