#include <SDL2/SDL_render.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

#include <SDL2/SDL.h>
#include "config.h"

/* Preset Values */
static float displayScale = 2;
static int cpuSpeed = 100000; // 100 kHz
static int singleStep = 0;
static int clockSteps = 1;

#include "sdlinit.c"
#include "events.c"
#include "cpu.c"
#include "memory.c"
#include "video.c"
#include "keyboard.c"
//#include "via.c"

uint8_t halt = 0;
uint8_t showHelp = 0;
uint8_t showDebug = 0;
uint8_t fpsCount = 0;
uint8_t displayMmap = 0;
uint8_t documentReload = 0;

uint8_t fpsStore = 60;
uint8_t FPS = 30;
uint8_t cpuHealth;

unsigned long cpuTicks = 0;
unsigned long tickTrigger = 0;



char debugString[512];
char snapshotFile[32];
char *reloadExecute;
char *inputFile;

int openFile(){
    FILE *file = fopen(inputFile, "rb");
    int retCode = 0;
    
    if (file != NULL){
        fseek(file, 0, SEEK_END);
        long filelen = ftell(file);
        rewind(file);
        
        if (fread(rom, filelen, 1, file) != 1) retCode = 1;
    } else retCode = 1;
    
    fclose(file);
    
    return retCode;
}

void resetSystem(){
    initVideo();
    updateVideo();
    reset6502();
}

void fetchArgs(int argc, char *argv[]){
    printf(PREAMBLE);
    
    for (int i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "--help"))                 printf(HELP);
        else if (!strcmp(argv[i], "--help-keys"))       printf(HELPKEYS);
        else if (!strcmp(argv[i], "--cpuspeed"))        cpuSpeed = atoi(argv[++i]);
        else if (!strcmp(argv[i], "--scale"))           displayScale = atof(argv[++i]);
        else if (!strcmp(argv[i], "--singlestep"))      singleStep = 1;
        else if (!strcmp(argv[i], "--clocksteps"))      clockSteps = atoi(argv[++i]);
        //else if (!strcmp(argv[i], "--snapshot"))        snapshotFile = &argv[i];
        else if (!strcmp(argv[i], "--fps"))		        FPS = atoi(argv[++i]);
        else if (!strcmp(argv[i], "--enable-reload"))   documentReload = 1;
        else if (!strcmp(argv[i], "--reload-execute"))  reloadExecute = argv[++i];
        else {
            inputFile = argv[i];
            if (openFile()){
                printf("%s: Unknown parameter or file '%s'\nTry '--help' for help\n", argv[0], argv[i]);
                exit(EXIT_FAILURE);
            }
        }
    }
}

int main(int argc, char *argv[]){
    fetchArgs(argc, argv);    
    initWindow();
    clearScreen(renderMemory, 0x000000FF);
    
    resetSystem();
    
    while (1){
        SDL_Delay(1000.f / FPS);
        pollEvents();
        
        fpsCount++; 
        updateVideo();

        if (!singleStep && !halt){
            for (int i = 0; i < ((float)cpuSpeed / (float)fpsStore); i++) {
                step6502();
                cpuTicks++;
            }
            irq6502();
        }
            
        if ((unsigned)time(NULL) != tickTrigger){
            tickTrigger = (unsigned)time(NULL);
            
            cpuHealth = (uint8_t)((cpuTicks / (float)cpuSpeed) * 100.);
            cpuTicks = 0;
            
            fpsStore = fpsCount; fpsCount = 0;

            /* Refresh entire video buffer */
            videoModified = 1;
        }
        
        
        uint16_t vStack = 16;
    
        if (halt) {
            drawString("!CPU Haltet!", renderMemory, 
                SDL_X_SIZE, 0x000000FF, time(NULL) % 2 ? 0xAAAAAAFF : 0xFFFFFFFF, 16, vStack, 2);
            
            vStack += (FONT_HEIGHT * 2 * 2);
        } if (showDebug){
            sprintf(debugString, "LOADED BINARY: \"%s\"\nSCREENSIZE: %dx%d\nFPS: %d\nSCALE: %d", inputFile, SDL_X_SIZE, SDL_Y_SIZE, fpsStore, (int)displayScale);
            drawString(debugString, renderMemory, 
                    SDL_X_SIZE, 0x000000FF, 0xAAAAAAFF, 16, vStack, 2);
            
            vStack += (FONT_HEIGHT * 4 * 2);

            sprintf(debugString, "CPU: %d%%", cpuHealth);
            drawString(debugString, renderMemory, 
                    SDL_X_SIZE, 0x000000FF, time(NULL) % 2 ? 0xAAAAAAFF : cpuHealth < 10 ? 
                    0xFF0000FF : cpuHealth < 50 ? 0xFFFF00FF : 0xAAAAAAFF, 16, vStack, 2);

            vStack += (FONT_HEIGHT * 2 * 2);
        } if (displayMmap){
            sprintf(debugString, "RAM:      $%04X - $%04X\nROM:      $%04X - $%04X\nVIAADDRS: $%04X\nKBDADDRS: $%04X\nVIDADDRS: $%04X", RAMLOC, RAMLOC + RAMSIZE, ROMLOC, ROMLOC + ROMSIZE, VIAADDRS, KBDADDRS, VIDADDRS);
            drawString(debugString, renderMemory, SDL_X_SIZE, 0x000000FF, 0xAAAAAAFF, 16, vStack, 2);

            vStack += (FONT_HEIGHT * 6 * 2);
        } if (showHelp) {
            drawString(HELPKEYS, renderMemory, SDL_X_SIZE, 0x000000FF, 0xAAAAAAFF, 16, vStack, 2);
            
            vStack += (FONT_HEIGHT * 2 * 2);
        }
        
        
 
        if (SDL_UpdateTexture(texture, NULL, renderMemory, (sizeof(uint32_t) * SDL_X_SIZE))) { 
            fprintf(stderr, "Could not update SDL texture: %s.\n", SDL_GetError());
            exit(EXIT_FAILURE);
        }
        if (SDL_RenderCopy(renderer, texture, NULL, NULL)) { 
            fprintf(stderr, "Could not display SDL texture: %s.\n", SDL_GetError());
            exit(EXIT_FAILURE);
        }
        
        SDL_RenderPresent(renderer);
    }

    return EXIT_SUCCESS;
}