#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <unistd.h>
#include <termios.h>
#include <limits.h>
#include <time.h>
#include <glad/glad.h>
#include <GLFW/glfw3.h>

#ifndef STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#endif

#include "hash.h"
#include "base.h"
#include "move.h"
#include "menus.h"
#include "proj.h"
#include "entities.h"
#include "display.h"
#include "generation.h"

double sim_time;
int triCount;
unsigned int fffff;
int gamemode;

char* newRoomName;
int newRoomCount;
bool switchRoom;

bool jPress = false;
bool gPress = false;
bool rPress = false;
bool pPress = false;

unsigned int textures[16];

bool paused = false;
bool hardReset = true;

double oldx, oldy, oldz;
int oldchx, oldchy;
void reset_everything(GLFWwindow *window, int count, char* folder) {
    oldx = camx;
    oldy = camy;
    oldz = camz;
    oldchx = player_chx;
    oldchy = player_chy;

    camx = /*2*room_width*player_chx+*/0.0;
    camy = 5.0;
    camz = /*2*room_depth*player_chy+*/0.0;

    player_chx = 0;
    player_chy = 0;

    camvx = 0.0;
    camvy = 0.0;
    camvz = 0.0;

    printf("-------------------------------- resetting hashtbl... --------------------------------\n");
    fflush(stdout);

    hashtbl_free(visited);

    printf("-------------------------------- done 1 --------------------------------\n");
    fflush(stdout);
    printf("-------------------------------- resetting pool... --------------------------------\n");
    fflush(stdout);

    free_pool();

    printf("-------------------------------- done 2 --------------------------------\n");
    fflush(stdout);

    printf("-------------------------------- initializing hashtbl... --------------------------------\n");
    fflush(stdout);

    init_hashtbl();

    printf("-------------------------------- done 3 --------------------------------\n");
    fflush(stdout);

    printf("-------------------------------- initializing rooms... --------------------------------\n");
    fflush(stdout);

    char* newFolder = malloc(sizeof(char)*52);
    int i = 0;
    while(folder[i] != '\0') {
        newFolder[i] = folder[i];
        i += 1;
    }
    newFolder[i] = '\0';

    free_to_free();
    init_to_free();

    parse_rooms(count, newFolder);

    free(newFolder);

    printf("-------------------------------- Done 4 --------------------------------\n");
    fflush(stdout);
    interface_set(-1);

    generate_nearby_chunks(1);

    printf("-------------------------------- Done 5 --------------------------------\n");

    if(!hardReset) {
        camx = oldx;
        camy = oldy;
        camz = oldz;
        player_chx = oldchx;
        player_chy = oldchy;
        generate_nearby_chunks(1);
        current_room = hashtbl_find_opt(visited, player_chx, player_chy);
        if(current_room == NULL) {
            current_room = hashtbl_find_opt(visited, 0, 0);
        }
    }
}

void processInput(GLFWwindow *window, float dtime) {
    // quit w
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
        glfwSetWindowShouldClose(window, true);
    }

    // horizontal movement
    bool pressed = false;
    if(!paused) {
        if(glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
            if(gamemode == 1) {
                for(int k = 0; k < 10; k++) {
                    camz -= creative_speed*cos(rot_hz)*cos(rot_vt)/10;
                    camx -= creative_speed*sin(rot_hz)*cos(rot_vt)/10;
                    camy += creative_speed*sin(rot_vt)/10;
                    if(is_colliding(dtime)) {
                        camz += creative_speed*cos(rot_hz)*cos(rot_vt)/10;
                        camx += creative_speed*sin(rot_hz)*cos(rot_vt)/10;
                        camy -= creative_speed*sin(rot_vt)/10;
                        k=11;
                    }
                }
            } else {
                camvz = -speed*cos(rot_hz);
                camvx = -speed*sin(rot_hz);
            }
            pressed = true;
            //camvy = vtmult*speed*sin(rot_vt);
        }
        if(glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
            if(gamemode == 1) {
                for(int k = 0; k < 10; k++) {
                    camx -= creative_speed*cos(rot_hz)/10;
                    camz += creative_speed*sin(rot_hz)/10;
                    if(is_colliding(dtime)) {
                        camx += creative_speed*cos(rot_hz)/10;
                        camz -= creative_speed*sin(rot_hz)/10;
                        k=11;
                    }
                }
            } else {
                camvx = -speed*cos(rot_hz);
                camvz = speed*sin(rot_hz);
            }
            pressed = true;
        }
        if(glfwGetKey(window, GLFW_KEY_Z) == GLFW_PRESS) {
            if(gamemode == 1) {
                for(int k = 0; k < 10; k++) {
                    camz += creative_speed*cos(rot_hz)*cos(rot_vt)/10;
                    camx += creative_speed*sin(rot_hz)*cos(rot_vt)/10;
                    camy -= creative_speed*sin(rot_vt)/10;
                    if(is_colliding(dtime)) {
                        camz -= creative_speed*cos(rot_hz)*cos(rot_vt)/10;
                        camx -= creative_speed*sin(rot_hz)*cos(rot_vt)/10;
                        camy += creative_speed*sin(rot_vt)/10;
                        k=11;
                    }
                }
            } else {
                camvz = speed*cos(rot_hz);
                camvx = speed*sin(rot_hz);
            }
            pressed = true;
            //camvy = -vtmult*speed*sin(rot_vt);
        }
        if(glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS) {
            if(gamemode == 1) {
                for(int k = 0; k < 10; k++) {
                    camx += creative_speed*cos(rot_hz)/10;
                    camz -= creative_speed*sin(rot_hz)/10;
                    if(is_colliding(dtime)) {
                        camx -= creative_speed*cos(rot_hz)/10;
                        camz += creative_speed*sin(rot_hz)/10;
                        k=11;
                    }
                }
            } else {
                camvx = speed*cos(rot_hz);
                camvz = -speed*sin(rot_hz);
            }
            pressed = true;
        }

        // jumps
        if(gamemode == 0 && njumps > 0 && glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) {
            if(jPress) {
                camvy = vtmult*speed;
                njumps -= 1;
                jPress = false;
            }
        } else {
            jPress = true;
        }
        if(!pressed) {
            camvx *= (1.0-1.0*5.0*((double)dtime));
            camvz *= (1.0-1.0*5.0*((double)dtime));
        }

        // gamemode
        if(glfwGetKey(window, GLFW_KEY_G) == GLFW_PRESS) {
            if(!gPress) {
                gPress = true;
                gamemode = 1-gamemode;
                njumps = 1;
                camvx = 0.0;
                camvy = 0.0;
                camvz = 0.0;
            }   
        } else {
            gPress = false;
        }

        // camera rotation
        if(glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS) {
            rot_hz -= sensitivity;
        }
        if(glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
            rot_hz += sensitivity;
        }

        if(glfwGetKey(window, GLFW_KEY_P) == GLFW_PRESS) {
            rot_vt = maxd(-3.14159/2.0, rot_vt-sensitivity);
        }
        if(glfwGetKey(window, GLFW_KEY_M) == GLFW_PRESS) {
            rot_vt = mind(3.14159/2.0, rot_vt+sensitivity);
        }

        // reset
        if(glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS) {
            if(!isMenuOpen() && !rPress) {
                rPress = true;
                hardReset = true;
                //reset_everything(window, 4, "levels/level_00/");
                //reset_everything(window, 7, "levels/level_01/");
                reset_everything(window, 1, "levels/level_02/");
            }   
        } else {
            rPress = false;
        }

        if(glfwGetKey(window, GLFW_KEY_T) == GLFW_PRESS) {
            if(!isMenuOpen() && !rPress) {
                rPress = true;
                hardReset = false;
                //reset_everything(window, 4, "levels/level_00/");
                //reset_everything(window, 7, "levels/level_01/");
                reset_everything(window, 1, "levels/level_02/");
            }   
        } else {
            rPress = false;
        }

        if(!isMenuOpen() && switchRoom) {
            //printf("-----\n");
            switchRoom = false;
            reset_everything(window, newRoomCount, newRoomName);
            //free(newRoomName);
        }
    }

    // pause
    if(glfwGetKey(window, GLFW_KEY_U) == GLFW_PRESS) {
        if(!isMenuOpen() && !pPress) {
            pPress = true;
            paused = !paused;
        }   
    } else {
        pPress = false;
    }
}

const char *vertexShaderSource = "#version 330 core\n"
    "layout (location = 0) in vec3 aPos;\n"
    "layout (location = 1) in vec2 aTex;\n"
    "uniform mat4 scale;\n"
    "uniform mat4 model;\n"
    "uniform mat4 view;\n"
    "uniform mat4 projection;\n"
    "out vec2 texCoord;\n"
    "void main() {\n"
    "   gl_Position = projection * view * model * scale * vec4(aPos, 1.0);\n"
    "   texCoord = aTex;\n"
    "}\0";

// Fragment Shader Source
const char *fragmentShaderSource = "#version 330 core\n"
    "uniform vec4 u_color;\n"
    "uniform mat2 translation;\n"
    "out vec4 FragColor;\n"
    "in vec2 texCoord;\n"
    "uniform sampler2D tex0;\n"
    "void main() {\n"
//  "   FragColor = u_color;\n"
    "   FragColor = texture(tex0, translation * texCoord) * u_color;\n"
    "}\0";

const char *vertexShaderSourceR = "#version 330 core\n"
    "layout (location = 0) in vec3 aPos;\n"
    "uniform mat4 scale;\n"
    "uniform mat4 slide;\n"
    "void main() {\n"
    "   gl_Position = slide * scale * vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
    "}\0";

const char *fragmentShaderSourceR = "#version 330 core\n"
    "uniform vec4 u_color2;\n"
    "out vec4 FragColor;\n"
    "void main() {\n"
    "   FragColor = u_color2;\n"
    "}\0";

typedef enum file_extension {PNG, JPG, JPEG} file_extension;
void generate_texture_2D(int id, char* filename, file_extension ext) {
    int widthImg, heightImg, numColCh;
    //unsigned char* bytes = stbi_load("res/container.jpg", &widthImg, &heightImg, &numColCh, 0);
    unsigned char* bytes = stbi_load(filename, &widthImg, &heightImg, &numColCh, 0);
    if(bytes == 0) {fprintf(stderr, "ERROR : cannot load texture\n"); exit(1);}
    glGenTextures(1, &textures[id]);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textures[id]);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    if(ext == JPG || ext == JPEG) {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, widthImg, heightImg, 0, GL_RGB, GL_UNSIGNED_BYTE, bytes);
        glGenerateMipmap(GL_TEXTURE_2D);
    } else if(ext == PNG) {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, widthImg, heightImg, 0, GL_RGBA, GL_UNSIGNED_BYTE, bytes);
        glGenerateMipmap(GL_TEXTURE_2D);
    }

    stbi_image_free(bytes);
    glBindTexture(GL_TEXTURE_2D, 0);
}

/*void generate_texture_3D(int id, char* filename, file_extension ext) {
    int widthImg, heightImg, depthImg, numColCh;
    //unsigned char* bytes = stbi_load("res/container.jpg", &widthImg, &heightImg, &numColCh, 0);
    unsigned char* bytes = stbi_load(filename, &widthImg, &heightImg, &numColCh, 0);
    if(bytes == 0) {fprintf(stderr, "ERROR : cannot load texture\n"); exit(1);}
    glGenTextures(1, &textures[id]);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_3D, textures[id]);

    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    if(ext == JPG || ext == JPEG) {
        glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB, widthImg, heightImg, depthImg, 0, GL_RGB, GL_UNSIGNED_BYTE, bytes);
        glGenerateMipmap(GL_TEXTURE_3D);
    } else if(ext == PNG) {
        glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, widthImg, heightImg, depthImg, 0, GL_RGBA, GL_UNSIGNED_BYTE, bytes);
        glGenerateMipmap(GL_TEXTURE_3D);
    }

    stbi_image_free(bytes);
    glBindTexture(GL_TEXTURE_2D, 0);
}*/

int main_alt() {
    // glfw: initialize and configure
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    // glfw window creation
    // --------------------
    GLFWwindow* window = glfwCreateWindow(1500, 1000, "yahoo", NULL, NULL);
    if (window == NULL)
    {
        fprintf(stderr, "ERROR : cannot initialize GL window");
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);

    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        fprintf(stderr, "ERROR : cannot initialize GLAD loader");
        return -1;
    }

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_FRONT); 
    glDepthFunc(GL_LESS);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);

    //glfwSetMouseButtonCallback(window, mouse_button_callback);

    init_csts();
    init_hashtbl();
    init_interf(window);
    build_all_menus();
    init_ent_generator(30);
    init_proj();
    init_to_free();
    parse_rooms(7, "templates/");

    // ---------------------------------------------------------------------------------------------------------------------------------------------- //
    // ---------------------------------------------------------------------------------------------------------------------------------------------- //

    // build and compile our shader program
    // ------------------------------------
    // vertex shader
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);
    // check for shader compile errors
    int success;
    char infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        fprintf(stderr, "ERROR : cannot initialize shader (1)");
    }

    // fragment shader
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);
    // check for shader compile errors
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        fprintf(stderr, "ERROR : cannot initialize shader (3)");
    }

    // link shaders
    unsigned int shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    // main VAO/VBO
    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
    glBindVertexArray(VAO);

    init_vertices();

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

    // position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    
    // texture coord attribute
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3*sizeof(float)));
    glEnableVertexAttribArray(1);

    printf("-----------------------------------------------------------------------------------------------\n"); fflush(stdout);
    // 16 max //

    //generate_texture_2D(0, "res/container.jpg", "jpg");
    generate_texture_2D(0, "res/white.png", PNG);
    generate_texture_2D(1, "res/question_block.png", PNG);
    generate_texture_2D(2, "res/exclamation_block.png", PNG);
    //generate_texture_2D(3, "res/money.png", PNG);
    generate_texture_2D(3, "res/coin2.png", JPG);
    generate_texture_2D(4, "res/container.jpg", JPG);
    //generate_texture_2D(4, "res/arrows.png", PNG);
    generate_texture_2D(5, "res/steel.jpeg", JPEG);
    generate_texture_2D(6, "res/gateway.jpeg", JPEG);
    generate_texture_2D(7, "res/lock.png", PNG);
    //generate_texture_2D(8, "res/beat_on.png", JPG);
    //generate_texture_2D(9, "res/beat_off.png", JPG);
    generate_texture_2D(8, "res/sq_full.png", PNG);
    generate_texture_2D(9, "res/sq_dotted.png", PNG);
    generate_texture_2D(10, "res/button_on.png", PNG);
    generate_texture_2D(11, "res/button_off.jpg", JPG);

    printf("-----------------------------------------------------------------------------------------------\n"); fflush(stdout);

    // ---------------------------------------------------------------------------------------------------------------------------------------------- //
    // ---------------------------------------------------------------------------------------------------------------------------------------------- //

    // build and compile our shader program
    // ------------------------------------
    // vertex shader
    unsigned int vertexShaderR = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShaderR, 1, &vertexShaderSourceR, NULL);
    glCompileShader(vertexShaderR);
    // check for shader compile errors
    glGetShaderiv(vertexShaderR, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(vertexShaderR, 512, NULL, infoLog);
        //std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // fragment shader
    unsigned int fragmentShaderR = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShaderR, 1, &fragmentShaderSourceR, NULL);
    glCompileShader(fragmentShaderR);
    // check for shader compile errors
    glGetShaderiv(fragmentShaderR, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(fragmentShaderR, 512, NULL, infoLog);
        //std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // link shaders
    unsigned int shaderProgramR = glCreateProgram();
    glAttachShader(shaderProgramR, vertexShaderR);
    glAttachShader(shaderProgramR, fragmentShaderR);
    glLinkProgram(shaderProgramR);
    // check for linking errors
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgramR, 512, NULL, infoLog);
        //std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
    }
    glDeleteShader(vertexShaderR);
    glDeleteShader(fragmentShaderR);

    // rectangle VAO/VBO
    initMenus();

    unsigned int RVBO, RVAO;
    glGenVertexArrays(1, &RVAO);
    glGenBuffers(1, &RVBO);
    // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
    glBindVertexArray(RVAO);

    glBindBuffer(GL_ARRAY_BUFFER, RVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(rectDefault), rectDefault, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    // note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
    glBindBuffer(GL_ARRAY_BUFFER, 0); 

    // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
    // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
    glBindVertexArray(0); 

    // ---------------------------------------------------------------------------------------------------------------------------------------------- //
    // ---------------------------------------------------------------------------------------------------------------------------------------------- //
    fffff = shaderProgram;

    int fps = 90;
    int interval = 1000000/fps;
    double slp_time = 1.0/fps;

    float delta = 0.0f;
    double deltad = 0.0;

    clock_t finish = clock();
    clock_t origin = clock();

    loc_tex = glGetUniformLocation(shaderProgram, "tex0");
    glUseProgram(shaderProgram);
    glUniform1i(loc_tex, 0);

    while(!glfwWindowShouldClose(window) && player_hp > 0) {
        // input
        // -----
        glClearColor(0.2f, 0.3f, 0.3f, 0.5f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        triCount = 0;

        fflush(stdout);
        generate_nearby_chunks(1);
        fflush(stdout);

        glUseProgram(shaderProgram);

        // draw the map
        glBindVertexArray(VAO);

        //printf("1\n"); fflush(stdout);
        gl_initRender(shaderProgram, shaderProgram, VAO, VBO);
        //printf("2\n"); fflush(stdout);
        gl_renderNearbyChunks(1);
        //printf("3\n"); fflush(stdout);
        gl_renderProj();
        //printf("4\n"); fflush(stdout);

        // draw data
        glUseProgram(shaderProgramR);
        glBindVertexArray(RVAO);

        gl_drawInteger(shaderProgramR, (int)(1.0f/(delta)), 0.0f, -0.92f, 0.05, 32, 255, 32, 0.005, -1);
        gl_drawInteger(shaderProgramR, triCount, 0.0f, 0.92f, 0.04f, 128, 128, 128, 0.005f, 1);
        gl_drawData(shaderProgramR);

        gl_drawDamageFadeout(shaderProgramR);

        if(!isInMenu(window, shaderProgramR)) {
            processInput(window, delta);
            if(!paused) {
                if(gamemode == 0) {
                    movePlayerG(delta);
                }
                teleport_on_edge();
                update_nearby_entities(delta, 1);
                updateProj(delta);
                update_buttons(delta);
            } else {
                gl_drawRectAlpha(shaderProgramR, -1.0f, -1.0f, 2.0f, 2.0f, 0, 0, 0, 128);
                gl_drawString(shaderProgramR, "game Paused", 0.0f,  0.1f, 0.08f, 128, 128, 255, 0.005f, 0);
                gl_drawString(shaderProgramR, "press u to resume", 0.0f, -0.1f, 0.08f, 128, 128, 255, 0.005f, 0);
            }
        }

        usleep(max(0, interval-(int)(1000000*delta)));
        sim_time += deltad;

        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        glfwSwapBuffers(window);
        glfwPollEvents();

        dmgCD = maxf(dmgCD - delta, 0.0f);
        if(lastDmg != player_hp) {
            lastDmg = player_hp;
            dmgCD = 0.5f;
        }
        finish = clock();
        delta = slp_time+((float)finish - (float)origin)/CLOCKS_PER_SEC;
        deltad = slp_time+((double)finish - (double)origin)/CLOCKS_PER_SEC;
        origin = clock();
        incr = 0.0f;
    }

    printf("| 1/5 |\n"); fflush(stdout);
    hashtbl_free(visited);
    printf("| 2/5 |\n"); fflush(stdout);
    free_proj();
    printf("| 3/5 |\n"); fflush(stdout);
    free_interf();
    printf("| 4/5 |\n"); fflush(stdout);
    free_pool();
    printf("| 5/5 |\n"); fflush(stdout);
    free_to_free();
    free_ent_generator();
    printf("Done\n"); fflush(stdout);

    // optional: de-allocate all resources once they've outlived their purpose:
    // ------------------------------------------------------------------------
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteTextures(1, &textures[0]);
    glDeleteTextures(1, &textures[1]);
    glDeleteProgram(shaderProgram);

    // glfw: terminate, clearing all previously allocated GLFW resources.
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}

int main(int argc, char** argv) {
    srand(time(NULL));
    triCount = 0;
    sim_time = 0.0;
    gamemode = 0;
    newRoomCount = 7;
    switchRoom = false;
    newRoomName = "templates/";
    //read_png_file("res/question_block.png");
    return main_alt();
}