#include <string.h>
#include <stdio.h>
#include <SDL.h>

#include "Statistics.h"
#include "Log.h"

// counts of instructions
static Uint32* _noPrefixTable = NULL;
static Uint32* _EDPrefixTable = NULL;
static Uint32* _CBPrefixTable = NULL;
static Uint32* _DDPrefixTable = NULL;
static Uint32* _DDCBPrefixTable = NULL;
static Uint32* _FDPrefixTable = NULL;
static Uint32* _FDCBPrefixTable = NULL;

static Uint8 _statisticsInitialized = 0;

void statistics_start() {
	statistics_destroy();

	_noPrefixTable = (Uint32*)malloc(256 * sizeof(Uint32));
	if (_noPrefixTable == NULL) {
		return;
	}
	memset(_noPrefixTable, 0, 256 * sizeof(Uint32));

	_EDPrefixTable = (Uint32*)malloc(256 * sizeof(Uint32));
	if (_EDPrefixTable == NULL) {
		return;
	}
	memset(_EDPrefixTable, 0, 256 * sizeof(Uint32));

	_CBPrefixTable = (Uint32*)malloc(256 * sizeof(Uint32));
	if (_CBPrefixTable == NULL) {
		return;
	}
	memset(_CBPrefixTable, 0, 256 * sizeof(Uint32));

	_DDPrefixTable = (Uint32*)malloc(256 * sizeof(Uint32));
	if (_DDPrefixTable == NULL) {
		return;
	}
	memset(_DDPrefixTable, 0, 256 * sizeof(Uint32));

	_DDCBPrefixTable = (Uint32*)malloc(256 * sizeof(Uint32));
	if (_DDCBPrefixTable == NULL) {
		return;
	}
	memset(_DDCBPrefixTable, 0, 256 * sizeof(Uint32));

	_FDPrefixTable = (Uint32*)malloc(256 * sizeof(Uint32));
	if (_FDPrefixTable == NULL) {
		return;
	}
	memset(_FDPrefixTable, 0, 256 * sizeof(Uint32));

	_FDCBPrefixTable = (Uint32*)malloc(256 * sizeof(Uint32));
	if (_FDCBPrefixTable == NULL) {
		return;
	}
	memset(_FDCBPrefixTable, 0, 256 * sizeof(Uint32));

	_statisticsInitialized = 1;
}

void statistics_destroy() {
	if (!_statisticsInitialized) {
		return;
	}

	if (_noPrefixTable != NULL) {
		free(_noPrefixTable);
		_noPrefixTable = NULL;
	}

	if (_EDPrefixTable != NULL) {
		free(_EDPrefixTable);
		_EDPrefixTable = NULL;
	}

	if (_CBPrefixTable != NULL) {
		free(_CBPrefixTable);
		_CBPrefixTable = NULL;
	}

	if (_DDPrefixTable != NULL) {
		free(_DDPrefixTable);
		_DDPrefixTable = NULL;
	}

	if (_DDCBPrefixTable != NULL) {
		free(_DDCBPrefixTable);
		_DDCBPrefixTable = NULL;
	}

	if (_FDPrefixTable != NULL) {
		free(_FDPrefixTable);
		_FDPrefixTable = NULL;
	}

	if (_FDCBPrefixTable == NULL) {
		free(_FDCBPrefixTable);
		_FDCBPrefixTable = NULL;
	}

	_statisticsInitialized = 0;
}

void statistics_record(struct instruction* instruction) {
	Uint32* table = NULL;

	switch (instruction->prefix.type) {
	case NoPrefix:
		table = _noPrefixTable;
		break;
	case ED:
		table = _EDPrefixTable;
		break;
	case CB:
		table = _CBPrefixTable;
		break;
	case DD:
		table = _DDPrefixTable;
		break;
	case FD:
		table = _FDPrefixTable;
		break;
	case DDCB:
		table = _DDCBPrefixTable;
		break;
	case FDCB:
		table = _FDCBPrefixTable;
		break;
	default:
		break;
	}

	if (table != NULL) {
		table[instruction->opcodeValue]++;
	}
}

void _statistics_log_one(Uint32* table, char* tableName) {
	log_write(tableName);

	char* message = (char*)malloc(256 * sizeof(char));
	if (message == NULL) {
		return;
	}

	for (Uint16 i = 0; i < 256; i++) {
		sprintf_s(message, 90, "%02x    %u", i, table[i]);
		log_write(message);
	}

	free(message);
}

void statistics_log_all() {
	log_write("\nRuntime instruction statistics");

	log_write("    execution frequency, by prefix and instruction opcode");
	_statistics_log_one(_noPrefixTable, "No prefix");
	_statistics_log_one(_EDPrefixTable, "ED prefix");
	_statistics_log_one(_CBPrefixTable, "CB prefix");
	_statistics_log_one(_DDPrefixTable, "DD prefix");
	_statistics_log_one(_DDCBPrefixTable, "DDCB prefix");
	_statistics_log_one(_FDPrefixTable, "FD prefix");
	_statistics_log_one(_FDCBPrefixTable, "FDCB prefix");
}
