#define GLEW_STATIC
#define GLM_FORCE_RADIANS

#include <iostream>
#include <fstream>
#include <sstream>

#include <GL/glew.h>
#include <GL/glu.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
#include <SOIL/SOIL.h>

#include <stdio.h>

#include <websock/websock.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include "camera.hpp"
#include "texture.hpp"
#include "shader.hpp"
#include "thing.hpp"

SDL_Window *window;
SDL_GLContext context;
float mL = 0;
float mR = 0;
int screenWidth = 800;
int screenHeight = 600;
float lastX = 400;
float lastY = 300;
bool firstMouse = true;
bool keys[1024];
bool anim = 1;
int tick = 0;

glm::vec4 bg = {0.0f, 0.0f, 0.0f, 1.0f};

enum anim_statess {
    ANIM_1,
    ANIM_2,
    ANIM_3,
    ANIM_4,
    ANIM_5, 
    ANIM_6,
    ANIM_7,
    ANIM_8,
    ANIM_9,
    ANIM_10,
    ANIM_11,
    ANIM_12,
    ANIM_13,
    ANIM_14,
    ANIM_15
};

int ANIM_STATE = ANIM_1;

Camera camera(glm::vec3(5.0f, 5.0f, 5.0f));

/* GLfloat vertices[] = { */
/*     0.5f,  0.5f, 0.0f,  0.0f, 0.0f,    // Top Right */
/*     0.5f, -0.5f, 0.0f,  1.0f, 0.0f,    // Bottom Right */
/*     -0.5f, -0.5f, 0.0f,  1.0f, 1.0f,    // Bottom Left */
/*     -0.5f,  0.5f, 0.0f,  0.0f, 1.0f     // Top Left */ 
/* }; */

GLfloat vertices[] = {
    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f, 0.0f,  0.0f, -1.0f,
    0.5f, -0.5f, -0.5f,  1.0f, 0.0f,  0.0f,  0.0f, -1.0f,
    0.5f,  0.5f, -0.5f,  1.0f, 1.0f,  0.0f,  0.0f, -1.0f,
    0.5f,  0.5f, -0.5f,  1.0f, 1.0f,  0.0f,  0.0f, -1.0f,
    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f, 0.0f,  0.0f, -1.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f, 0.0f,  0.0f, -1.0f,
                                                          
    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 0.0f,  0.0f,  1.0f,
    0.5f, -0.5f,  0.5f,  1.0f, 0.0f,  0.0f,  0.0f,  1.0f,
    0.5f,  0.5f,  0.5f,  1.0f, 1.0f,  0.0f,  0.0f,  1.0f,
    0.5f,  0.5f,  0.5f,  1.0f, 1.0f,  0.0f,  0.0f,  1.0f,
    -0.5f,  0.5f,  0.5f,  0.0f, 1.0f, 0.0f,  0.0f,  1.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 0.0f,  0.0f,  1.0f,
                                                          
    -0.5f,  0.5f,  0.5f,  1.0f, 0.0f, 1.0f,  0.0f,  0.0f,
    -0.5f,  0.5f, -0.5f,  1.0f, 1.0f, 1.0f,  0.0f,  0.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f, 1.0f,  0.0f,  0.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f, 1.0f,  0.0f,  0.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 1.0f,  0.0f,  0.0f,
    -0.5f,  0.5f,  0.5f,  1.0f, 0.0f, 1.0f,  0.0f,  0.0f,
                                                          
    0.5f,  0.5f,  0.5f,  1.0f, 0.0f,  1.0f,  0.0f,  0.0f,
    0.5f,  0.5f, -0.5f,  1.0f, 1.0f,  1.0f,  0.0f,  0.0f,
    0.5f, -0.5f, -0.5f,  0.0f, 1.0f,  1.0f,  0.0f,  0.0f,
    0.5f, -0.5f, -0.5f,  0.0f, 1.0f,  1.0f,  0.0f,  0.0f,
    0.5f, -0.5f,  0.5f,  0.0f, 0.0f,  1.0f,  0.0f,  0.0f,
    0.5f,  0.5f,  0.5f,  1.0f, 0.0f,  1.0f,  0.0f,  0.0f,
                                                          
    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f, 0.0f, -1.0f,  0.0f,
    0.5f, -0.5f, -0.5f,  1.0f, 1.0f,  0.0f, -1.0f,  0.0f,
    0.5f, -0.5f,  0.5f,  1.0f, 0.0f,  0.0f, -1.0f,  0.0f,
    0.5f, -0.5f,  0.5f,  1.0f, 0.0f,  0.0f, -1.0f,  0.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 0.0f, -1.0f,  0.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f, 0.0f, -1.0f,  0.0f,
                                                          
    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f, 0.0f,  1.0f,  0.0f,
    0.5f,  0.5f, -0.5f,  1.0f, 1.0f,  0.0f,  1.0f,  0.0f,
    0.5f,  0.5f,  0.5f,  1.0f, 0.0f,  0.0f,  1.0f,  0.0f,
    0.5f,  0.5f,  0.5f,  1.0f, 0.0f,  0.0f,  1.0f,  0.0f,
    -0.5f,  0.5f,  0.5f,  0.0f, 0.0f, 0.0f,  1.0f,  0.0f,
    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,  0.0f,  1.0f,  0.0f
};

glm::vec3 cubePositions[] = {
    glm::vec3( 0.0f,  0.0f,  0.0f),
    glm::vec3( 2.0f,  5.0f, -15.0f),
    glm::vec3(-1.5f, -2.2f, -2.5f),
    glm::vec3(-3.8f, -2.0f, -12.3f),
    glm::vec3( 2.4f, -0.4f, -3.5f),
    glm::vec3(-1.7f,  3.0f, -7.5f),
    glm::vec3( 1.3f, -2.0f, -2.5f),
    glm::vec3( 1.5f,  2.0f, -2.5f),
    glm::vec3( 1.5f,  0.2f, -1.5f),
    glm::vec3(-1.3f,  1.0f, -1.5f)
};

GLuint indices[] = {  // Note that we start from 0!
    0, 1, 3,   // First Triangle
    1, 2, 3    // Second Triangle
}; 

GLfloat texCoords[] = {
    0.0f, 0.0f,
    1.0f, 0.0f,
    1.0f, 1.0f,
    0.0f, 1.0f
};



int gl_init() {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);

    window = SDL_CreateWindow("OpenGL", 100, 100, screenWidth, screenHeight, SDL_WINDOW_OPENGL);
    context = SDL_GL_CreateContext(window); 
    if (context == NULL) fprintf(stderr, "ERROR in context (Don't use Intel with GL > 3.3): %s\n", SDL_GetError()); 

    return 0;
}

int glew_init() {
    // Set up glew 
    glewExperimental = GL_TRUE;
    GLenum glewError = glewInit();
    if (glewError != GLEW_OK) fprintf(stderr, "ERROR in glewInit: %s\n", glewGetErrorString(glewError));

    SDL_SetRelativeMouseMode(SDL_TRUE);
    return 0;
}

int event_controller(SDL_Event *windowEvent, int *alive, int time_delta) {
    while (SDL_PollEvent(windowEvent) != 0) {
        if (windowEvent->type == SDL_QUIT) {
            *alive = 0;     
            break;

        }
        else if (windowEvent->type == SDL_KEYDOWN) {
            switch (windowEvent->key.keysym.sym) {
                case SDLK_LEFT:
                    camera.ProcessKeyboard(LEFT, time_delta);
                    mL += 0.1f;
                    break;
                case SDLK_RIGHT:
                    camera.ProcessKeyboard(RIGHT, time_delta);
                    mL -= 0.1f;
                    break;
                case SDLK_UP:
                    camera.ProcessKeyboard(FORWARD, time_delta);
                    mR += 0.1f;
                    break;
                case SDLK_DOWN:
                    camera.ProcessKeyboard(BACKWARD, time_delta);
                    mR -= 0.1f;
                    break;

                case SDLK_1:
                    anim = !anim;
                    break;
            }
        }
    }

    // Get mouse movement
    int xpos, ypos;
    SDL_GetMouseState(&xpos, &ypos);

    if(firstMouse)
    {
        lastX = xpos;
        lastY = ypos;
        firstMouse = false;
    }

    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos;

    lastX = xpos;
    lastY = ypos;

    camera.ProcessMouseMovement(xoffset, yoffset);
    /* printf("x: %d, y: %d\n", xpos, ypos); */
    return 0;
}

glm::vec3 cubes_pos[50*50*2] = {glm::vec3(0.0f, -10.0f, 0.0f)};

void cubes_anim(Thing **things, GLfloat dt) {
            for (int i = 0; i < 50; i++) {
                for (int j = 0; j < 50; j++) {
                    cubes_pos[i*50+j] = cubes_pos[i*50+j] + (cubes_pos[50*50 + i*50+j] - cubes_pos[i*50+j]) * (dt/10000) * 0.1f;
                    things[i*50+j]->set_position(cubes_pos[i*50+j]);
                }
            }
            printf("CUBE_ANIM_POS %4f\tDT %4f\n", cubes_pos[0*50].y, dt/10000);
    
}

void a_rainbow(Thing **things, GLfloat time) {
    GLfloat norm_ticks = glm::sin(time / 1000.0f);
    bg.x = 1.0f;
    bg.y = 1.0f;
    bg.z = 1.0f;
            for (int i = 0; i < 50; i++) {
                for (int j = 0; j < 50; j++) {
                    things[i*50+j]->set_cube_color(glm::vec3(1.0f, 1.0f, 1.0f));
                    things[i*50+j]->set_light_color(glm::vec3(1.0f, 1.0f, 1.0f));
                    things[i*50+j]->set_position(glm::vec3(i, -5.0f, j));
                    things[i*50+j]->set_light_position(glm::vec3(10.0f, 10.0f, 10.0f));
                }
            }

}
void a_bgflash(Thing **things, GLfloat time) {
    GLfloat norm_ticks = glm::sin(time / 1000.0f);
    bg.x = 1.0f * norm_ticks;
    bg.y = 1.0f * -norm_ticks;
    bg.z = 1.0f * glm::cos(time / 1000);
            for (int i = 0; i < 50; i++) {
                for (int j = 0; j < 50; j++) {
                    things[i*50+j]->set_cube_color(glm::vec3(0.0f, 0.0f, 0.0f));
                    things[i*50+j]->set_light_color(glm::vec3(0.0f, 0.0f, 0.0f));
                    things[i*50+j]->modify_position(glm::vec3(0.0f, norm_ticks * 30, 0.0f));
                    things[i*50+j]->set_light_position(glm::vec3((float) 10, 10.0f, (float) 10));
                }
            }

}
void a_2(Thing **things, GLfloat time) {
    GLfloat norm_ticks = glm::sin(time / 1000.0f);
    GLfloat norm_ticksc = glm::cos(time / 1000.0f);

    GLfloat norm_ticks_fast = glm::sin(time / 500.0f);
    GLfloat norm_ticksc_fast = glm::cos(time / 500.0f);
        for (int i = 0; i < 50; i++) {
            for (int j = 0; j < 50; j++) {
                things[i*50+j]->set_rotation(glm::vec3(1.0f, 1.0f, 1.0f), norm_ticks*4 );
                things[i*50+j]->set_scale(glm::vec3(1.0f, 1.0f, 1.0f));
                things[i*50+j]->set_light_color(glm::vec3((float) glm::sin(i/10)+1.0f, -5.0f + norm_ticks*10 , (float) glm::sin(j/10)+1.0f));
                things[i*50+j]->set_light_position(glm::vec3((float) i/10, -5.0f + glm::sin(i*j)*20, (float) j/10));

                things[i*50+j]->set_position(glm::vec3((float) i, -5.0f + glm::sin(i)*glm::sin(j)*20 , (float) -j));
                if (i%8 && j%8) {
                things[i*50+j]->set_position(glm::vec3((float) i, -5.0f + norm_ticks*12 , (float) -j));
                    if (i%4 && j%4) {
                        things[i*50+j]->set_position(glm::vec3((float) i, -5.0f - norm_ticks_fast*12 , (float) -j));
                    }
                }
            }
        }
}

int animation_controller(Thing **things, GLfloat time, GLfloat dt) {
    bool once = 0;
    GLfloat norm_ticks = glm::sin(time / 1000.0f);
    GLfloat norm_ticksc = glm::cos(time / 1000.0f);

    GLfloat norm_ticks_fast = glm::sin(time / 500.0f);
    GLfloat norm_ticksc_fast = glm::cos(time / 500.0f);

    if ((int)(time/1000)%8) {
        tick++;
    }
    if (tick >= 50) {
        tick = 0;
    }
    
    switch (ANIM_STATE) {
        case ANIM_1: 
                camera.Position.x = 0;
                camera.Position.y = 0;
                camera.Position.z = 0;

            for (int i = 0; i < 50; i++) {
                for (int j = 0; j < 50; j++) {
                    cubes_pos[50*50 + i*50+j] = glm::vec3(-25 + i*1.2, -10.0f, -100 + j*1.2);
                }
            }

            cubes_anim(things, dt);
        /* for (int i = 0; i < 50; i++) { */
        /*     for (int j = 0; j < 50; j++) { */
        /*             things[i*50+j]->set_cube_color(glm::vec3(1.0f, 0.0f, 0.0f)); */
        /*         if (i%4) { */
        /*             things[i*50+j]->set_position(glm::vec3((float) i, -5.0f + norm_ticks*8 , (float) -j)); */
        /*         } else if (i%3) { */
        /*             things[i*50+j]->set_position(glm::vec3((float) i, -5.0f + norm_ticks*10 , (float) -j)); */
        /*         } else if (i%2) { */
        /*             things[i*50+j]->set_position(glm::vec3((float) i, -5.0f + norm_ticks*12 , (float) -j)); */
        /*         } else { */
        /*             things[i*50+j]->set_position(glm::vec3((float) i, -5.0f + norm_ticks*14 , (float) -j)); */
        /*         } */
        /*     } */
        /* } */
            break;

        case ANIM_2: 
            /* a_2(things, time); */
            cubes_anim(things, dt);
            break;

        case ANIM_3:
            /* a_2(things, time); */
            /* a_bgflash(things, time); */
            for (int i = 0; i < 50; i++) {
                for (int j = 0; j < 50; j++) {
                    cubes_pos[50*50 + i*50+j] = glm::vec3(-100 + glm::pow(i,2), 8.0f, -200 + glm::pow(j,2));
                }
            }
            cubes_anim(things, dt);
            break;

        case ANIM_4: 
            /* a_2(things, time); */
            /* a_rainbow(things, time); */
            cubes_anim(things, dt);
            break;

        case ANIM_5: 
            cubes_anim(things, dt);
            break;
        case ANIM_6:
           if (!once) {
            for (int i = 0; i < 50; i++) {
                for (int j = 0; j < 50; j++) {
                    cubes_pos[50*50 + i*50+j] += glm::vec3(glm::sin(i/3.14), 0.0f, 0.0f);
                }
            }
            once = !once;
           }
            cubes_anim(things, dt);
            break;
        case ANIM_7:
            if (once) {
                for (int i = 0; i < 50; i++) {
                    for (int j = 0; j < 50; j++) {
                        cubes_pos[50*50 + i*50+j] += glm::vec3(0.0f, glm::sin(i/3.14), 0.0f);
                    }
                }
            }
            once = !once;
                for (int i = 0; i < 50; i++) {
                    for (int j = 0; j < 50; j++) {
                        cubes_pos[50*50 + i*50+j] += glm::vec3(0.0f, glm::sin(time / 100)*2, 0.0f);
                        if ((i*j)%2) {
                            cubes_pos[50*50 + i*50+j] += glm::vec3(0.0f, glm::sin(time / 200)*4, 0.0f);
                        }
                    }
                }
            cubes_anim(things, dt);
            break;
        case ANIM_8:
            if (!once) {
                for (int i = 0; i < 50; i++) {
                    for (int j = 0; j < 50; j++) {
                        cubes_pos[50*50 + i*50+j] = glm::vec3(20.0f, j, -30.0f);
                    }
                }
            }
            once = !once;
            cubes_anim(things, dt);
            break;
        case ANIM_9:
            /* if (once) { */
                for (int i = 0; i < 50; i++) {
                    for (int j = 0; j < 50; j++) {
                        cubes_pos[50*50 + i*50+j] += glm::vec3(glm::sin(time/600)*glm::cos(i/3.14)*2, 0.0f, 0.0f);
                    }
                }
            /* } */
            once = !once;
            cubes_anim(things, dt);
            break;
        case ANIM_10:
            /* if (once) { */
                for (int i = 0; i < 50; i++) {
                    for (int j = 0; j < 50; j++) {
                        cubes_pos[50*50 + i*50+j] = glm::vec3( 20.0f, 0.0f, -30.0f );
                    }
                }
            /* } */
            /* once = !once; */
            cubes_anim(things, dt);
            break;
        case ANIM_11:
            if (!once) {
                for (int i = 0; i < 50; i++) {
                    for (int j = 0; j < 50; j++) {
                        cubes_pos[50*50 + i*50+j] = glm::vec3( -5.0f + j, i, -30.0f );
                    }
                }
            }
            once = !once;
            cubes_anim(things, dt);
            break;
        case ANIM_12:
                for (int i = 0; i < 50; i++) {
                    for (int j = 0; j < 50; j++) {
                        cubes_pos[50*50 + i*50+j] = glm::vec3( 20.0f, 0.0f, -30.0f );
                        things[i*50+j]->set_cube_color(glm::vec3(tick/50, tick/50, tick/50));
                    }
                }
            cubes_anim(things, dt);
            break;
        case ANIM_13:
                for (int i = 0; i < 50; i++) {
                    for (int j = 0; j < 50; j++) {
                        cubes_pos[50*50 + i*50+j] = glm::vec3( -5.0f + j, i, -30.0f );
                        things[i*50+j]->set_light_position(glm::vec3(20.0f + glm::cos(time/600)*10, 10 + glm::sin(time/600)*10, -20.0f));
                            

                    }
                }
                for (int i = 0; i < 50; i++) {
                    cubes_pos[50*50 + i*50+tick] = glm::vec3( -5.0f + glm::sin(time/300)*4, i, -30.0f );
                    /* cubes_pos[50*50 + tick*50+i] = glm::vec3( -5.0f + glm::sin(time/300)*4, i, -30.0f ); */
                }
            once = !once;
            cubes_anim(things, dt);
            break;
        case ANIM_14:
                for (int i = 0; i < 50; i++) {
                    for (int j = 0; j < 50; j++) {
                        cubes_pos[50*50 + i*50+j] = glm::vec3( -5.0f + j, i, -30.0f );
                        things[i*50+j]->set_light_position(glm::vec3(20.0f + glm::cos(time/600)*10, 10 + glm::sin(time/600)*10, -20.0f));
                    }
                }
                for (int i = 0; i < 50; i++) {
                    cubes_pos[50*50 + i*50+tick] = glm::vec3( -5.0f + glm::sin(time/300)*4, i, -30.0f );
                    /* cubes_pos[50*50 + tick*50+i] = glm::vec3( -5.0f + glm::sin(time/300)*4, i, -30.0f ); */
                }
            cubes_anim(things, dt);
            break;
        case ANIM_15:
            cubes_pos[50*50 + 0*50+25] = glm::vec3( -5.0f + 25, 10, 2.0f );
                    for (int j = 0; j < 50; j++) {
                        things[j*50+tick]->set_light_color(glm::vec3(glm::sin(tick/3.14), glm::sin(tick/3.14), glm::sin(tick/3.14)));
                    }
            cubes_anim(things, dt);
            break;

    }

        if (time > 1000 && time < 2000) {
            ANIM_STATE = ANIM_2; 
        } else if (time > 2000 && time < 4000) {
            ANIM_STATE = ANIM_2;
        } else if (time > 4000 && time < 6000) {
            ANIM_STATE = ANIM_3;
        } else if (time > 6000 && time < 8000) {
            ANIM_STATE = ANIM_4;
        } else if (time > 8000 && time < 10000) {
            ANIM_STATE = ANIM_5;
        } else if (time > 10000 && time < 14000) {
            ANIM_STATE = ANIM_6;
        } else if (time > 14000 && time < 18000) {
            ANIM_STATE = ANIM_7;
        } else if (time > 18000 && time < 22000) {
            ANIM_STATE = ANIM_8;
        } else if (time > 22000 && time < 26000) {
            ANIM_STATE = ANIM_9;
        } else if (time > 26000 && time < 28000) {
            ANIM_STATE = ANIM_10;
        } else if (time > 28000 && time < 28030) {
            ANIM_STATE = ANIM_14;
        } else if (time > 28030 && time < 30000) {
            ANIM_STATE = ANIM_15;
        } else if (time > 30000 && time < 34000) {
            ANIM_STATE = ANIM_14;
        } else if (time > 34000 && time < 38000) {
            ANIM_STATE = ANIM_15;
        }
        printf(" ANIM_STATE %d TICK %d", ANIM_STATE, tick);
    return 0;
}

int render_init(GLuint *VBO, GLuint *VAO, GLuint *EBO) {
    glGenBuffers(1, VBO); 
    glGenVertexArrays(1, VAO); 
    /* glGenBuffers(1, EBO); */

    glBindBuffer(GL_ARRAY_BUFFER, *VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindVertexArray(*VAO);
    /* glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *EBO); */
    /* glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); */ 

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0);

    glEnable(GL_DEPTH_TEST);

    return 0;
}


int main(int argc, char *argv[]) {
    int alive = 1;
    SDL_Event windowEvent;
    GLuint VBO, VAO, EBO;
    Shader *s, *lampShader;
    Texture *t0, *t1;

    /* libwebsock_context *ctx = NULL; */
    /* ctx = libwebsock_init(); */
    /* if(ctx == NULL) { */
    /*     fprintf(stderr, "Error during libwebsock_init.\n"); */
    /*     exit(1); */
    /* } */
    /* libwebsock_bind(ctx, "0.0.0.0", "8080"); */
    /* libwebsock_wait(ctx); */

    gl_init();
    glew_init();

    s = new Shader("vertex.glsl", "fragment.glsl");
    lampShader = new Shader("lamp.vert", "lamp.frag");
    t0 = new Texture("wall.png");
    t1 = new Texture("awesomeface.png");

    render_init(&VBO, &VAO, &EBO);

    int xdir = 50;
    int ydir = 50;
    Thing *things[xdir*ydir];
    for (int i = 0; i < xdir; i++) {
        for (int j = 0; j < ydir; j++) {
            things[i*xdir + j] = new Thing(&camera, VBO, s, t0, glm::vec3((float) i, -5.0f,(float) -j)); 
            things[i*xdir + j]->set_rotation(glm::vec3(1.0f, 1.0f, 1.0f), i*2);
        }
    }

    while (alive) {
        GLfloat time_delta = (GLfloat) SDL_GetTicks();
        event_controller(&windowEvent, &alive, time_delta);
        if ( anim ) {
            animation_controller(things, (GLfloat) SDL_GetTicks(), time_delta );
        }
        glClearColor(bg.x, bg.y, bg.z, bg.w);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


        for (int i = 0; i < xdir; i++) {
            for (int j = 0; j < ydir; j++) {
                things[i*xdir+j]->render();
            }
        }


        SDL_GL_SwapWindow(window);
        fprintf(stderr, "\r Frame time: %.1f ms", ((GLfloat) SDL_GetTicks() - time_delta));
    }

    for (int i = 0; i < xdir; i++) {
        for (int j = 0; j < ydir; j++) {
            delete things[i*xdir + j];
        }
    }
    delete s;
    delete lampShader;
    delete t0;
    delete t1;

    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);

    SDL_GL_DeleteContext(context);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}
