extern crate glium;
extern crate image;

use glium::{VertexBuffer, IndexBuffer};
use glium::texture::Texture2d;
use therm_util::reader::ByteReader;

#[derive(Debug, Copy, Clone)]
pub struct V3D {
    pub pos: [f32; 3],
    pub nrm: [f32; 3],
    pub tex: [f32; 2],
}

implement_vertex!(V3D, pos, nrm, tex);

impl V3D {
    pub fn from_reader<Rd>(reader: &mut ByteReader<Rd>) -> Result<V3D, String>
        where Rd: Iterator<Item=u8>
    {
        Ok(V3D { pos: [ try!(reader.read_twip()) * 0.1,
                        try!(reader.read_twip()) * 0.1,
                        try!(reader.read_twip()) * 0.1],
                 nrm: [ try!(reader.read_twip()),
                        try!(reader.read_twip()),
                        try!(reader.read_twip()) ],
                 tex: [ try!(reader.read_ratio()),
                        try!(reader.read_ratio()) ],
        })
    }
}

#[derive(Debug, Clone)]
pub struct Model {
    pub points:  Vec<V3D>,
    pub tris:    Vec<u16>,
    pub texture: Option<Vec<u8>>,
}

impl Model {
    pub fn load_from_file(path: &str) -> Result<Model, String> {
        let mut reader = ByteReader::from_file(path).unwrap();
        Model::from_reader(&mut reader)
    }

    pub fn from_reader<T>(reader: &mut ByteReader<T>) -> Result<Model, String>
        where T: Iterator<Item=u8>
    {
        Ok(Model {
            tris: try!(reader.read_several(|r| r.read_prefix_int().map(|x| x as u16))),
            points: try!(reader.read_several(V3D::from_reader)),
            texture: None,
        })
    }

    pub fn get_vbuf(&self, display: &glium::Display) -> VertexBuffer<V3D> {
        VertexBuffer::new(display, self.points.as_slice()).unwrap()
    }

    pub fn get_ibuf(&self, display: &glium::Display) -> IndexBuffer<u16> {
        IndexBuffer::new(
            display,
            glium::index::PrimitiveType::TrianglesList,
            &self.tris).unwrap()
    }

    pub fn get_tex(&self, display: &glium::Display) -> Option<Texture2d> {
        use std::io::Cursor;
        use glium::texture::RawImage2d;
        if let Some(ref tex) = self.texture {
            let img = image::load(Cursor::new(tex.clone()),
                                  image::PNG).unwrap().to_rgba();
            let dims = img.dimensions();
            let img = RawImage2d::from_raw_rgba_reversed(&img.into_raw(),
                                                         dims);
            Some(Texture2d::new(display, img).unwrap())
        } else {
            None
        }
    }
}
