#ifdef SUPERMODEL_DEBUGGER

#include "Supermodel.h"

#include "ConsoleDebugger.h"
#include "CPUDebug.h"
#include "Label.h"

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

namespace Debugger
{
	CSupermodelDebugger::CSupermodelDebugger(::CModel3 *model3, ::CInputs *inputs, ::CLogger *logger) : 
		CConsoleDebugger(), m_model3(model3), m_inputs(inputs), m_logger(logger)
	{
		AddCPU(new CPPCDebug());
#ifdef SUPERMODEL_SOUND
		AddCPU(new C68KDebug());
#endif
	}

	void CSupermodelDebugger::WaitCommand(CCPUDebug *cpu)
	{
		m_inputs->GetInputSystem()->UngrabMouse();

		CConsoleDebugger::WaitCommand(cpu);

		m_inputs->GetInputSystem()->GrabMouse();
	}

	bool CSupermodelDebugger::ProcessToken(const char *token, const char *cmd)
	{
		// TODO - load/saving emu state not supported
		//if (CheckToken(token, "les", "loademustate"))				// loademustate FILENAME
		//{
		//	// Parse arguments
		//	token = strtok(NULL, " ");
		//	if (token == NULL)
		//	{
		//		puts("Missing filename.");
		//		return false;
		//	}

		//	if (LoadModel3State(token))
		//		printf("Emulator state successfully loaded from <%s>\n", token);
		//	else
		//		printf("Unable to load emulator state from <%s>\n", token);
		//	return false;
		//}
		//else if (CheckToken(token, "ses", "savestate"))				// saveemustate FILENAME
		//{
		//	// Parse arguments
		//	token = strtok(NULL, " ");
		//	if (token == NULL)
		//	{
		//		puts("Missing filename.");
		//		return false;
		//	}

		//	if (SaveModel3State(token))
		//		printf("Emulator state successfully saved to <%s>\n", token);
		//	else
		//		printf("Unable to save emulator state to <%s>\n", token);
		//	return false;
		//}
		if (CheckToken(token, "lip", "listinputs"))					// listinputs
		{
			ListInputs();
			return false;
		}
		else if (CheckToken(token, "pip", "printinput"))			// printinput NAME
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Mising input name.");
				return false;
			}
			::CInput *input = (*m_inputs)[token];
			if (input == NULL)
			{
				printf("No input with id or label '%s'.\n", token);
				return false;
			}
			if (!InputIsValid(input))
			{
				printf("Input '%s' is not valid for current game.\n", token);
				return false;
			}

			if (!input->IsVirtual())
			{
				char mapTrunc[41];
				Truncate(mapTrunc, 40, input->GetMapping());
				printf("Input %s (%s) [%s] = %04X (%d)\n", input->id, input->label, mapTrunc, input->value, input->value);
			}
			else
				printf("Input %s (%s) = %04X (%d)\n", input->id, input->label, input->value, input->value);
			return false;
		}
		else if (CheckToken(token, "sip", "setinput"))				// setinput NAME MAPPING
		{
			// Parse arguments
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Mising input name.");
				return false;
			}
			::CInput *input = (*m_inputs)[token];
			if (input == NULL)
			{
				printf("No input with id or label '%s'.\n", token);
				return false;
			}
			if (!InputIsValid(input))
			{
				printf("Input '%s' is not valid for current game.\n", token);
				return false;
			}
			token = strtok(NULL, " ");
			if (token == NULL)
			{
				puts("Missing mapping to set.");
				return false;
			}

			input->SetMapping(token);

			printf("Set input %s (%s) to [%s]\n", input->id, input->label, input->GetMapping());
			return false;
		}
		else if (CheckToken(token, "cip", "configinput"))			// configinput NAME
		{
			//// Parse arguments
			// TODO
			//token = strtok(NULL, " ");
			//if (token == NULL)
			//{
			//	puts("Missing mode (a)ll, (s)et single, (a)ppend single or (r)eset single");
			//	return false;
			//}
			//if (CheckToken("a", "all"))
			//{
			//	//m_inputs->ConfigureInputs();
			//}
			return false;
		}
		else
			return CConsoleDebugger::ProcessToken(token, cmd);		
	}

	bool CSupermodelDebugger::InputIsValid(::CInput *input)
	{
		return input->IsUIInput() || (input->gameFlags & m_model3->GetGameInfo()->inputFlags);
	}

	void CSupermodelDebugger::ListInputs()
	{
		puts("Inputs:");
		if (m_inputs->Count() == 0)
			puts(" None");

		// Get maximum id, label and mapping widths
		size_t idAndLabelWidth = 0;
		size_t mappingWidth    = 0;
		for (unsigned i = 0; i < m_inputs->Count(); i++)
		{
			::CInput *input = (*m_inputs)[i];
			if (!InputIsValid(input))
				continue;

			idAndLabelWidth = max<size_t>(idAndLabelWidth, strlen(input->id) + strlen(input->label) + 3);
			if (!input->IsVirtual())
				mappingWidth = max<size_t>(mappingWidth, strlen(input->GetMapping()));
		}
		mappingWidth = min<size_t>(mappingWidth, 20);
		
		// Print labels, mappings and values for each input
		const char *groupLabel = NULL;
		char idAndLabel[255];
		char mapping[21];
		for (unsigned i = 0; i < m_inputs->Count(); i++)
		{
			::CInput *input = (*m_inputs)[i];
			if (!InputIsValid(input))
				continue;

			if (groupLabel == NULL || stricmp(groupLabel, input->GetInputGroup()) != 0)
			{
				groupLabel = input->GetInputGroup();
				printf(" %s:\n", groupLabel);
			}

			sprintf(idAndLabel, "%s (%s)", input->id, input->label);
			printf("  %-*s", (int)idAndLabelWidth, idAndLabel);
			if (!input->IsVirtual())
				Truncate(mapping, 20, input->GetMapping());
			else
				mapping[0] = '\0';
			printf(" %-*s %04X (%d)\n", (int)mappingWidth, mapping, input->value, input->value);
		}
	}

	void CSupermodelDebugger::Attached()
	{
		CConsoleDebugger::Attached();

		char fileName[25];
		sprintf(fileName, "Debug/%s.ds", m_model3->GetGameInfo()->id);
		LoadState(fileName);
	}

	void CSupermodelDebugger::Detaching()
	{
		char fileName[25];
		sprintf(fileName, "Debug/%s.ds", m_model3->GetGameInfo()->id);
		SaveState(fileName);
		
		CConsoleDebugger::Detaching();
	}

	bool CSupermodelDebugger::LoadModel3State(const char *fileName)
	{
		// Open file and find header 
		CBlockFile state;
		if (state.Load(fileName) != OKAY)
			return false;
		if (state.FindBlock("Debugger Model3 State") != OKAY)
		{
			state.Close();
			return false;
		}

		// Check version in header matches
		unsigned version;
		state.Read(&version, sizeof(version));
		if (version != MODEL3_STATEFILE_VERSION)
		{
			state.Close();
			return false;
		}

		// Load Model3 state
		m_model3->LoadState(&state);
		state.Close();

		// Reset debugger
		Reset();
		return true;
	}

	bool CSupermodelDebugger::SaveModel3State(const char *fileName)
	{
		// Create file with header
		CBlockFile state;
		if (state.Create(fileName, "Debugger Model3 State", __FILE__) != OKAY)
			return false;

		// Write out version in header
		unsigned version = MODEL3_STATEFILE_VERSION;
		state.Write(&version, sizeof(version));

		// Save Model3 state
		m_model3->SaveState(&state);
		state.Close();
		return true;
	}

	void CSupermodelDebugger::DebugLog(const char *fmt, va_list vl)
	{
		// Use the supplied logger, if any
		if (m_logger != NULL)
			m_logger->DebugLog(fmt, vl);
		else
			CConsoleDebugger::DebugLog(fmt, vl);
	}
		
	void CSupermodelDebugger::InfoLog(const char *fmt, va_list vl)
	{
		// Use the supplied logger, if any
		if (m_logger != NULL)
			m_logger->InfoLog(fmt, vl);
		else
			CConsoleDebugger::InfoLog(fmt, vl);
	}
	
	void CSupermodelDebugger::ErrorLog(const char *fmt, va_list vl)
	{
		// Use the supplied logger, if any
		if (m_logger != NULL)
			m_logger->ErrorLog(fmt, vl);
		else
			CConsoleDebugger::ErrorLog(fmt, vl);
	}
}

#endif  // SUPERMODEL_DEBUGGER