#include <debug.h>
#include <stdio.h>
#include <cpu.h>
#include <emu.h>
#include <bus.h>
#include <string.h>

static debug_context ctx;

debug_context *debug_get_context() {
    return &ctx;
}

static char **dissasembly;

void debug_init() {
    dissasembly = malloc(sizeof(char*) * 0x10000);
    for(int i = 0; i < 0x10000; i++) {
        dissasembly[i] = malloc(sizeof(char) * 100);
    }
}

void debug_update() {
    switch(ctx.state) {
        case DS_MAIN:
            char flags[16];
            sprintf(flags, "%c%c%c%c",
                cpu_get_context()->regs.f & (1 << 7) ? 'Z' : '-',
                cpu_get_context()->regs.f & (1 << 6) ? 'N' : '-',
                cpu_get_context()->regs.f & (1 << 5) ? 'H' : '-',
                cpu_get_context()->regs.f & (1 << 4) ? 'C' : '-'
            );

            char inst[16];
            inst_to_str(cpu_get_context(), inst);
            printf("%08lX - %04X: %-12s (%02X %02X %02X) A: %02X F: %s BC: %02X%02X DE: %02X%02X HL: %02X%02X SP: %04X PC: %04X\n", emu_get_context()->ticks, cpu_get_context()->inst_pc, inst, cpu_get_context()->cur_opcode, bus_read(cpu_get_context()->inst_pc+1), bus_read(cpu_get_context()->inst_pc+2), cpu_get_context()->regs.a, flags, cpu_get_context()->regs.b, cpu_get_context()->regs.c, cpu_get_context()->regs.d, cpu_get_context()->regs.e, cpu_get_context()->regs.h, cpu_get_context()->regs.l, cpu_get_context()->regs.sp, cpu_get_context()->regs.pc);
            printf("> ");
            char cmd[128];
            scanf("%s", cmd);
            if(!strcmp(cmd, "q")) {
                emu_get_context()->die = true;
                emu_get_context()->debug = false;
                emu_get_context()->paused = false;
                emu_stop();
            } else if(!strcmp(cmd, "r")) {
                emu_get_context()->debug = false;
                emu_get_context()->paused = false;
            } else if(!strcmp(cmd, "d")) {
                ctx.dissasembly_target = cpu_get_context()->inst_pc;
                ctx.state = DS_DISASSEMBLE; 
            } else if(!strcmp(cmd, "da")) {
                scanf("%d", &ctx.dissasembly_target);
                ctx.state = DS_DISASSEMBLE;
            } else if(cmd[0] == 'b' && cmd[1] == 'p') {
                scanf("%X", &ctx.breakpoint);
                //printf("Set breakpoint to %04X\n", ctx.breakpoint);
            } else if(cmd[0] == 's') {
                int steps;
                scanf("%d", &steps);
                //printf("adding %d steps\n", steps);
                emu_get_context()->step += steps;
                emu_get_context()->debug = false;
            }
        break;
        case DS_DISASSEMBLE:
            cpu_context cpu_ctx = {0};
            u16 i = 0;
            while (cpu_ctx.regs.pc <= 0xFFFF) {
                u16 pc = cpu_ctx.regs.pc;
                fetch_instruction(&cpu_ctx);
                fetch_data(&cpu_ctx);
                char inst[16];
                inst_to_str(&cpu_ctx, inst);
                sprintf(dissasembly[i], "%04X: %s", pc, inst);
                if(pc == cpu_get_context()->inst_pc) {
                    ctx.dissasembly_scroll = i - 5;
                    ctx.dissasembly_pc = i;
                }
                i++;
                if(cpu_ctx.regs.pc < pc)
                    break;
            }
            for(int j = ctx.dissasembly_scroll; j < ctx.dissasembly_pc+6; j++) {
                printf("%c %s\n", j == ctx.dissasembly_pc ? '>' : ' ', dissasembly[j]);
            }
            ctx.state = DS_MAIN;
        break;
    }
}