#![allow(dead_code)]
use gl;
use std::mem;

use board;

/// A trait defined on each type used to create a VBO
pub trait Vertex {
    fn size() -> i32;
    fn pos_location() -> (i32, i32);
    fn uv_location() -> Option<(i32, i32)>;
    fn normal_location() -> Option<(i32, i32)>;
}

#[derive(Debug, PartialEq)]
pub struct V2D {
    pub x: gl::GLfloat,
    pub y: gl::GLfloat,
    pub u: gl::GLfloat,
    pub v: gl::GLfloat,
}

impl V2D {
    fn new(
        x: gl::GLfloat,
        y: gl::GLfloat,
        u: gl::GLfloat,
        v: gl::GLfloat,
    ) -> V2D {
        V2D { x, y, u, v }
    }
}

impl Vertex for V2D {
    fn size() -> i32 {
        mem::size_of::<V2D>() as i32
    }

    fn pos_location() -> (i32, i32) {
        (0, 2)
    }

    fn uv_location() -> Option<(i32, i32)> {
        Some((2, 2))
    }

    fn normal_location() -> Option<(i32, i32)> {
        None
    }
}


pub struct V3D {
    pub x: gl::GLfloat,
    pub y: gl::GLfloat,
    pub z: gl::GLfloat,
    pub u: gl::GLfloat,
    pub v: gl::GLfloat,
}

impl V3D {
    fn new(
        x: gl::GLfloat,
        y: gl::GLfloat,
        z: gl::GLfloat,
        u: gl::GLfloat,
        v: gl::GLfloat,
    ) -> V3D {
        V3D { x, y, z, u, v }
    }
}

impl Vertex for V3D {
    fn size() -> i32 {
        mem::size_of::<V3D>() as i32
    }

    fn pos_location() -> (i32, i32) {
        (0, 3)
    }

    fn uv_location() -> Option<(i32, i32)> {
        Some((3, 2))
    }

    fn normal_location() -> Option<(i32, i32)> {
        None
    }
}

pub fn make_grid(w: usize, h: usize) -> Vec<V2D> {
    let mut vec = Vec::with_capacity(w * h * 4);
    let x_stride = 2.0 / w as f32;
    let y_stride = 2.0 / h as f32;
    for x in 0..w {
        for y in 0..h {
            let x = x as f32;
            let y = y as f32;
            let o = 1.0 / 32.0;
            vec.extend(vec![
                V2D { x: x_stride * (x + 1.0) - 1.0, y: y_stride *  y        - 1.0, u: o  , v: o   },
                V2D { x: x_stride *  x        - 1.0, y: y_stride *  y        - 1.0, u: 0.0, v: o   },
                V2D { x: x_stride * (x + 1.0) - 1.0, y: y_stride * (y + 1.0) - 1.0, u: o,   v: 0.0 },
                V2D { x: x_stride *  x        - 1.0, y: y_stride * (y + 1.0) - 1.0, u: 0.0, v: 0.0 },
            ]);
        }
    }
    vec
}


pub fn make_grid_with(w: usize, h: usize, b: &board::Board) -> Vec<V2D> {
    let mut vec = Vec::with_capacity(w * h * 4);
    let x_stride = 2.0 / w as f32;
    let y_stride = 2.0 / h as f32;
    let o_stride = 1.0 / 32.0;
    b.each_tile(|x, y, t| {
        let x = x as f32;
        let y = h as f32 - y as f32 - 1.0;
        let tx = (t.sprite.x as f32) * o_stride;
        let ty = (t.sprite.y as f32) * o_stride;
        vec.extend(mk_quad((x, y), (x_stride, y_stride), (tx, ty), o_stride));
    });
    vec
}


pub fn make_upper_grid_with(w: usize, h: usize, b: &board::Board) -> Vec<V2D> {
    let mut vec = Vec::with_capacity(w * h * 4);
    let x_stride = 2.0 / w as f32;
    let y_stride = 2.0 / h as f32;
    let o_stride = 1.0 / 32.0;
    b.each_esse(|x, y, t| {
        let x = x as f32;
        let y = h as f32 - y as f32 - 1.0;
        let tx = (t.sprite.x as f32) * o_stride;
        let ty = (t.sprite.y as f32) * o_stride;
        vec.extend(mk_quad((x, y), (x_stride, y_stride), (tx, ty), o_stride));
    });
    vec
}


fn mk_quad(
    (x, y): (f32, f32),
    (x_stride, y_stride): (f32, f32),
    (tx, ty): (f32, f32),
    o_stride: f32,
) -> Vec<V2D> {
    vec![
        V2D {
            x: x_stride * (x + 1.0) - 1.0,
            y: y_stride *  y        - 1.0,
            u: tx + o_stride,
            v: ty + o_stride,
        },
        V2D {
            x: x_stride *  x        - 1.0,
            y: y_stride *  y        - 1.0,
            u: tx,
            v: ty + o_stride,
        },
        V2D {
            x: x_stride * (x + 1.0) - 1.0,
            y: y_stride * (y + 1.0) - 1.0,
            u: tx + o_stride,
            v: ty,
        },

        V2D {
            x: x_stride *  x        - 1.0,
            y: y_stride *  y        - 1.0,
            u: tx,
            v: ty + o_stride,
        },
        V2D {
            x: x_stride * (x + 1.0) - 1.0,
            y: y_stride * (y + 1.0) - 1.0,
            u: tx + o_stride,
            v: ty,
        },
        V2D {
            x: x_stride *  x        - 1.0,
            y: y_stride * (y + 1.0) - 1.0,
            u: tx,
            v: ty,
        },
    ]
}
