#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 <string.h>

#include "hash.h"
#include "base.h"
#include "entities.h"
#include "generation.h"

hashtbl visited;
room* current_room;

int player_chx;
int player_chy;

entry* pool;
int pool_size;
int total_weight;

int coins;
int fct_entry_size;

bool is_one_room;

static char** to_free;
static int to_length;

fct_entry* hashtbl_entities;

void init_ent_generator(int n) {
    //!\\ size = 30 now
    hashtbl_entities = malloc(sizeof(fct_entry)*n);
    fct_entry_size = n;
    for(int k = 0; k < n; k++) {
        hashtbl_entities[k].id = (-1);
    }

    hashtbl_entities[0].id = 0;
    hashtbl_entities[0].tex = 3;
    hashtbl_entities[0].tex2 = 3;
    hashtbl_entities[0].name = "Coin";      // 0 = default
    hashtbl_entities[0].updatePos = &speen;
    hashtbl_entities[0].onHit = &money;
    hashtbl_entities[0].onDeath = NULL;

    hashtbl_entities[1].id = 1;
    hashtbl_entities[1].tex = 0;
    hashtbl_entities[1].tex2 = 0;
    hashtbl_entities[1].name = "ExplosiveStill";
    hashtbl_entities[1].updatePos = &speen2;
    hashtbl_entities[1].onHit = &explodeOnHit;
    hashtbl_entities[1].onDeath = NULL;

    hashtbl_entities[2].id = 2;
    hashtbl_entities[2].tex = 0;
    hashtbl_entities[2].tex2 = 0;
    hashtbl_entities[2].name = "ExplosiveSeek";
    hashtbl_entities[2].updatePos = &go_to_player;
    hashtbl_entities[2].onHit = &explodeOnHit;
    hashtbl_entities[2].onDeath = NULL;

    hashtbl_entities[3].id = 3;
    hashtbl_entities[3].tex = 0;
    hashtbl_entities[3].tex2 = 0;
    hashtbl_entities[3].name = "ExplosiveShoot";
    hashtbl_entities[3].updatePos = &speen3;
    hashtbl_entities[3].onHit = &explodeOnHit;
    hashtbl_entities[3].onDeath = NULL;

    hashtbl_entities[4].id = 4;
    hashtbl_entities[4].tex = 4;
    hashtbl_entities[4].tex2 = 4;
    hashtbl_entities[4].name = "SinePlatform";
    hashtbl_entities[4].updatePos = &moving_xyz;
    hashtbl_entities[4].onHit = &translatePlayer;
    hashtbl_entities[4].onDeath = NULL;

    hashtbl_entities[5].id = 5;
    hashtbl_entities[5].tex = 4;
    hashtbl_entities[5].tex2 = 4;
    hashtbl_entities[5].name = "LinePlatform";
    hashtbl_entities[5].updatePos = &moving_xyz_line;
    hashtbl_entities[5].onHit = &translatePlayerLine;
    hashtbl_entities[5].onDeath = NULL;

    hashtbl_entities[6].id = 6;
    hashtbl_entities[6].tex = 1;
    hashtbl_entities[6].tex2 = 1;
    hashtbl_entities[6].name = "TextBox";
    hashtbl_entities[6].updatePos = NULL;
    hashtbl_entities[6].onHit = &pop_text;
    hashtbl_entities[6].onDeath = NULL;

    hashtbl_entities[7].id = 7;
    hashtbl_entities[7].tex = 2;
    hashtbl_entities[7].tex2 = 2;
    hashtbl_entities[7].name = "WarpBox";
    hashtbl_entities[7].updatePos = NULL;
    hashtbl_entities[7].onHit = &pop_and_tp;
    hashtbl_entities[7].onDeath = NULL;

    hashtbl_entities[8].id = 8;
    hashtbl_entities[8].tex = 7;
    hashtbl_entities[8].tex2 = 7;
    hashtbl_entities[8].name = "Lock";
    hashtbl_entities[8].updatePos = &subtitle_text_box;
    hashtbl_entities[8].onHit = &locker;
    hashtbl_entities[8].onDeath = NULL;

    hashtbl_entities[9].id = 9;
    hashtbl_entities[9].tex = 8;
    hashtbl_entities[9].tex2 = 9;
    hashtbl_entities[9].name = "BeatBlock";
    hashtbl_entities[9].updatePos = &beating_block;
    hashtbl_entities[9].onHit = NULL;
    hashtbl_entities[9].onDeath = NULL;

    hashtbl_entities[10].id = 10;
    hashtbl_entities[10].tex = 4;
    hashtbl_entities[10].tex2 = 4;
    hashtbl_entities[10].name = "SpinningPlatform";
    hashtbl_entities[10].updatePos = &spinning_platform;
    hashtbl_entities[10].onHit = &spinning_translate;
    hashtbl_entities[10].onDeath = NULL;

    hashtbl_entities[11].id = 11;
    hashtbl_entities[11].tex = 10;
    hashtbl_entities[11].tex2 = 11;
    hashtbl_entities[11].name = "buttonInterface";
    hashtbl_entities[11].updatePos = NULL;
    hashtbl_entities[11].onHit = &update_button;
    hashtbl_entities[11].onDeath = NULL;

    hashtbl_entities[12].id = 12;
    hashtbl_entities[12].tex = 8;
    hashtbl_entities[12].tex2 = 9;
    hashtbl_entities[12].name = "buttonBlock";
    hashtbl_entities[12].updatePos = NULL;
    hashtbl_entities[12].onHit = NULL;
    hashtbl_entities[12].onDeath = NULL;
}

fct_entry* get_entry(int k0) {
    for(int k = 0; k < fct_entry_size; k++) {
        if(hashtbl_entities[k].id == k0) {
            return &(hashtbl_entities[k]);
        }
    }
    return NULL;
}

void copy_room(room* src, room* dest, int chx, int chy) {
    // considering dest has already been malloc'd
    dest->chunk_x = chx;
    dest->chunk_y = chy;
    dest->map = malloc(sizeof(cube_0*)*src->map_size);
    for(int k = 0; k < src->map_size; k++) {
        dest->map[k] = create_cube_0(
            src->map[k]->x, src->map[k]->y, src->map[k]->z, 
            src->map[k]->w, src->map[k]->h, src->map[k]->d,
            src->map[k]->hz_angle, src->map[k]->vt_angle, src->map[k]->red, src->map[k]->green, src->map[k]->blue 
        );
    }
    dest->map_size = src->map_size;
    dest->tps = malloc(sizeof(teleporter*)*src->tps_size);
    for(int k = 0; k < src->tps_size; k++) {
        dest->tps[k] = malloc(sizeof(teleporter));
        dest->tps[k]->hitbox = create_cube_0(
            src->tps[k]->hitbox->x, src->tps[k]->hitbox->y, src->tps[k]->hitbox->z, 
            src->tps[k]->hitbox->w, src->tps[k]->hitbox->h, src->tps[k]->hitbox->d,
            src->tps[k]->hitbox->hz_angle, src->tps[k]->hitbox->vt_angle, src->tps[k]->hitbox->red, src->tps[k]->hitbox->green, src->tps[k]->hitbox->blue 
        );
        dest->tps[k]->dest_chx = src->tps[k]->dest_chx + chx; 
        dest->tps[k]->dest_chy = src->tps[k]->dest_chy + chy; 
        dest->tps[k]->dest_x = src->tps[k]->dest_x;
        dest->tps[k]->dest_y = src->tps[k]->dest_y;
        dest->tps[k]->dest_z = src->tps[k]->dest_z;
    }
    dest->ents = malloc(sizeof(entity*)*src->ent_memlen);
    dest->ent_memlen = src->ent_memlen;
    dest->ent_len = src->ent_len;
    for(int k = 0; k < src->ent_len; k++) {
        dest->ents[k] = malloc(sizeof(entity));
        dest->ents[k]->entity_type = src->ents[k]->entity_type;
        dest->ents[k]->damage = src->ents[k]->damage;
        dest->ents[k]->tex = src->ents[k]->tex;
        dest->ents[k]->tex2 = src->ents[k]->tex2;
        //printf("!! %d !!\n", dest->ents [k]->tex);
        dest->ents[k]->hitpoints = malloc(sizeof(int));
        dest->ents[k]->metai1 = src->ents[k]->metai1;
        dest->ents[k]->metai2 = src->ents[k]->metai2;
        dest->ents[k]->metai3 = src->ents[k]->metai3;
        dest->ents[k]->metai4 = src->ents[k]->metai4;
        dest->ents[k]->metai5 = src->ents[k]->metai5;
        dest->ents[k]->metai6 = src->ents[k]->metai6;
        dest->ents[k]->metad1 = src->ents[k]->metad1;
        dest->ents[k]->metad2 = src->ents[k]->metad2;
        dest->ents[k]->metad3 = src->ents[k]->metad3;
        dest->ents[k]->metad4 = src->ents[k]->metad4;
        dest->ents[k]->metad5 = src->ents[k]->metad5;
        dest->ents[k]->metad6 = src->ents[k]->metad6;
        dest->ents[k]->metad7 = src->ents[k]->metad7;
        dest->ents[k]->metad8 = src->ents[k]->metad8;
        dest->ents[k]->metad9 = src->ents[k]->metad9;
        dest->ents[k]->metach1 = src->ents[k]->metach1;
        dest->ents[k]->metach2 = src->ents[k]->metach2;
        *(dest->ents[k]->hitpoints) = *(src->ents[k]->hitpoints);
        dest->ents[k]->pos = create_cube_0(
            (*(src->ents[k]->pos)).x, (*(src->ents[k]->pos)).y, (*(src->ents[k]->pos)).z,
            (*(src->ents[k]->pos)).w, (*(src->ents[k]->pos)).h, (*(src->ents[k]->pos)).d,
            (*(src->ents[k]->pos)).hz_angle, (*(src->ents[k]->pos)).vt_angle, (*(src->ents[k]->pos)).red, (*(src->ents[k]->pos)).green, (*(src->ents[k]->pos)).blue 
        );
        dest->ents[k]->updatePos = src->ents[k]->updatePos;
        dest->ents[k]->onHit = src->ents[k]->onHit;
        dest->ents[k]->onDeath = src->ents[k]->onDeath;
    }
    for(int k = src->ent_len; k < src->ent_memlen; k++) {
        dest->ents[k] = malloc(sizeof(entity));
        dest->ents[k]->hitpoints = malloc(sizeof(int));
        dest->ents[k]->pos = malloc(sizeof(cube_0));
    }
    dest->tps_size = src->tps_size;
}

void build_starting_chunk(int chx, int chy) {
    room* new = malloc(sizeof(room));
    new->chunk_x = chx;
    new->chunk_y = chy;
    new->map = malloc(sizeof(cube_0*)*9);
    new->map_size = 9;
    new->tps = malloc(sizeof(teleporter*)*4);
    new->tps_size = 4;

    new->map[0] = create_cube_0(0.0, 0.0, 0.0, 5.0, 1.0, 5.0, 0.0, 0.0, 255, 255, 255);
    new->map[1] = create_cube_0(0.0, 15.0, 0.0, 5.0, 1.0, 5.0, 0.0, 0.0, 255, 255, 255);
    new->map[2] = create_cube_0(0.0, 30.0, 0.0, 5.0, 1.0, 5.0, 0.0, 0.0, 255, 255, 255);
    new->map[3] = create_cube_0(0.0, 45.0, 0.0, 5.0, 1.0, 5.0, 0.0, 0.0, 255, 255, 255);
    new->map[4] = create_cube_0(0.0, 1.0, 0.0, 1.0, 5.0, 1.0, 0.0, 0.0, 255, 255, 128);
    new->map[5] = create_cube_0(4.0, 1.0, 0.0, 1.0, 5.0, 1.0, 0.0, 0.0, 255, 255, 128);
    new->map[6] = create_cube_0(0.0, 1.0, 4.0, 1.0, 5.0, 1.0, 0.0, 0.0, 255, 255, 128);
    new->map[7] = create_cube_0(4.0, 1.0, 4.0, 1.0, 5.0, 1.0, 0.0, 0.0, 255, 255, 128);
    new->map[8] = create_cube_0(10.0, 10.0, 10.0, 2.0, 2.0, 2.0, 0.0, 0.0, 255, 128, 255);

    new->tps[0] = create_teleporter(-1.0, 1.0, 2.0, 1.0, 2.0 + (double)(rand()%2), 1.0, 0.0, 0.0, 255, 0, 0  , chx-1, chy  , 2.5, 2.5, 2.5);
    new->tps[1] = create_teleporter(2.0, 1.0, -1.0, 1.0, 2.0 + (double)(rand()%2), 1.0, 0.0, 0.0, 255, 255, 0, chx  , chy-1, 2.5, 2.5, 2.5);
    new->tps[2] = create_teleporter(5.0, 1.0, 2.0, 1.0, 2.0 + (double)(rand()%2), 1.0, 0.0, 0.0, 0, 255, 0   , chx+1, chy  , 2.5, 2.5, 2.5);
    new->tps[3] = create_teleporter(2.0, 1.0, 5.0, 1.0, 2.0 + (double)(rand()%2), 1.0, 0.0, 0.0, 0, 0, 255   , chx  , chy+1, 2.5, 2.5, 2.5);

    new->ents = malloc(sizeof(entity*)*32);
    for(int k = 0; k < 32; k++) {
        new->ents[k] = malloc(sizeof(entity));
        new->ents[k]->hitpoints = malloc(sizeof(int));
        new->ents[k]->tex = 0;
        new->ents[k]->pos = malloc(sizeof(cube_0));
    };
    new->ent_len = 1;
    new->ent_memlen = 32;
    fill_cube_0(new->ents[0]->pos, -0.25, 8.25, -0.25, 0.5, 0.5, 0.5, 0.0, 0.0, 193, 128, 0);
    new->ents[0]->damage = 0;
    *(new->ents[0]->hitpoints) = 5;
    new->ents[0]->onDeath = NULL;
    new->ents[0]->onHit = *detectHit;
    new->ents[0]->updatePos = *speen2;

    hashtbl_add(visited, chx, chy, new);
}

void init_hashtbl() {
    printf("+1\n"); fflush(stdout);
    visited = hashtbl_generate(1789);
    printf("+1\n"); fflush(stdout);
    //generate_nearby_chunks(0);
    //build_starting_chunk(player_chx, player_chy);
    printf("+1\n"); fflush(stdout);
    //current_room = hashtbl_find_opt(visited, player_chx, player_chy);
    printf("+1\n"); fflush(stdout);
    total_weight = 0;
    printf("+1\n"); fflush(stdout);
}

void get_number_blocks(int* ret_cubes, int* ret_tps, int* ret_ent, FILE* ptr) {
    *ret_cubes = 0;
    *ret_tps = 0;
    *ret_ent = 0;

    int in_blocks = 0;
    char c = fgetc(ptr);
    while(c != EOF && c != '$') {
        //printf("%c", c);
        if(c == 'B') {
            in_blocks = 0;
        } else if(c == 'T') {
            in_blocks = 1;
        } else if(c == 'E') {
            in_blocks = 2;
        } else if(c == '[') {
            if(in_blocks == 0) {
                *ret_cubes += 1;
            } else if(in_blocks == 1) {
                *ret_tps += 1;
            } else {
                *ret_ent += 1;
            }
        }
        c = fgetc(ptr);
    }

    fclose(ptr);
}

void align_to(FILE* ptr, char ch) {
    char c = fgetc(ptr);
    while(c != EOF && c != ch) {
        c = fgetc(ptr);
    }
}

int is_neg0 = 1;
int read_int(FILE* ptr) {
    bool is_reading = false;
    int buffer = 0;
    int sign = 1;
    char c = fgetc(ptr);
    while(c != EOF) {
        if(c == '-') {
            sign = -1;
        } else if((int)c >= 48 && (int)c <= 57) {
            is_reading = true;
            buffer = 10*buffer + (int)c - 48;
        } else if(is_reading) {
            if(sign == -1 && buffer == 0) {
                is_neg0 = -1;
            } else {
                is_neg0 = 1;
            }
            return buffer*sign;
        }
        c = fgetc(ptr);
    }
    if(sign == -1 && buffer == 0) {
        is_neg0 = -1;
    } else {
        is_neg0 = 1;
    }
    return buffer*sign;
}

char* read_string(FILE* ptr) {
    char* res0 = malloc(sizeof(char)*52);
    char c = fgetc(ptr);
    int i = 0;
    while(c != EOF && c == ' ') { // ignore initial spaces
        c = fgetc(ptr);
    }
    while(c != EOF && c != ',') {
        res0[i] = c;
        i += 1;
        c = fgetc(ptr);
    }
    res0[i] = '\0';
    return res0;
}

double sign(double __x) {
    if(__x >= 0.0) {
        return 1.0;
    }
    return -1.0;
}

double read_float(FILE* ptr) {
    int ent = read_int(ptr);
    int sn0 = is_neg0;
    int frac = read_int(ptr);
    //printf("%d.%d; ", ent, frac);
    if(ent != 0.0) {
        return sn0*(ent/abs(ent))*(absf((double)ent) + ((double)frac)/(pow(10.0, (double)ln_baseN(frac, 10))));
    } else {
        //printf("%d, %d\n", ent, frac);
        return sn0*((double)frac)/(pow(10.0, (double)ln_baseN(frac, 10)));
    }
}

void parse_one_room(int id, char* filename) {
    //printf("    parsing %s...", filename);
    FILE* ptr = fopen(filename, "r");

    pool[id].area = malloc(sizeof(room));
    int ncubes;
    int ntps;
    int nent;

    FILE* ptr2 = fopen(filename, "r");
    get_number_blocks(&ncubes, &ntps, &nent, ptr2);
    printf("(%d, %d, %d)\n", ncubes, ntps, nent);

    int nmemlen = maxd(nent, 64);

    pool[id].area->map = malloc(sizeof(cube_0*)*ncubes);
    pool[id].area->map_size = ncubes;
    pool[id].area->tps = malloc(sizeof(teleporter*)*ntps);
    for(int k = 0; k < ntps; k++) {
        pool[id].area->tps[k] = malloc(sizeof(teleporter));
    }
    pool[id].area->tps_size = ntps;
    pool[id].area->ents = malloc(sizeof(entity*)*nmemlen);
    for(int k = 0; k < nmemlen; k++) {
        pool[id].area->ents[k] = malloc(sizeof(entity));
        pool[id].area->ents[k]->hitpoints = malloc(sizeof(int));
        pool[id].area->ents[k]->pos = malloc(sizeof(cube_0));
        pool[id].area->ents[k]->tex = 0;
    }
    pool[id].area->ent_len = nent;
    pool[id].area->ent_memlen = nmemlen;

    printf("0/3...\n");
    fflush(stdout);

    for(int k = 0; k < ncubes; k++) {
        align_to(ptr, '[');
        double cx = read_float(ptr);
        double cy = read_float(ptr);
        double cz = read_float(ptr);
        double cw = read_float(ptr);
        double ch = read_float(ptr);
        double cd = read_float(ptr);
        double chz = read_float(ptr);
        double cvt = read_float(ptr);
        int red = read_int(ptr);
        int green = read_int(ptr);
        int blue = read_int(ptr);
        pool[id].area->map[k] = create_cube_0(cx, cy, cz, cw, ch, cd, chz, cvt, red, green, blue);
        //printf("\n");
    }

    printf("1/3...\n");
    fflush(stdout);

    for(int k = 0; k < ntps; k++) {
        align_to(ptr, '[');
        double cx = read_float(ptr);
        double cy = read_float(ptr);
        double cz = read_float(ptr);
        double cw = read_float(ptr);
        double ch = read_float(ptr);
        double cd = read_float(ptr);
        double chz = read_float(ptr);
        double cvt = read_float(ptr);
        int red = read_int(ptr);
        int green = read_int(ptr);
        int blue = read_int(ptr);
        pool[id].area->tps[k]->hitbox = create_cube_0(cx, cy, cz, cw, ch, cd, chz, cvt, red, green, blue);
        pool[id].area->tps[k]->dest_chx = read_int(ptr);
        pool[id].area->tps[k]->dest_chy = read_int(ptr);
        //printf("\n");
    }

    printf("2/3...\n");
    fflush(stdout);

    for(int k = 0; k < nent; k++) {
        align_to(ptr, '[');
        double cx = read_float(ptr);
        double cy = read_float(ptr);
        double cz = read_float(ptr);
        double cw = read_float(ptr);
        double ch = read_float(ptr);
        double cd = read_float(ptr);
        double chz = read_float(ptr);
        double cvt = read_float(ptr);
        int red = read_int(ptr);
        int green = read_int(ptr);
        int blue = read_int(ptr);
        int hp = read_int(ptr);
        int dmg = read_int(ptr);
        int fid = read_int(ptr);
        fct_entry* entry = get_entry(fid);
        if(entry == NULL) {
            entry = get_entry(0);
        }
        //printf(">>><>>>>><> %d\n", entry->tex);
        fill_cube_0(pool[id].area->ents[k]->pos, cx, cy, cz, cw, ch, cd, chz, cvt, red, green, blue);
        pool[id].area->ents[k]->damage = dmg;
        pool[id].area->ents[k]->tex = entry->tex;
        pool[id].area->ents[k]->tex2 = entry->tex2;
        *(pool[id].area->ents[k]->hitpoints) = hp;
        pool[id].area->ents[k]->updatePos = entry->updatePos;
        pool[id].area->ents[k]->onHit = entry->onHit;
        pool[id].area->ents[k]->onDeath = entry->onDeath;
        pool[id].area->ents[k]->entity_type = entry->id;
        pool[id].area->ents[k]->metai1 = entry->metai1;
        pool[id].area->ents[k]->metai2 = entry->metai2;
        pool[id].area->ents[k]->metai3 = entry->metai3;
        pool[id].area->ents[k]->metai4 = entry->metai4;
        pool[id].area->ents[k]->metai5 = entry->metai5;
        pool[id].area->ents[k]->metai6 = entry->metai6;
        pool[id].area->ents[k]->metad1 = entry->metad1;
        pool[id].area->ents[k]->metad2 = entry->metad2;
        pool[id].area->ents[k]->metad3 = entry->metad3;
        pool[id].area->ents[k]->metad4 = entry->metad4;
        pool[id].area->ents[k]->metad5 = entry->metad5;
        pool[id].area->ents[k]->metad6 = entry->metad6;
        pool[id].area->ents[k]->metad7 = entry->metad7;
        pool[id].area->ents[k]->metad8 = entry->metad8;
        pool[id].area->ents[k]->metad9 = entry->metad9;
        if(entry->id == 4) {
            // sine platform
            double ccw = read_float(ptr);
            double cch = read_float(ptr);
            double ccd = read_float(ptr);
            int mult = read_int(ptr);
            int divd = read_int(ptr);
            int phase = read_int(ptr);
            pool[id].area->ents[k]->metad1 = cx;
            pool[id].area->ents[k]->metad2 = cy;
            pool[id].area->ents[k]->metad3 = cz;
            pool[id].area->ents[k]->metad4 = ccw;
            pool[id].area->ents[k]->metad5 = cch;
            pool[id].area->ents[k]->metad6 = ccd;
            pool[id].area->ents[k]->metai1 = mult;
            pool[id].area->ents[k]->metai2 = divd;
            pool[id].area->ents[k]->metai3 = phase;
        } else if(entry->id == 5) {
            // linear platform
            double amp_x = read_float(ptr);
            double amp_y = read_float(ptr);
            double amp_z = read_float(ptr);
            double speed_x = read_float(ptr);
            double speed_y = read_float(ptr);
            double speed_z = read_float(ptr);
            printf("%lf %lf %lf %lf %lf %lf\n", amp_x, amp_y, amp_z, speed_x, speed_y, speed_z);
            pool[id].area->ents[k]->metad1 = cx;
            pool[id].area->ents[k]->metad2 = cy;
            pool[id].area->ents[k]->metad3 = cz;
            pool[id].area->ents[k]->metad4 = speed_x;
            pool[id].area->ents[k]->metad5 = speed_y;
            pool[id].area->ents[k]->metad6 = speed_z;
            pool[id].area->ents[k]->metad7 = amp_x;
            pool[id].area->ents[k]->metad8 = amp_y;
            pool[id].area->ents[k]->metad9 = amp_z;
            pool[id].area->ents[k]->metai1 = 1;
            pool[id].area->ents[k]->metai2 = 1;
            pool[id].area->ents[k]->metai3 = 1;
        } else if(entry->id == 6) {
            // text box
            char* msg = read_string(ptr);
            to_free[to_length] = msg;
            to_length += 1;
            pool[id].area->ents[k]->metach1 = msg;
            int ired = read_int(ptr);
            int igreen = read_int(ptr);
            int iblue = read_int(ptr);
            pool[id].area->ents[k]->metai1 = build_text_box(msg, ired, igreen, iblue);
            pool[id].area->ents[k]->metai3 = (-727); // random value to recognize
        } else if(entry->id == 7) {
            // warp text box
            char* dest = read_string(ptr);
            to_free[to_length] = dest;
            to_length += 1;
            int count = read_int(ptr);
            char* msg = read_string(ptr);
            to_free[to_length] = msg;
            to_length += 1;
            pool[id].area->ents[k]->metach1 = dest;
            pool[id].area->ents[k]->metach2 = msg;
            int ired = read_int(ptr);
            int igreen = read_int(ptr);
            int iblue = read_int(ptr);
            pool[id].area->ents[k]->metai1 = build_text_box(msg, ired, igreen, iblue);
            pool[id].area->ents[k]->metai2 = count;
            pool[id].area->ents[k]->metai3 = (-72727); // random value to recognize
        } else if(entry->id == 8) {
            // lock box
            int cost = read_int(ptr); // assume cost > 0
            int doPay = read_int(ptr);

            //printf("%d %d --------<>---------\n", cost, doPay);
            
            int ired = read_int(ptr);
            int igreen = read_int(ptr);
            int iblue = read_int(ptr);

            int nsize = 1+ln_baseN(cost, 10);
            // char* test = "you need 0 to pass"; char[19] //
            char* msg = malloc(sizeof(int)*(nsize+25));
            to_free[to_length] = msg;
            to_length += 1;
            msg[0] = 'y'; msg[1] = 'o'; msg[2] = 'u'; msg[3] = ' ';
            msg[4] = 'n'; msg[5] = 'e'; msg[6] = 'e'; msg[7] = 'd'; msg[8] = ' ';
            int ki = 9;
            int csize = nsize-1;
            int left = reverse(cost);
            while(csize > 0) {
                msg[ki] = (char)(48 + left%10);
                left = left/10;
                csize -= 1;
                ki += 1;
            }
            // at the end, k=10 if cost has one single digit (<==> nsize = 1)

            msg[ki] = ' '; ki += 1;
            msg[ki] = 'c'; ki += 1;
            msg[ki] = 'o'; ki += 1;
            msg[ki] = 'i'; ki += 1;
            msg[ki] = 'n'; ki += 1;
            msg[ki] = 's'; ki += 1;
            msg[ki] = ' '; ki += 1;
            msg[ki] = 't'; ki += 1;
            msg[ki] = 'o'; ki += 1;
            msg[ki] = ' '; ki += 1;
            msg[ki] = 'p'; ki += 1;
            msg[ki] = 'a'; ki += 1;
            msg[ki] = 's'; ki += 1;
            msg[ki] = 's'; ki += 1;
            msg[ki] = '\0';
            // k=18 if single digit
            
            //pool[id].area->ents[k]->metai1 = build_text_box(msg, ired, igreen, iblue);
            pool[id].area->ents[k]->metai2 = cost;
            pool[id].area->ents[k]->metai3 = doPay;
            pool[id].area->ents[k]->metach1 = msg;
        } else if(entry->id == 9) {
            // beat block
            double ontime = read_float(ptr);
            double offtime = read_float(ptr);
            int stst = read_int(ptr);
            if(stst != 0 && stst != 1) {
                stst = 1;
            }

            pool[id].area->ents[k]->metad1 = ontime;
            pool[id].area->ents[k]->metad2 = offtime;
            pool[id].area->ents[k]->metad3 = 0.0;
            pool[id].area->ents[k]->metai1 = 1-stst;
        } else if(entry->id == 10) {
            // spinning platform
            double hzspd = read_float(ptr);
            double vtspd = read_float(ptr);

            pool[id].area->ents[k]->metad1 = hzspd;
            pool[id].area->ents[k]->metad2 = vtspd;
        } else if(entry->id == 11) {
            int freq = read_int(ptr);
            double acttime = read_float(ptr);

            if(freq < 0 || freq >= 16) {
                fprintf(stderr, "WARNING : invalid frequency for button : %d\n", freq);
                fprintf(stderr, "using frequency 0\n");
                freq = 0;
            }

            pool[id].area->ents[k]->metai1 = freq;
            pool[id].area->ents[k]->metad1 = acttime;
        } else if(entry->id == 12) {
            int freq = read_int(ptr);
            int offstate = read_int(ptr);

            if(freq < 0 || freq >= 16) {
                fprintf(stderr, "WARNING : invalid frequency for button : %d\n", freq);
                fprintf(stderr, "using frequency 0\n");
                freq = 0;
            }

            pool[id].area->ents[k]->metai1 = freq;
            pool[id].area->ents[k]->metai2 = offstate;
        } else {
            pool[id].area->ents[k]->metai3 = 0;
        }
    }

    printf("3/3...\n");
    fflush(stdout);

    align_to(ptr, 'W');

    pool[id].weight = read_int(ptr);
    total_weight += pool[id].weight;

    printf("(w = %d) OK\n", pool[id].weight);
    fflush(stdout);
    printf("\n\n");

    fclose(ptr);
}

char* get_name_and_i(char* folder, int nrooms, int* reti) {
    int len = 0;
    while(folder[len] != '\0') {
        len+=1;
    }
    int lnn = 1+ln_baseN(nrooms, 10);
    int str_size = len+lnn+5;
    char* res = malloc(sizeof(char)*str_size);
    for(int k = 0; k < len; k++) {
        res[k] = folder[k];
    }
    res[len  ] = 'r';
    res[len+1] = 'o';
    res[len+2] = 'o';
    res[len+3] = 'm';
    res[len+4] = '_';
    res[len+5] = '0';
    for(int k = len+6; k < str_size; k++) {
        res[k] = '\0';
    }

    *reti = len+5;
    return res;
}

void parse_rooms(int n_rooms, char* folder) {
    int id = 0;
    char* name = get_name_and_i(folder, n_rooms, &id);

    pool = malloc(sizeof(entry)*n_rooms);
    pool_size = n_rooms;

    printf("<%s> with %d rooms\n", folder, n_rooms);
    printf("Parsing...\n");

    for(int k = 0; k < n_rooms; k++) {
        printf("parsing %d...", k);
        if(k < 10) {
            name[id] =   (char)(k%10 + 48);
        } else if(k < 100) {
            name[id] =   (char)((k/10)%10 + 48);
            name[id+1] = (char)(k%10 + 48);
        } else if(k < 1000) {
            name[id] =   (char)((k/100)%10 + 48);
            name[id+1] = (char)((k/10)%10 + 48);
            name[id+2] = (char)(k%10 + 48);
        }
        parse_one_room(k, name);
        printf("done.\n");
    }

    printf("\nDone.\n");
    free(name);
    printf("Total sum : %d\n", total_weight);

    if(total_weight == 0) {
        // avoid floating point exception
        total_weight = 1;
        is_one_room = true;
    } else {
        is_one_room = false;
    }

    generate_nearby_chunks(0);
    current_room = hashtbl_find_opt(visited, player_chx, player_chy);
}

// has to be a multiple of both room_width and room_depth
int divider = 4;

// unused
void generate_terrain(room* r) {
    int rsize = 4*(room_width/divider)*(room_width/divider)*(room_depth/divider)*(room_depth/divider); // floor size (with 1x1 cubes)

    cube_0** newMap = malloc(sizeof(cube_0*)*(rsize+r->map_size));
    for(int k = 0; k < rsize+r->map_size; k++) {
        newMap[k] = malloc(sizeof(cube_0));
    }

    for(int k = 0; k < r->map_size; k++) {
        copy_cube(r->map[k], newMap[k]);
        free(r->map[k]);
    }
    free(r->map);

    // use whatever noise funtcion here
    int i = r->map_size;
    for(int l = -room_width; l < room_width; l+=divider) {
        for(int d = -room_depth; d < room_depth; d+=divider) {
            fill_cube_0(newMap[i], (double)l, -20.0, (double)d, (double)divider, 1.0, (double)divider, 0.0, 0.0,32, 128, 32);
            i+=1;
        }
    }

    r->map = newMap;
    r->map_size += rsize;
}

void generate_nearby_chunks(int render_dist) {
    for(int w = -render_dist; w <= render_dist; w++) {
        for(int h = -render_dist; h <= render_dist; h++) {
            if(!hashtbl_mem(visited, player_chx + w, player_chy + h)) {
                printf("generating (%d, %d)... ", player_chx + w, player_chy + h); fflush(stdout);
                //build_starting_chunk(player_chx + w, player_chy + h);
                int pick = rand()%(abs(total_weight));
                if(w == 0 && h == 0) {          // 1st room will always be room_0
                    pick = -1;
                }
                printf("R = %d ", pick); fflush(stdout);
                int sum = 0;
                bool has_spawned = false;
                for(int k = 0; k < pool_size; k++) {
                    sum += pool[k].weight;
                    if(pick < sum) {
                        printf("chose %d\n", k); fflush(stdout);
                        has_spawned = true;
                        room* new = malloc(sizeof(room));
                        copy_room(pool[k].area, new, player_chx + w, player_chy + h);
                        //generate_terrain(new);
                        hashtbl_add(visited, player_chx + w, player_chy + h, new);
                        k = pool_size+1;
                    }
                }
                if(!has_spawned) {
                    printf("chose NULL\n"); fflush(stdout);
                    hashtbl_add(visited, player_chx + w, player_chy + h, NULL);
                }
                printf("Done\n");
            }
        }
    }
}

void free_pool() {
    for(int k0 = 0; k0 < pool_size; k0++) {
        printf("%d/%d\n", 1+k0, pool_size); fflush(stdout);
        for(int k = 0; k < pool[k0].area->map_size; k++) {
            printf("%d-%d\n", k, pool[k0].area->map_size);
            free(pool[k0].area->map[k]);
        }
        printf("...\n"); fflush(stdout);
        for(int k = 0; k < pool[k0].area->tps_size; k++) {
            free(pool[k0].area->tps[k]->hitbox);
            free(pool[k0].area->tps[k]);
        }
        printf("...\n"); fflush(stdout);
        for(int k = 0; k < pool[k0].area->ent_memlen; k++) {
            if(pool[k0].area->ents[k]->metai3 == -727) { // 6 and 7 //
                //free(pool[k0].area->ents[k]->metach1);
            }
            if(pool[k0].area->ents[k]->metai3 == -72727) { // 7 //
                //free(pool[k0].area->ents[k]->metach1);
                //free(pool[k0].area->ents[k]->metach2);
            }
            free(pool[k0].area->ents[k]->hitpoints);
            free(pool[k0].area->ents[k]->pos);
            free(pool[k0].area->ents[k]);
        }
        printf("...\n"); fflush(stdout);
        free(pool[k0].area->ents);
        free(pool[k0].area->tps);
        free(pool[k0].area->map);
        free(pool[k0].area);
    }
    free(pool);
}

void init_to_free() {
    to_free = malloc(sizeof(char*)*100); // ------------------------------------------------------------------------------------------------- 100 max //
    to_length = 0;
}

void free_to_free() {
    for(int k = 0; k < to_length; k++) {
        free(to_free[k]);
    }
    free(to_free);

}

void free_ent_generator() {
    free(hashtbl_entities);
}