use kos::ffi::{
    dc::maple,
    MAPLE_FOREACH,
};
use gldc::{
    gl::*, glext::*, glkos::*, glu::*,
};

// Include texture data generated by build script
static EXTERIOR: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/exterior.vq"));
static INTERIOR: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/interiors.vq"));
static TOP: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/caketops.vq"));
static SLICE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/slices.vq"));

fn main() {
    unsafe {
        // Initialize GLdc
        glKosInit();

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(45.0, 640.0 / 480.0, 0.1, 100.0);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

        glEnable(GL_TEXTURE_2D);
        glShadeModel(GL_SMOOTH);
        glClearColor(0.0, 0.0, 0.0, 0.5);
        glClearDepth(1.0);
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LEQUAL);

        let mut tex_exterior: u32 = 0;
        glGenTextures(1, &mut tex_exterior);
        glBindTexture(GL_TEXTURE_2D, tex_exterior);
        glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_565_VQ_TWID_KOS,
                                  512, 512, 0, EXTERIOR.len() as u32, EXTERIOR.as_ptr() as *const c_void);

        let mut tex_interior: u32 = 0;
        glGenTextures(1, &mut tex_interior);
        glBindTexture(GL_TEXTURE_2D, tex_interior);
        glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_565_VQ_TWID_KOS,
                                  512, 512, 0, INTERIOR.len() as u32, INTERIOR.as_ptr() as *const c_void);

        let mut tex_top: u32 = 0;
        glGenTextures(1, &mut tex_top);
        glBindTexture(GL_TEXTURE_2D, tex_top);
        glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_565_VQ_TWID_KOS,
                                  512, 512, 0, TOP.len() as u32, TOP.as_ptr() as *const c_void);

        let mut tex_slice: u32 = 0;
        glGenTextures(1, &mut tex_slice);
        glBindTexture(GL_TEXTURE_2D, tex_slice);
        glCompressedTexImage2DARB(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_565_VQ_TWID_KOS,
                                  512, 512, 0, SLICE.len() as u32, SLICE.as_ptr() as *const c_void);

        let mut xrot: f32 = 1400.0;
        let mut yrot: f32 = 0.0;
        let mut zrot: f32 = 0.0;


        let mut slices: [f32; 7] = [-1.0;7];

        let mut draw_gl = |slices: &mut [f32; 7]| {
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            glLoadIdentity();

            glTranslatef(0.0, 0.0, -5.0);
            glRotatef(xrot, 1.0, 0.0, 0.0);
            glRotatef(yrot, 0.0, 1.0, 0.0);
            glRotatef(zrot, 0.0, 0.0, 1.0);

            //slice(tex_exterior, tex_top, tex_interior);

            glTranslatef(0.0, 0.0, -1.0);

            for i in 0..7 {
                glPushMatrix();
                glTranslatef(0., -1.0,  1.0);
                glRotatef(2970. * i as f32, 0.0, 1.0, 0.0);
                glTranslatef(0., 1.0,  -1.0);
                if slices[i] != -1. {
                    slices[i] += 0.01;
                    slices[i] *= 1.2;
                    glTranslatef(slices[i], 0.-slices[i]/2.0, 0.);
                }
                glScalef(1., 0.4, 1.);
                let edges = slices[i] != -1.
                    || slices[(i+1)%7] != -1.
                    || slices[(i+6)%7] != -1.;
                slice(tex_exterior, tex_top, tex_interior, edges);
                glPopMatrix();
            }

            yrot += 24.0;
        };

        let mut last_pressed = false;
        let mut current = 0;

        // Done setting up, now let's loop!
        loop {
            let mut done = false;

            MAPLE_FOREACH!(maple::MAPLE_FUNC_CONTROLLER, maple::controller::cont_state_t, state, || {
                if ((*state).buttons & maple::controller::CONT_START) != 0 {
                    println!("Start pressed! Quitting...");
                    done = true;
                }
                if ((*state).buttons & maple::controller::CONT_A) != 0 {
                    slices[current % 7] = 0.0;
                    last_pressed = true;
                } else if {last_pressed} {
                    current += 1;
                    last_pressed = false;
                    
                }
            });

            if done == true {
                break;
            }

            draw_gl(&mut slices);

            glKosSwapBuffers();
        }

        // Clean up our textures
        glDeleteTextures(1, &mut tex_exterior);
        glDeleteTextures(1, &mut tex_interior);
        glDeleteTextures(1, &mut tex_top);
        glDeleteTextures(1, &mut tex_slice);
    }

    println!("Bye!");
}

unsafe fn slice(tex_exterior: u32, tex_top: u32, tex_interior: u32, edges: bool) {
    if edges {
        // left face
        glBindTexture(GL_TEXTURE_2D, tex_interior);
        glBegin(GL_QUADS);
        glColor3f(1.0, 1.0, 1.0);
        glTexCoord2f(1.0, 0.0);
        glVertex3f(0., -1.0,  1.0);
        glTexCoord2f(0.0, 0.0);
        glVertex3f(1.0, -1.0,  1.5);
        glTexCoord2f(0.0, 1.0);
        glVertex3f(1.0,  1.0,  1.5);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(0.0,  1.0,  1.0);
        glEnd();

        // right face
        glBindTexture(GL_TEXTURE_2D, tex_interior);
        glBegin(GL_QUADS);
        glColor3f(1.0, 1.0, 1.0);
        glTexCoord2f(1.0, 0.0);
        glVertex3f(0., -1.0,  1.0);
        glTexCoord2f(0.0, 0.0);
        glVertex3f(1.0, -1.0,  0.5);
        glTexCoord2f(0.0, 1.0);
        glVertex3f(1.0,  1.0,  0.5);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(0.0,  1.0,  1.0);
        glEnd();
    }
    
    // top face
    glBindTexture(GL_TEXTURE_2D, tex_top);
    glBegin(GL_TRIANGLES);
    glColor3f(1.0, 1.0, 1.0);
    glTexCoord2f(1.0, 0.0);
    glVertex3f(1.0,  1.0,  0.5);
    glTexCoord2f(1.0, 1.0);
    glVertex3f(0.0,  1.0,  1.0);
    glTexCoord2f(0.0, 0.0);
    glVertex3f(1.0,  1.0,  1.5);
    glEnd();

    // bottom face
    glBindTexture(GL_TEXTURE_2D, tex_exterior);
    glBegin(GL_TRIANGLES);
    glColor3f(1.0, 1.0, 1.0);
    glTexCoord2f(1.0, 0.0);
    glVertex3f(1.0,  -1.0,  0.5);
    glTexCoord2f(1.0, 1.0);
    glVertex3f(0.0,  -1.0,  1.0);
    glTexCoord2f(0.0, 0.0);
    glVertex3f(1.0,  -1.0,  1.5);
    glEnd();

    // outer face
    glBindTexture(GL_TEXTURE_2D, tex_exterior);
    glBegin(GL_QUADS);
    glColor3f(1.0, 1.0, 1.0);

    glTexCoord2f(1.0, 0.0);
    glVertex3f(1.0,  1.0,  0.5);
    glTexCoord2f(0.0, 0.0);
    glVertex3f(1.0,  1.0,  1.5);
    glTexCoord2f(0.0, 1.0);
    glVertex3f(1.0,  -1.0,  1.5);
    glTexCoord2f(1.0, 1.0);
    glVertex3f(1.0,  -1.0,  0.5);
    glEnd();
}
