#include <emu.h>
#include <stdio.h>
#include <cart.h>
#include <cpu.h>
#include <ui.h>
#include <timer.h>
#include <dma.h>
#include <ppu.h>
#include <audio.h>
#include <cart.h>
#include <debug.h>
#include <serial.h>

#ifdef _WIN32
#include <windows.h>
#else
#include <pthread.h>
#include <unistd.h>
#include <sys/stat.h>
#endif

static emu_context ctx;
#ifdef __windows__
static HANDLE thread;
#else
static pthread_t thread;
#endif

emu_context *emu_get_context() {
    return &ctx;
}

#ifdef _WIN32
DWORD WINAPI cpu_run(void *p) {
#else
void *cpu_run(void *p) {
#endif

    ctx.running = true;
    ctx.paused = false;
    ctx.ticks = 0;

    while (ctx.running) {
        //if (ctx.paused) {
        //    delay(10);
        //    continue;
        //}

        if (!cpu_step()) {
            printf("CPU stopped\n");
            return 0;
        }

    }

    return 0;
}

void sleep_ms(int milis) {
#ifdef _WIN32
    Sleep(milis);
#else
   	usleep(milis * 1000);
#endif
}

void emu_stop() {
    ctx.running = false;
#ifdef __windows__
    WaitForSingleObject(thread, INFINITE);
#else
    pthread_join(thread, NULL);

#endif
}

void emu_reset() {
    ppu_init();
    timer_init();
    cpu_init();
    audio_init();
    cart_init();
    serial_init();
}

void emu_start() {
#ifdef __windows__
    thread = CreateThread(NULL, 0, cpu_run, NULL, 0, NULL);
#else
    pthread_create(&thread, NULL, cpu_run, NULL);
#endif
    if(!thread) {
        fprintf(stderr, "Unable to create main CPU thread!\n");
    }
}

int emu_run(int argc, char **argv) {
    if (argc < 2) {
        printf("Usage: gbemu <rom_file>\n");
        return -1;
    }

    if(!cart_load(argv[1])) {
        printf("Failed to load ROM file: %s\n", argv[1]);
        return -2;
    }

    char fn[1048];
#ifdef __windows__
    char *profile = getenv("USERPROFILE");
    sprintf(fn, "%s\\Documents\\gbemu", profile);
    CreateDirectory(fn, NULL);
    sprintf(fn, "%s\\Documents\\gbemu\\saves", profile);
    CreateDirectory(fn, NULL);
    sprintf(fn, "%s\\Documents\\gbemu\\states", profile);
    CreateDirectory(fn, NULL);
#else
    char *profile = getenv("HOME");
    sprintf(fn, "%s/.config/gbemu", profile);
    mkdir(fn, 0755);
    sprintf(fn, "%s/.config/gbemu/saves", profile);
    mkdir(fn, 0755);
    sprintf(fn, "%s/.config/gbemu/states", profile);
    mkdir(fn, 0755);
#endif
	
    printf("Cart loaded..\n");
    ctx.app_path = argv[0];

    ui_init();
    emu_reset();
    emu_start();

    u32 prev_frame = 0;
    debug_init();
    while(!ctx.die) {
        sleep_ms(1);
        ui_handle_events();
        //if (prev_frame != ppu_get_context()->current_frame) {
            ui_update();
        //}
        //prev_frame = ppu_get_context()->current_frame;
        if(ctx.debug) {
            debug_update();
        }
    }
    

    return 0;
}

void emu_cycles(int cpu_cycles) {
    if(emu_get_context()->paused && !emu_get_context()->step)
        return;
    for (int i = 0; i < cpu_cycles; i++){
        for(int n = 0; n < 4; n++){
            ctx.ticks++;
            timer_tick();
            ppu_tick();
        }
        dma_tick();
    }
}
