Documentation
extern crate gl;
extern crate glutin;
extern crate time;
extern crate gl_helpers;


use glutin::GlContext;
use gl::types::*;

use gl_helpers::*;


#[cfg(target_os = "emscripten")]
pub mod emscripten {
    use std::cell::RefCell;
    use std::ptr::null_mut;
    use std::os::raw::{c_int, c_void, c_float};

    #[allow(non_camel_case_types)]
    type em_callback_func = unsafe extern fn();

    extern {
        pub fn emscripten_set_main_loop(func: em_callback_func, fps: c_int, simulate_infinite_loop: c_int);
        pub fn emscripten_cancel_main_loop();
        pub fn emscripten_get_now() -> c_float;
    }

    thread_local!(static MAIN_LOOP_CALLBACK: RefCell<*mut c_void> = RefCell::new(null_mut()));

    pub fn set_main_loop_callback<F>(callback: F) where F: FnMut() {
        MAIN_LOOP_CALLBACK.with(|log| {
            *log.borrow_mut() = &callback as *const _ as *mut c_void;
        });

        unsafe { emscripten_set_main_loop(wrapper::<F>, 0, 1); }

        unsafe extern "C" fn wrapper<F>() where F: FnMut() {
            MAIN_LOOP_CALLBACK.with(|z| {
                let closure = *z.borrow_mut() as *mut F;
                (*closure)();
            });
        }
    }
}

static SIMPLE_VERTEX_DATA: [GLfloat; 16] = [
    //   position     uv
     1f32,  1f32,   1f32, 1f32,
    -1f32,  1f32,   0f32, 1f32,
     1f32, -1f32,   1f32, 0f32,
    -1f32, -1f32,   0f32, 0f32
];

static SIMPLE_VERTEX: &'static str = "
    uniform vec2 size;

    attribute vec2 position;
    attribute vec2 uv;

    varying vec2 v_uv;

    void main() {
        gl_Position = vec4(position * size * 0.5, 0, 1.0);
        v_uv = uv;
    }
";

static SIMPLE_FRAGMENT: &'static str = "
    precision mediump float;

    uniform float alpha;

    varying vec2 v_uv;

    void main() {
        gl_FragColor = vec4(v_uv, 1.0, alpha);
    }
";

fn main() {
    let mut screen_width = 1024_usize;
    let mut screen_height = 768_usize;

    let mut events_loop = glutin::EventsLoop::new();
    let window = glutin::WindowBuilder::new()
        .with_title("Simple")
        .with_dimensions(screen_width as u32, screen_height as u32);
    let gl_window = glutin::GlWindow::new(
        window,
        glutin::ContextBuilder::new()
            .with_vsync(true),
        &events_loop
    ).unwrap();

    unsafe {
        gl_window.make_current().unwrap();
    }

    gl::load_with(|symbol| gl_window.get_proc_address(symbol) as *const _);

    let gl_info = GLInfo::new();
    println!("{}", gl_info.version());
    println!(
        "OpenGL version: {:?}.{:?}, GLSL version {:?}.{:?}0",
        gl_info.major(), gl_info.minor(), gl_info.glsl_major(), gl_info.glsl_minor()
    );

    gl_set_defaults();

    let program = GLProgram::new(SIMPLE_VERTEX, SIMPLE_FRAGMENT);

    let buffer = GLBuffer::new(BufferTarget::Array, 4, Usage::StaticDraw, &SIMPLE_VERTEX_DATA);

    let mut vertex_array = GLVertexArray::new();

    vertex_array.bind();
    vertex_array.add_attribute(&buffer, program.get_attribute("position"), 0);
    vertex_array.add_attribute(&buffer, program.get_attribute("uv"), 2);

    vertex_array.enable_attributes();

    program.get_uniform("alpha").set_1f(0.5_f32);

    let start_time = time::now();
    let mut last_time = start_time;
    let mut current_time = time::now();
    let mut ms = 0_f32;
    let mut dt = 1000.0 / 60.0;

    let mut size = [1f32; 2];

    emscripten::set_main_loop_callback(move || {
        current_time = time::now();
        dt = (current_time - last_time).num_nanoseconds().unwrap() as f32 * 0.000001f32;
        ms += dt;
        last_time = current_time;

        events_loop.poll_events(|event| {
            match event {
                glutin::Event::WindowEvent { event: glutin::WindowEvent::Closed, .. } => {},
                glutin::Event::WindowEvent { event: glutin::WindowEvent::Resized(w, h), .. } => {
                    screen_width = w as usize;
                    screen_height = h as usize;

                    gl_window.resize(w, h);
                    gl_set_viewport(0, 0, screen_width, screen_height);
                },
                _ => (),
            }
        });

        gl_set_clear_color(&[0.3, 0.3, 0.3, 1.0]);
        gl_clear(true, true, true);

        size[0] = 1_f32 + ((ms * 0.005_f32).cos() * 0.5_f32);
        size[1] = 1_f32 + ((ms * 0.005_f32).sin() * 0.5_f32);
        program.get_uniform("size").set_2f(&size);

        gl_draw_arrays(DrawMode::TriangleStrip, 0, 4);

        gl_window.swap_buffers().unwrap();
    });
}