#[macro_use]
extern crate glium;
extern crate image;

use glium::{DisplayBuild,Display,IndexBuffer,Program,Surface,VertexBuffer};
use glium::glutin::{Event,WindowBuilder};
use glium::index::PrimitiveType;
use glium::texture::Texture2d;

struct BallState {
    pos: (f32, f32),
    dpos: (f32, f32),
}

#[derive(Debug,Clone,Copy)]
struct V2D {
    pos: [f32;2],
    tex: [f32;2],
}

implement_vertex!(V2D, pos, tex);

fn load_texture(d: &Display) -> Texture2d {
    use std::io::Cursor;
    let img = image::load(
        Cursor::new(&include_bytes!("../assets/texture.png")[..]),
        image::PNG).unwrap().to_rgba();
    let dims = img.dimensions();
    let img = glium::texture::RawImage2d::from_raw_rgba_reversed(
        img.into_raw(), dims);
    Texture2d::new(d, img).unwrap()
}

fn walls(d: &Display) -> (VertexBuffer<V2D>, IndexBuffer<u16>) {
    let mut vertices = vec![];
    let mut indices = vec![];
    let mut i = 0;
    for x in -9..10i32 {
        for y in -9..10i32 {
            if x.abs() == 9 || y.abs() == 9 {
                vertices.extend_from_slice(&square_at((x as f32, y as f32), (0.5, 0.0)));
                indices.extend_from_slice(
                    &[i + 2, i + 1, i, i + 3, i + 2, i]);
                i += 4;
            }
        }
    }
    let vbuf = VertexBuffer::new(d, vertices.as_slice()).unwrap();
    let ibuf = IndexBuffer::new(d, PrimitiveType::TrianglesList, indices.as_slice()).unwrap();
    (vbuf, ibuf)
}

fn square_at((x, y): (f32, f32), (bu, bv): (f32, f32)) -> [V2D;4] {
    [ V2D { pos: [ x - 0.5, y - 0.5 ], tex: [bu,bv] },
      V2D { pos: [ x + 0.5, y - 0.5 ], tex: [bu,bv+0.5] },
      V2D { pos: [ x + 0.5, y + 0.5 ], tex: [bu+0.5,bv+0.5] },
      V2D { pos: [ x - 0.5, y + 0.5 ], tex: [bu,bv+0.5] },
    ]
}

fn ball(d: &Display, pos: (f32, f32)) -> (VertexBuffer<V2D>, IndexBuffer<u16>) {
    let vertices = square_at(pos, (0.0, 0.0));
    let indices = [ 2, 1, 0, 3, 2, 0 ];
    let vbuf = VertexBuffer::new(d, &vertices).unwrap();
    let ibuf = IndexBuffer::new(d, PrimitiveType::TrianglesList, &indices).unwrap();
    (vbuf, ibuf)
}

fn main() {
    let display = WindowBuilder::new()
        .with_title(format!("bouncy"))
        .build_glium()
        .unwrap();
    let program = Program::from_source(
        &display,
        include_str!("../assets/vertex.glsl"),
        include_str!("../assets/fragment.glsl"),
        None).unwrap();
    let texture = load_texture(&display);

    let mut st = BallState {
        pos: (0.0, 0.0),
        dpos: (0.1, 0.1),
    };

    let (w_vbuf, w_ibuf) = walls(&display);

    loop {
        let mut tgt = display.draw();

        let params = Default::default();
        tgt.clear_color(1.0, 1.0, 1.0, 1.0);

        let (width, height) = tgt.get_dimensions();
        let aspect_ratio = height as f32 / width as f32;
        let (vbuf, ibuf) = ball(&display, st.pos);
        tgt.draw(&vbuf, &ibuf, &program,
                 &uniform! {
                     texmap: &texture,
                     ratio: aspect_ratio,
                 },
                 &params).unwrap();
        tgt.draw(&w_vbuf, &w_ibuf, &program,
                 &uniform! {
                     texmap: &texture,
                     ratio: aspect_ratio,
                 },
                 &params).unwrap();

        tgt.finish().unwrap();

        for ev in display.poll_events() {
            match ev {
                Event::Closed => return,
                Event::KeyboardInput(_, n, _) =>
                    match n {
                        9 => return,
                        24 => return,
                        _  => {},
                    },
                _ => {},
            }
        }
        {
            let (x, y) = st.pos;
            let (mut dx, mut dy) = st.dpos;
            if x < -8.0 || x > 8.0 {
                dx = -dx;
            }
            if y < -8.0 || y > 8.0 {
                dy = -dy;
            }
            dy -= 0.01;
            st.pos = (x + dx, y + dy);
            st.dpos = (dx, dy);
        }
    }
}
