//! Common types shared between the encoder and decoder
use std::mem;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ColorType {
    Grayscale = 0,
    RGB = 2,
    Indexed = 3,
    GrayscaleAlpha = 4,
    RGBA = 6
}

impl ColorType {
    /// Returns the number of samples used per pixel of `ColorType`
    pub fn samples(&self) -> usize {
        use self::ColorType::*;
        match *self {
            Grayscale => 1,
            RGB => 3,
            Indexed => 1,
            GrayscaleAlpha => 2,
            RGBA => 4
        }
    }
    
    /// u8 -> Self. Temporary solution until Rust provides a canonical one.
    pub fn from_u8(n: u8) -> Option<ColorType> {
        match n {
            0 | 2 | 3 | 4 | 6 => Some(unsafe { mem::transmute(n) }),
            _ => None
        }
    }
}

/// Bit depth of the png file
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum BitDepth {
    One     = 1,
    Two     = 2,
    Four    = 4,
    Eight   = 8,
    Sixteen = 16,
}

impl BitDepth {
    /// u8 -> Self. Temporary solution until Rust provides a canonical one.
    pub fn from_u8(n: u8) -> Option<BitDepth> {
        match n {
            1 | 2 | 4 | 8 | 16 => Some(unsafe { mem::transmute(n) }),
            _ => None
        }
    }
}

/// Pixel dimensions information
#[derive(Clone, Copy, Debug)]
pub struct PixelDimensions {
    /// Pixels per unit, X axis
    pub xppu: u32,
    /// Pixels per unit, Y axis
    pub yppu: u32,
    /// Either *Meter* or *Unspecified*
    pub unit: Unit,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
/// Physical unit of the pixel dimensions
pub enum Unit {
    Unspecified = 0,
    Meter = 1,
}

impl Unit {
    /// u8 -> Self. Temporary solution until Rust provides a canonical one.
    pub fn from_u8(n: u8) -> Option<Unit> {
        match n {
            0 | 1 => Some(unsafe { mem::transmute(n) }),
            _ => None
        }
    }
}

/// Frame control information
#[derive(Debug)]
pub struct FrameControl {
    /// Sequence number of the animation chunk, starting from 0
    pub sequence_number: u32,
    /// Width of the following frame
    pub width: u32,
    /// Height of the following frame
    pub height: u32,
    /// X position at which to render the following frame
    pub x_offset: u32,
    /// Y position at which to render the following frame
    pub y_offset: u32,
    /// Frame delay fraction numerator
    pub delay_num: u16,
    /// Frame delay fraction denominator
    pub delay_den: u16,
    /// Type of frame area disposal to be done after rendering this frame
    pub dispose_op: u8,
    /// Type of frame area rendering for this frame
    pub blend_op: u8,
}

/// Animation control information
#[derive(Clone, Copy, Debug)]
pub struct AnimationControl {
    /// Number of frames
    pub num_frames: u32,
    /// Number of times to loop this APNG.  0 indicates infinite looping.
    pub num_plays: u32,
}

/// PNG info struct
#[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>
}

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
        }
    }
}

impl Info {
    /// Size of the image
    pub fn size(&self) -> (u32, u32) {
        (self.width, self.height)
    }
    
    /// Returns true if the image is an APNG image.
    pub fn is_animated(&self) -> bool {
        self.frame_control.is_some() && self.animation_control.is_some()
    }
    
    /// Returns the frame control information of the image
    pub fn animation_control(&self) -> Option<&AnimationControl> {
        self.animation_control.as_ref()
    }
    
    /// Returns the frame control information of the current frame
    pub fn frame_control(&self) -> Option<&FrameControl> {
        self.frame_control.as_ref()
    }
    
    /// Returns the bits per pixel
    pub fn bits_per_pixel(&self) -> usize {
        self.color_type.samples() * self.bit_depth as usize
    }
    
    /// Returns the bytes per pixel
    pub fn bytes_per_pixel(&self) -> usize {
        self.color_type.samples() * ((self.bit_depth as usize + 7) >> 3)
    }
    
    /// Returns the number of bytes needed for one deinterlaced image
    pub fn raw_bytes(&self) -> usize {
        self.height as usize * self.raw_row_length()
    }
    
    /// Returns the number of bytes needed for one deinterlaced row 
    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 // filter method
    }
    
    /// Returns the number of bytes needed for one deinterlaced row of width `width`
    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 // filter method
    }
}


bitflags! {
    /// # Output transformations
    ///
    /// Only `IDENTITY` and `TRANSFORM_EXPAND | TRANSFORM_STRIP_ALPHA` can be used at the moment.
    pub struct Transformations: u32 {
        /// No transformation
        const IDENTITY            = 0x0000; // read and write */
        /// Strip 16-bit samples to 8 bits
        const STRIP_16            = 0x0001; // read only */
        /// Discard the alpha channel
        const STRIP_ALPHA         = 0x0002; // read only */
        /// Expand 1; 2 and 4-bit samples to bytes
        const PACKING             = 0x0004; // read and write */
        /// Change order of packed pixels to LSB first
        const PACKSWAP            = 0x0008; // read and write */
        /// Expand paletted images to RGB; expand grayscale images of
        /// less than 8-bit depth to 8-bit depth; and expand tRNS chunks
        /// to alpha channels.
        const EXPAND              = 0x0010; // read only */
        /// Invert monochrome images
        const INVERT_MONO         = 0x0020; // read and write */
        /// Normalize pixels to the sBIT depth
        const SHIFT               = 0x0040; // read and write */
        /// Flip RGB to BGR; RGBA to BGRA
        const BGR                 = 0x0080; // read and write */
        /// Flip RGBA to ARGB or GA to AG
        const SWAP_ALPHA          = 0x0100; // read and write */
        /// Byte-swap 16-bit samples
        const SWAP_ENDIAN         = 0x0200; // read and write */
        /// Change alpha from opacity to transparency
        const INVERT_ALPHA        = 0x0400; // read and write */
        const STRIP_FILLER        = 0x0800; // write only */
        const STRIP_FILLER_BEFORE = 0x0800; // write only
        const STRIP_FILLER_AFTER  = 0x1000; // write only */
        const GRAY_TO_RGB         = 0x2000; // read only */
        const EXPAND_16           = 0x4000; // read only */
        const SCALE_16            = 0x8000; // read only */
    }
}


    
