#include "mini.h"
#include "version.h"

#include "surf/surf.h"
#include "pal/pal.h"
#include "font/font.h"
#include "map/map.h"
#include "draw/draw.h"
#include "win/win.h"
#include "view/view.h"
#include "shader/shader.h"
#include "audio/audio.h"
#include "mouse/mouse.h"
#include "key/key.h"
#include "pad/pad.h"

#include "file/file.h"
#include "lua/lua.h"
#include "other/log.h"

#include "backends/backend.h"

#include <string.h>


#define UPDATE_ALWAYS   0
#define UPDATE_WAIT     1
#define UPDATE_TIMEOUT  2

#ifdef MACOS_BUNDLE
#include <libgen.h>
#endif

char        config_folder[256];
char        main_lua_file[200] = "main.lua";
bool        override_ini = false;


int update_mode = UPDATE_ALWAYS;
int timeout = 0;

int         fps=0;
uint32_t    last_update = 0;
float       delta_time = 0.0f;

int16_t beats, num_beats = 0;

void createNewProject();

char* get_value_from_line(char* line) {
    char* equal_character = strchr(line, '=');
    if (equal_character == NULL) return NULL;
    *equal_character = '\0';
    return ++equal_character;
}

void read_ini() {
    int size;
    FILE *f = mini::file::getfilepointer("game.ini", size); // fopen("game.ini", "r");
    char line[1024];
    if (f == NULL) { log_msg(LOG_FAIL, "No s'ha pogut obrir 'game.ini'\n"); exit(-1); }
    log_msg(LOG_OK, "'game.ini' carregat\n");
    while (fgets(line, sizeof(line), f)) {
        char *value = get_value_from_line(line);
        if (value != NULL) {
            value[strlen(value)-1] = '\0';
            if (strcmp(line, "title") == 0) { strcpy(mini::win::state.title, value); log_msg(LOG_VERBOSE, "title = %s\n", mini::win::state.title); }
            else if (strcmp(line, "config") == 0) { strcpy(config_folder, value); log_msg(LOG_VERBOSE, "config = %s\n", config_folder); }
            else if (strcmp(line, "width") == 0) { mini::win::state.width = atoi(value); log_msg(LOG_VERBOSE, "screen width = %i\n", mini::win::state.width); }
            else if (strcmp(line, "height") == 0) { mini::win::state.height = atoi(value); log_msg(LOG_VERBOSE, "screen height = %i\n", mini::win::state.height); }
            else if (strcmp(line, "zoom") == 0) { mini::win::state.zoom = atoi(value); log_msg(LOG_VERBOSE, "screen zoom = %i\n", mini::win::state.zoom); }
            else if (strcmp(line, "fullscreen") == 0) { mini::win::state.fullscreen = atoi(value); log_msg(LOG_VERBOSE, "screen sullscreen = %i\n", mini::win::state.fullscreen); }
            //else if (strcmp(line, "files") == 0) {
                //lua_files = (char*)malloc(strlen(value));
            //    strcpy(lua_files, value);
            //}
        }
    }
    fclose(f);
    //SDL_Log("'game.ini' carregat!\n");
}

#define CURRENT(x, y) surfaces[i].p[(x)+(y)*surfaces[i].w]

namespace mini
{


    namespace sys
    {
        float delta() {
            return delta_time;
        }

        float time() {
            return float(backend::get_time_ms())/1000.0f;
        }

        bool beat(int16_t i) {
            if (i<0) {
                return beats==0;
            } else {
                beats=num_beats=i;
                return false;
            }
        }

        int getfps() {
            return fps;
        }

        namespace update {
            void set(const int value, const int t) {
                update_mode = value;
                timeout = t;
            }
            int get() {
                return update_mode;
            }
        }

        void exit() {
            backend::exit();
        }
    }


}



using namespace mini;

int main(int argc,char*argv[]){
    
    //#ifdef DEBUG
    //    SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG);
        log_msg(LOG_UNSALTED, "MINI v%s\n",MINI_VERSION);
    //#endif

    // Gestió de arguments
    // ===============================================================
    if (argc>1)
    {
        if (argv[1][0]=='-' && argv[1][1]=='-') {
            const char *command = &argv[1][2];
            if (strcmp(command, "new")==0) {
                createNewProject();
                exit(0);
            } else if (strcmp(command, "version")==0) {
                exit(0);
            }
        } else if (strstr(argv[1], ".lua")!=nullptr) {
            file::setresourcefolder("./");
            file::setsource(SOURCE_FOLDER);
            strcpy(main_lua_file, argv[1]);
            strcpy(mini::win::state.title, argv[1]);
            override_ini = true;
        } else if (strstr(argv[1], ".jf2")!=nullptr) {
            file::setresourcefilename(argv[1]);
            file::setsource(SOURCE_FILE);
        } else {
            char path[256] = "./";
            strcat(path, argv[1]);
            file::setresourcefolder(path);
        }
    }
    #ifdef MACOS_BUNDLE
        char res_file[255] = "";
        strcpy(res_file, dirname(argv[0]));
        strcat(res_file, "/../Resources/data.jrf");
        file::setresourcefilename(res_file);
    #endif

    while (backend::state()!=backend::quitting) {

        // READ INI
        if (!override_ini)
        {
            read_ini();
            file::setconfigfolder(config_folder);
            const char *zoom = file::getconfigvalue("zoom");
            if (zoom) win::state.zoom=atoi(zoom);
            const char *fullscreen = file::getconfigvalue("fullscreen");
            if (fullscreen) win::state.fullscreen=strcmp(fullscreen, "true")==0?true:false;
            const char *cursor = file::getconfigvalue("cursor");
            if (cursor) win::state.cursor=strcmp(cursor, "true")?true:false;
            const char *music_enabled = file::getconfigvalue("music");
            if (music_enabled) audio::music::enable::set(strcmp(music_enabled, "true")==0?true:false);
            const char *sound_enabled = file::getconfigvalue("sound");
            if (sound_enabled) audio::sound::enable::set(strcmp(sound_enabled, "true")==0?true:false);
        }

        backend::init();

        log_msg(LOG_INFO, "STARTING A SYSTEM REINITIALIZATION\n");
        win::init();
        pad::init();
        audio::init();
        view::init();
        draw::init();
        surf::init();
        pal::init();
        font::init();

        lua::init(main_lua_file);
        lua::callbacks::init();

        uint32_t dt = backend::get_time_ms();

        backend::input::reset();
        
        int         fps_counter=0;
        uint32_t    fps_timer=0;

        while(backend::state()==backend::running) {

            backend::poll_events();

            // Do update
            if (backend::get_time_ms()-dt>13) {
                dt = backend::get_time_ms();
                
                if (lua::running()) {
                    delta_time = float(backend::get_time_ms() - last_update)/1000.0f;
                    last_update = backend::get_time_ms();

                    lua::callbacks::update();
                } else {
                    exception_loop();
                }

                if (beats == 0) beats = num_beats;
                if (beats > 0) beats--;

                backend::input::reset();
            }

            backend::video::render();

            fps_counter++;
            if (backend::get_time_ms()>=(fps_timer+1000)) {
                fps = fps_counter;
                fps_counter=0;
                fps_timer = backend::get_time_ms();
            }
        }

        font::quit();
        lua::quit();
        audio::quit();
        surf::quit();
        map::quit();
        win::quit();
        backend::quit();
    }

    lua::cleanup();
    return 0;
}


void createNewProject() {
    if (mini::file::createFolder("data")) {
        log_msg(LOG_OK, "Directori 'data' creat\n");
    } else {
        log_msg(LOG_FAIL, "No s'ha pogut crear la carpeta 'data'\n");
        exit(-1);
    }

    FILE *f = fopen("./data/game.ini", "w");
    if (f) {
        log_msg(LOG_OK, "Arxiu 'data/game.ini' creat\n");
    } else {
        log_msg(LOG_FAIL, "No s'ha pogut crear l'arxiu 'data/game.ini'\n");
        exit(-1);
    }
    fprintf(f, "title=NEW MINI PROJECT\nconfig=newminiproject\nwidth=320\nheight=240\nzoom=2\n");
    fclose(f);

    f = fopen("./data/main.lua", "w");
    if (f) {
        log_msg(LOG_OK, "Arxiu 'data/main.lua' creat\n");
    } else {
        log_msg(LOG_FAIL, "No s'ha pogut crear l'arxiu 'data/main.lua'\n");
        exit(-1);
    }
    fprintf(f, "function mini.init()\n\nend\n\nfunction mini.update()\n    surf.cls(0)\nend\n");
    fclose(f);

    log_msg(LOG_OK, "Projecte nou creat. Ja està fet lo més dificil del jailgame!\n");
}
