#include "debug_wnd.h"

CDebugWnd::CDebugWnd(nes_emu_t* nes, QWidget* parent)
	: QWidget(parent)
{
	QGridLayout* mainLayout;
	QGroupBox* controlBox;
	QGridLayout* controlLayout;
	QPushButton* startBtn;
	QPushButton* stepBtn;
	QPushButton* stopBtn;
	QPushButton* resetBtn;
	
	setFixedSize(712, 388);
	
	mainLayout = new QGridLayout();
	
	/* Controls */
	controlBox = new QGroupBox("Controls");
	controlBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
	
	startBtn = new QPushButton("Start", this);
	stepBtn = new QPushButton("Step", this);
	stopBtn = new QPushButton("Stop", this);
	resetBtn = new QPushButton("Reset", this);

	startBtn->setDisabled(true);
	stopBtn->setDisabled(true);
	
	this->connect(startBtn, SIGNAL(clicked()), this, SLOT(startEmulation()));
	this->connect(stepBtn, SIGNAL(clicked()), this, SLOT(stepEmulation()));
	this->connect(stopBtn, SIGNAL(clicked()), this, SLOT(stopEmulation()));
	this->connect(resetBtn, SIGNAL(clicked()), this, SLOT(resetEmulation()));
	
	controlLayout = new QGridLayout();
	controlLayout->addWidget(startBtn, 0, 0);
	controlLayout->addWidget(stepBtn, 0, 1);
	controlLayout->addWidget(stopBtn, 0, 2);
	controlLayout->addWidget(resetBtn, 1, 0, 1, 3);
	controlBox->setLayout(controlLayout);
	/* Controls */
	
	/* Dissassembly */
	disassemblyBox = new QTextEdit();
	disassemblyBox->setFont(QFont("Courier New", 10, QFont::Normal));
	/* Dissassembly */
	
	mainLayout->addWidget(controlBox, 0, 0);
	mainLayout->addWidget(disassemblyBox, 0, 1, 2, 1);
	this->setLayout(mainLayout);
	
	this->nes = nes;
	nes_load_rom(this->nes);
}

void CDebugWnd::startEmulation(void)
{
	/* TODO: Add logic. */
}

void CDebugWnd::stepEmulation(void)
{
	nes_step(this->nes);
	this->updateDisassembly();
}

void CDebugWnd::stopEmulation(void)
{
	/* TODO: Add logic. */
}

void CDebugWnd::resetEmulation(void)
{
	nes_reset(this->nes);
}

void CDebugWnd::updateDisassembly(void)
{
	int t, u, pc;
	char buffer[2048] = {0};
	cpu6502_t* cpu = nes_get_cpu(this->nes);
	mapper_t* mapper = nes_get_mapper(this->nes);
	
	t = 0;
	pc = cpu_get_pc(cpu);
	do {
		opcode_t op = cpu_get_op(cpu, pc);
		sprintf(buffer, "%s$%04X", buffer, pc);
		
		/* Raw memory */
		sprintf(buffer, "%s:%02X", buffer, mapper_read(mapper, pc));
		for (u = pc + 1; u < (pc + op.size); u++) {
			sprintf(buffer, "%s %02X", buffer, mapper_read(mapper, u));
		}
		/* Raw memory */

		/* Disassembly */
		sprintf(buffer, "%s\t%s", buffer, op.inst_name);
		switch (op.addr_mode) {
			case ADDRMODE_ZP: {
				int val = mapper_read(mapper, mapper_read(mapper, pc + 1));
				sprintf(buffer, "%s $%02X = #$%02X", buffer, mapper_read(mapper, pc + 1), val);
				break;
			}

			case ADDRMODE_REL: {
				int val = mapper_read(mapper, pc + 1);
				if (val < 0x80) {
					val += pc;
				}
				else {
					val += pc - 0xFF;
				}
				val++;
				sprintf(buffer, "%s $%04X", buffer, val);
				break;
			}

			case ADDRMODE_ABS: {
				int val = mapper_read(mapper, mapper_read16(mapper, pc + 1));
				sprintf(buffer, "%s $%04X = #$%02X", buffer, mapper_read16(mapper, pc + 1), val);
				break;
			}

			case ADDRMODE_ACC: {
				sprintf(buffer, "%s A = #$%02X", buffer, cpu_get_a(cpu));
				break;
			}

			case ADDRMODE_IMM: {
				sprintf(buffer, "%s #$%02X", buffer, mapper_read(mapper, pc + 1));
				break;
			}

			case ADDRMODE_ZPX: {
				int val = (mapper_read(mapper, pc + 1) + cpu_get_x(cpu)) & 0xFF;
				sprintf(buffer, "%s $%02X,X @ $%04X = #$%02X", buffer, mapper_read(mapper, pc + 1), val, mapper_read(mapper, val));
				break;
			}

			case ADDRMODE_ZPY: {
				int val = (mapper_read(mapper, pc + 1) + cpu_get_y(cpu)) & 0xFF;
				sprintf(buffer, "%s $%02X,Y @ $%04X = #$%02X", buffer, mapper_read(mapper, pc + 1), val, mapper_read(mapper, val));
				break;
			}
			
			case ADDRMODE_ABSX: {
				int val = mapper_read16(mapper, pc + 1) + cpu_get_x(cpu);
				sprintf(buffer, "%s $%04X,X @ $%04X = #$%02X", buffer, mapper_read16(mapper, pc + 1), val, mapper_read(mapper, val));
				break;
			}

			case ADDRMODE_ABSY: {
				int val = mapper_read16(mapper, pc + 1) + cpu_get_y(cpu);
				sprintf(buffer, "%s $%04X,Y @ $%04X = #$%02X", buffer, mapper_read16(mapper, pc + 1), val, mapper_read(mapper, val));
				break;
			}

			case ADDRMODE_PREIDXIND: {
				sprintf(buffer, "%s ($%02X,X)", buffer, mapper_read(mapper, pc + 1));
				break;
			}

			case ADDRMODE_POSTIDXIND: {
				sprintf(buffer, "%s ($%02X),Y", buffer, mapper_read(mapper, pc + 1));
				break;
			}
			
			case ADDRMODE_INDABS: {
				int addr = mapper_read16(mapper, mapper_read16(mapper, pc + 1));
				sprintf(buffer, "%s ($%04X) @ $%04X = #$%02X", buffer, mapper_read16(mapper, pc + 1), addr, mapper_read(mapper, addr));
				break;
			}

			default: {
				sprintf(buffer, "%s", buffer);
				break;
			}
		}
		/* Disassembly */

		sprintf(buffer, "%s\r\n", buffer);

		pc += op.size;
	} while(t++ < 20);
	
	disassemblyBox->setText(buffer);
}