
#include "window.h"

#include "vec.h"
#include "mat.h"
#include "color.h"

#include "mesh.h"
#include "orbiter.h"

#include "wavefront.h"
#include "draw.h"


Orbiter camera;


struct Object
{
    Mesh mesh;
    Transform model;
    Transform dynamic;
    
    Object( ) : model(make_identity()), dynamic(make_identity()) {}
    
    Object( const Mesh& m, const Color& c ) : mesh(m), model(make_identity()), dynamic(make_identity())
    {
        mesh.color= c;
    }
};

void draw( Object& object, const Transform& view, const Transform& projection )
{
    draw(object.mesh, object.model * object.dynamic, view, projection);
}

void draw( Object& object, const Orbiter& orbiter )
{
    draw(object.mesh, object.model * object.dynamic, orbiter);
}


std::vector<Object> objects;
Mesh grid;

// utilitaire. creation d'une grille / repere.
Mesh make_grid( )
{
    Mesh grid= create_mesh(GL_LINES);
    
    for(int x= 0; x < 10; x++)
    {
        float px= (float) x - 5.f + 0.5f;
        push_vertex(grid, make_point(px, 0, -4.5f)); 
        push_vertex(grid, make_point(px, 0, 4.5f)); 
    }

    for(int z= 0; z < 10; z++)
    {
        float pz= (float) z - 5.f + 0.5f;
        push_vertex(grid, make_point(-4.5f, 0, pz)); 
        push_vertex(grid, make_point(4.5f, 0, pz)); 
    }
    
    return grid;
}

// creation des objets openGL
int init( )
{
    // lire un maillage
    Mesh cube= read_mesh("data/cube.obj");
    if(cube == Mesh::error())
        return -1;
    
    Object cube1(cube, make_red());
    cube1.model= make_translation(-1, 0, 0);
    
    Object cube2(cube, make_blue());
    cube2.model= make_translation(1, 0, 0);
    
    objects.push_back(cube1);
    objects.push_back(cube2);
    
    camera= make_orbiter();
    grid= make_grid();
    
    // etat par defaut
    glClearColor(0.2f, 0.2f, 0.2f, 1);           // couleur par defaut de la fenetre
    
    glClearDepthf(1);                   // profondeur par defaut
    glDepthFunc(GL_LEQUAL);               // ztest, conserver l'intersection la plus proche de la camera
    glEnable(GL_DEPTH_TEST);            // activer le ztest
    
    glLineWidth(2.5f);                  // epaisseur des lignes de la grille (pixels)
    
    return 0;   // renvoyer 0 ras, pas d'erreur, sinon renvoyer -1
}

// affichage
int draw( )
{
    // on commence par effacer la fenetre avant de dessiner quelquechose
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // on efface aussi le zbuffer
    
    // recupere les mouvements de la souris, utilise directement SDL2
    int mx, my;
    unsigned int mb= SDL_GetRelativeMouseState(&mx, &my);
    
    // deplace la camera
    if(mb & SDL_BUTTON(1))              // le bouton gauche est enfonce
        // tourne autour de l'objet
        orbiter_rotation(camera, mx, my);
    
    else if(mb & SDL_BUTTON(3))         // le bouton droit est enfonce
        // approche / eloigne l'objet
        orbiter_move(camera, mx);
    
    else if(mb & SDL_BUTTON(2))         // le bouton du milieu est enfonce
        // deplace le point de rotation
        orbiter_translation(camera, (float) mx / (float) window_width(), (float) my / (float) window_height()); 
    
    // affiche la grille / repere
    draw(grid, camera);
    
#if 1
    float time= (float) SDL_GetTicks();
    
    static float cube1x= 0;
    if(key_state('q'))
        cube1x-= 0.1f;
    if(key_state('s'))
        cube1x+= 0.1f;
    
    //~ objects[0].dynamic= make_translation(cube1x, 0, 0);
    //~ objects[0].dynamic= make_rotationY(time / 16) * make_translation(cube1x, 0, 0);
    objects[0].dynamic= make_translation(cube1x, 0, 0) * make_rotationY(time / 16);
    
    static float cube2z= 0;
    if(key_state('r'))
        cube2z-= 0.1f;
    if(key_state('f'))
        cube2z+= 0.1f;

    objects[1].dynamic= make_translation(0, 0, cube2z);
    
    // affiche les objets
    for(unsigned int i= 0; i < objects.size(); i++)
        draw(objects[i], camera);
#endif

    return 1;   // on continue, renvoyer 0 pour sortir de l'application
}

// destruction des objets openGL
int quit( )
{
    return 0;   // ras, pas d'erreur
}


int main( int argc, char **argv )
{
    // etape 1 : creer la fenetre
    Window window= create_window(1024, 640);
    if(window == NULL) 
        return 1;       // erreur lors de la creation de la fenetre ou de l'init de sdl2
    
    // etape 2 : creer un contexte opengl pour pouvoir dessiner
    Context context= create_context(window);
    if(context == NULL) 
        return 1;       // erreur lors de la creation du contexte opengl
    
    // etape 3 : creation des objets 
    if(init() < 0)
    {
        printf("[error] init failed.\n");
        return 1;
    }
    
    // etape 4 : affichage de l'application, tant que la fenetre n'est pas fermee. ou que draw() ne renvoie pas 0
    run(window, draw);

    // etape 5 : nettoyage
    quit();
    release_context(context);
    release_window(window);
    return 0;
}
