use std;
use std::io::{Read, Error, ErrorKind};

pub trait Reader {
    fn read_u8(&mut self) -> std::io::Result<Option<u8>>;
    fn read_u16(&mut self) -> std::io::Result<Option<u16>>;
    fn read_u32(&mut self) -> std::io::Result<Option<u32>>;
    fn read_u64(&mut self) -> std::io::Result<Option<u64>>;
    fn read_bytes(&mut self, buf: &mut [u8]) -> std::io::Result<usize>;
    fn read_string(&mut self) -> std::io::Result<Option<String>>;
}

pub struct BasicReader<'a> {
    read: &'a mut Read
}

impl<'a> BasicReader<'a> {
    pub fn new(read: &'a mut Read) -> BasicReader {
        BasicReader {
            read: read
        }
    }
}

impl<'a> Reader for BasicReader<'a> {
    fn read_u8(&mut self) -> std::io::Result<Option<u8>> {
        let mut buf = [0];
        match self.read.read(&mut buf[..]) {
            Ok(1) => Ok(Some(buf[0])),
            Ok(_) => Ok(None),
            Err(err) => {
                return Err(err)
            }
        }
    }

    fn read_u16(&mut self) -> std::io::Result<Option<u16>> {
        let mut buf = [0; 2];
        match self.read.read(&mut buf[..]) {
            Ok(2) => {
                let a = (buf[0] as u16) << 8;
                let b = buf[1] as u16;
                Ok(Some(a | b))
            },
            Ok(_) => Ok(None),
            Err(err) => {
                Err(err)
            }
        }
    }

    fn read_u32(&mut self) -> std::io::Result<Option<u32>> {
        let mut buf = [0; 4];
        match self.read.read(&mut buf[..]) {
            Ok(4) => {
                let a = (buf[0] as u32) << 24;
                let b = (buf[1] as u32) << 16;
                let c = (buf[2] as u32) << 8;
                let d = buf[3] as u32;
                Ok(Some(a | b | c | d))
            },
            Ok(_) => Ok(None),
            Err(err) => {
                Err(err)
            }
        }
    }

    fn read_u64(&mut self) -> std::io::Result<Option<u64>> {
        let mut buf = [0; 8];
        match self.read.read(&mut buf[..]) {
            Ok(8) => {
                let a = (buf[0] as u64) << 56;
                let b = (buf[1] as u64) << 48;
                let c = (buf[2] as u64) << 40;
                let d = (buf[3] as u64) << 32;
                let e = (buf[4] as u64) << 24;
                let f = (buf[5] as u64) << 16;
                let g = (buf[6] as u64) << 8;
                let h = buf[7] as u64;
                Ok(Some(a | b | c | d | e | f | g | h))
            },
            Ok(_) => Ok(None),
            Err(err) => {
                Err(err)
            }
        }
    }

    fn read_bytes(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        self.read.read(buf)
    }

    fn read_string(&mut self) -> std::io::Result<Option<String>> {
        let len = match try!(self.read_u16()) {
            Some(len) => len as usize,
            None => {
                return Ok(None)
            }
        };
        let mut buf = vec![0 as u8; len];
        let n = try!(self.read_bytes(&mut buf[..]));
        if n != len {
            return Ok(None)
        }
        let s = match String::from_utf8(buf) {
            Ok(s) => s,
            Err(err) => {
                return Err(Error::new(ErrorKind::Other, format!("encoding error: {}", err)))
            }
        };
        return Ok(Some(s))
    }
}

