#include "GeckoCode.h"

#include "Thread.h"
#include "HW/Memmap.h"
#include "ConfigManager.h"

#include "vector"

namespace Gecko
{

enum
{
	// Code Types
	CODETYPE_WRITE_FILL =	0x0,
	CODETYPE_IF =			0x1,
	CODETYPE_BA_PO_OPS =	0x2,
	CODETYPE_FLOW_CONTROL =	0x3,
	CODETYPE_REGISTER_OPS =	0x4,
	CODETYPE_SPECIAL_IF =	0x5,
	CODETYPE_ASM_SWITCH_RANGE =	0x6,
	CODETYPE_END_CODES =	0x7,

	// Data Types
	DATATYPE_8BIT =		0x0,
	DATATYPE_16BIT =	0x1,
	DATATYPE_32BIT =	0x2,
};

// globals
static u32 base_address = 0;
static u32 pointer_address = 0;
static u32 gecko_register[0x10] = {0};
static struct
{
	u32 address;
	u32 number;
} block[0x10];

// codes execute when counter is 0
static int	code_execution_counter = 0;

// the currently active codes
std::vector<GeckoCode>	active_codes;

// return true if code execution is on
inline bool CodeExecution()
{
	return (0 == code_execution_counter);
}

u32 GeckoCode::Code::GetAddress() const
{
	return gcaddress + (use_po ? pointer_address : (base_address & 0xFE000000));
}

static std::mutex active_codes_lock;

// currently running code
static GeckoCode::Code *codes_start = NULL, *current_code = NULL;
static const GeckoCode::Code *codes_end = NULL;

// asm codes used for CT6 CST1
static std::map<u32, std::vector<u32> > inserted_asm_codes;

// Functions for each code type
bool RamWriteAndFill();
bool RegularIf();
bool BaPoOps();
bool FlowControl();
bool RegisterOps();
bool SpecialIf();
bool AsmSwitchRange();
bool EndCodes();

bool MathOperation(u32& ret, const u32 left, const u32 right, const u8 type);

void SetActiveCodes(const std::vector<GeckoCode>& gcodes)
{
	std::lock_guard<std::mutex> lk(active_codes_lock);

	active_codes.clear();
	// add enabled codes
	std::vector<GeckoCode>::const_iterator
		gcodes_iter = gcodes.begin(),
		gcodes_end = gcodes.end();
	for (; gcodes_iter!=gcodes_end; ++gcodes_iter)
		if (gcodes_iter->enabled)
		{
			// TODO: apply modifiers
			// TODO: don't need description or creator string, just takin up memory
			active_codes.push_back(*gcodes_iter);
		}

	inserted_asm_codes.clear();
}

bool RunGeckoCode(GeckoCode& gecko_code)
{
	static bool (*code_type_funcs[])(void) =
	{ RamWriteAndFill, RegularIf, BaPoOps, FlowControl, RegisterOps, SpecialIf, AsmSwitchRange, EndCodes };

	base_address = 0x80000000;
	pointer_address = 0x80000000;
	code_execution_counter = 0;

	current_code = codes_start = &*gecko_code.codes.begin();
	codes_end = &*gecko_code.codes.end();
	for (; current_code < codes_end; ++current_code)
	{
		const GeckoCode::Code& code = *current_code;

		bool result = true;

		switch (code.type)
		{
			// These codetypes run even if code_execution is off
		case CODETYPE_IF :
		case CODETYPE_FLOW_CONTROL :
		case CODETYPE_SPECIAL_IF :
		case CODETYPE_ASM_SWITCH_RANGE :
		case CODETYPE_END_CODES :
			result = code_type_funcs[code.type]();
			break;

			// The rest of the codetypes only run if code_execution is on
		default :
			if (CodeExecution())
				result = code_type_funcs[code.type]();
			break;
		}

		// code failed
		if (false == result)
		{
			// disable code to stop annoying error messages
			gecko_code.enabled = false;

			PanicAlertT("GeckoCode failed to run (CT%i CST%i) (%s)"
				"\n(either a bad code or the code type is not yet supported.)"
				, code.type, code.subtype, gecko_code.name.c_str());
			return false;
		}
	}

	return true;
}

bool RunActiveCodes()
{
	if (SConfig::GetInstance().m_LocalCoreStartupParameter.bEnableCheats)
	{
		std::lock_guard<std::mutex> lk(active_codes_lock);

		std::vector<GeckoCode>::iterator
			gcodes_iter = active_codes.begin(),
			gcodes_end = active_codes.end();
		for (; gcodes_iter!=gcodes_end; ++gcodes_iter)
		{
			if (gcodes_iter->enabled)
				RunGeckoCode(*gcodes_iter);
			// we don't need to stop all codes if one fails, maybe
			//if (false == RunGeckoCode(*gcodes_iter))
				//return false;
		}
	}

	return true;
}

const std::map<u32, std::vector<u32> >& GetInsertedAsmCodes() {
	return inserted_asm_codes;
}

// CT0: Direct ram write/fill
// COMPLETE, maybe
bool RamWriteAndFill()
{
	const GeckoCode::Code& code = *current_code;
	u32 new_addr = code.GetAddress();
	const u32& data = code.data;

	u32 count = (data >> 16) + 1;	// note: +1

	switch (code.subtype)
	{
		// CST0: 8bits Write & Fill
	case DATATYPE_8BIT :
		while (count--)
		{
			Memory::Write_U8((u8)data, new_addr);
			++new_addr;
		}
		break;

		// CST1: 16bits Write & Fill
	case DATATYPE_16BIT :
		while (count--)
		{
			Memory::Write_U16((u16)data, new_addr);
			new_addr += 2;
		}
		break;

		// CST2: 32bits Write
	case DATATYPE_32BIT :
		Memory::Write_U32((u32)data, new_addr);
		break;

		// CST3: String Code
	case 0x3 :
		count = code.data;	// count is different from the other subtypes
		while (count)
		{
			if (codes_end == ++current_code)
				return false;

			// write bytes from address
			int byte_num = 4;
			while (byte_num-- && count)
			{
				Memory::Write_U8((u8)(current_code->address >> byte_num * 8), new_addr);
				++new_addr;
				--count;
			}

			// write bytes from data
			byte_num = 4;
			while (byte_num-- && count)
			{
				Memory::Write_U8((u8)(current_code->data >> byte_num * 8), new_addr);
				++new_addr;
				--count;
			}
		}
		break;

		// CST4: Serial Code
	case 0x4 :
	{
		if (codes_end == ++current_code)
			return false;
		u32 new_data = data;	// starting value of data
		const u8 data_type = current_code->address >> 28;
		const u32 data_inc = current_code->data;	// amount to increment the data
		const u16 addr_inc = (u16)current_code->address;	// amount to increment the address
		count =	((current_code->address >> 16) & 0xFFF) + 1;	// count is different from the other subtypes, note: +1
		while (count--)
		{
			// switch inside the loop, :/ o well
			switch (data_type)
			{
			case DATATYPE_8BIT :
				Memory::Write_U8((u8)new_data, new_addr);
				new_data = (u8)new_data + (u8)data_inc;
				break;

			case DATATYPE_16BIT :
				Memory::Write_U16((u16)new_data, new_addr);
				new_data = (u16)new_data + (u16)data_inc;
				break;

			case DATATYPE_32BIT :
				Memory::Write_U32((u32)new_data, new_addr);
				new_data += data_inc;
				break;

				// INVALID DATATYPE
			default :
				return false;
				break;
			}

			new_addr += addr_inc;
		}
	}
		break;

		// INVALID SUBTYPE
	default :
		return false;
		break;
	}

	return true;
}

// CT1 Regular If codes (16/32 bits)
// COMPLETE
bool RegularIf()
{
	const GeckoCode::Code& code = *current_code;

	const bool is_endif = !!(code.address & 0x1);

	// if code_execution is off and this is an endif, decrease the execution counter
	if (false == CodeExecution())
	{
		if (is_endif)
			--code_execution_counter;
	}

	bool result = false;

	// if code_execution is on, execute the conditional
	if (CodeExecution())
	{
		const u32 new_addr = code.GetAddress() & ~0x1;
		const u32& data = code.data;

		s32 read_value = 0;
		s32 data_value = 0;

		// 16bit vs 32bit
		if (code.subtype & 0x4)
		{
			// 16bits
			read_value = Memory::Read_U16(new_addr) & ~(data >> 16);
			data_value = (data & 0xFFFF);
		}
		else
		{
			// 32bits
			read_value = Memory::Read_U32(new_addr);
			data_value = data;
		}

		switch (code.subtype & 0x3)
		{
			// CST0 : 32bits (endif, then) If equal
			// CST4 : 16bits (endif, then) If equal
		case 0x0 :
			result = (read_value == data_value);
			break;

			// CST1 : 32bits (endif, then) If not equal
			// CST5 : 16bits (endif, then) If not equal
		case 0x1 :
			result = (read_value != data_value);
			break;

			// CST2 : 32bits (endif, then) If greater
			// CST6 : 16bits (endif, then) If greater
		case 0x2 :
			result = (read_value > data_value);
			break;

			// CST3 : 32bits (endif, then) If lower
			// CST7 : 16bits (endif, then) If lower
		case 0x3 :
			result = (read_value < data_value);
			break;	
		}
	}

	// if the conditional returned false, or it never ran because execution is off,
	// increase the code execution counter
	if (false == result)
		++code_execution_counter;

	return true;
}

// CT2 Base Address/Pointer Operations
// NOT COMPLETE, last 2 subtypes aren't done
bool BaPoOps()
{
	const GeckoCode::Code& code = *current_code;

	// base_address vs pointer (ba vs po)
	u32& change_address = (code.subtype & 0x4) ? pointer_address : base_address;

	// 4STYZ00N XXXXXXXX
	u32 new_data = code.data;

	// append grN
	if (code.z)
		new_data += gecko_register[code.n];

	// append ba or po (depending on last nibble's first bit)
	if (code.y)
		new_data += (code.use_po ? pointer_address : base_address);

	// append to current value (not used in all subtypes, T will be 0 in those)
	if (code.t)
		new_data += change_address;

	switch (code.subtype & 0x3)
	{
		// CST0 : Load into Base Address
		// CST4 : Load into Pointer
	case 0x0 :
		change_address = Memory::Read_U32(new_data);
		break;

		// CST1 : Set Base Address to
		// CST5 : Set Pointer to
	case 0x1 :
		change_address = new_data;
		break;

		// CST2 : Save Base Address to
		// CST6 : Save Pointer to
	case 0x2 :
		Memory::Write_U32(change_address, new_data);
		break;

		// CST3 : Put next line of code location into the Base Address
		// CST7 : Put grN's location into the Pointer

		// conflicting documentation, one doc says CST7 is the same as CST3 but with po
	case 0x3 :
		// TODO:
		return false;	//
		break;
	}

	return true;
}

// CT3 Repeat/Goto/Gosub/Return
// COMPLETE, maybe
bool FlowControl()
{
	const GeckoCode::Code& code = *current_code;

	// only the return subtype runs when code execution is off
	if (false == CodeExecution() && code.subtype != 0x2)
		return true;

	// not all of these are used in all subtypes
	const u8 block_num = (u8)(code.data & 0xF);

	switch (code.subtype)
	{
		// CST0 : Set Repeat
	case 0x0 :
		block[block_num].number = code.address & 0xFFFF;
		block[block_num].address = (u32)(current_code - codes_start + 1);
		break;

		// CST1 : Execute Repeat
	case 0x1 :
		if (block[block_num].number)
		{
			--block[block_num].number;
			// needs -1 cause iterator gets ++ after code runs
			current_code = codes_start + block[block_num].address - 1;
		}
		break;

		// CST2 : Return
	case 0x2 :
		if (((code.address >> 20) & 0xF) ^ (u32)CodeExecution())
		{
			// needs -1 cause iterator gets ++ after code runs
			current_code = codes_start + block[block_num].address - 1;
		}
		break;

		// CST3 : Goto
	case 0x3 :
		if (((code.address >> 20) & 0xF) ^ (u32)CodeExecution())
		{
			GeckoCode::Code* const target_code = current_code + (s16)(code.address & 0xFFFF);

			if (target_code >= codes_start && target_code < codes_end)
			{
				// needs -1 cause iterator gets ++ after code runs
				current_code = target_code - 1;
			}
			else
				return false;	// trying to GOTO to bad address
		}
		break;

		// CST4 : Gosub
	case 0x4 :
		if (((code.address >> 20) & 0xF) ^ (u32)CodeExecution())
		{
			GeckoCode::Code* const target_code = current_code + (s16)(code.address & 0xFFFF);

			if (target_code >= codes_start && target_code < codes_end)
			{
				block[block_num].address = u32(current_code - codes_start + 1);
				// needs -1 cause iterator gets ++ after code runs
				current_code = target_code - 1;
			}
			else
				return false;	// trying to GOSUB to bad address
		}
		break;

		// INVALID SUBTYPE
	default :
		return false;
		break;
	}

	return true;
}

// CT4 Gecko Register Operations
// COMPLETE, maybe
bool RegisterOps()
{
	const GeckoCode::Code& code = *current_code;

	// 80TYZZZN XXXXXXXX

	u32 new_data = code.data;

	// append ba or po (depending on last nibble's first bit)
	if (code.y)
		new_data += (code.use_po ? pointer_address : base_address);

	u32& geckreg = gecko_register[code.n];

	switch (code.subtype)
	{
		// CST0 : Set Gecko Register to
	case 0x0 :
		// append to or set register
		geckreg = new_data + (code.t ? geckreg : 0);
		break;

		// CST1 : Load into Gecko Register
	case 0x1 :
		switch (code.t)
		{
			// 8bit
		case DATATYPE_8BIT :
			geckreg = Memory::Read_U8(new_data);
			break;

			// 16bit
		case DATATYPE_16BIT :
			geckreg = Memory::Read_U16(new_data);
			break;

			// 32bit
		case DATATYPE_32BIT :
			geckreg = Memory::Read_U32(new_data);
			break;

			// INVALID DATATYPE
		default :
			return false;
			break;
		}
		break;

		// CST2 : Save Gecko Register to
	case 0x2 :
		switch (code.t)
		{
			// 8bit
		case DATATYPE_8BIT :
			for (u16 i = 0; i <= code.z; ++i)
				Memory::Write_U8((u8)geckreg, new_data + i);
			break;

			// 16bit
		case DATATYPE_16BIT :
			for (u16 i = 0; i <= code.z; ++i)
				Memory::Write_U16((u16)geckreg, new_data + (i << 1));
			break;

			// 32bit
		case DATATYPE_32BIT :
			for (u16 i = 0; i <= code.z; ++i)
				Memory::Write_U32((u32)geckreg, new_data + (i << 2));
			break;

			// INVALID DATATYPE
		default :
			return false;
			break;
		}
		break;

		// CST3 : Gecko Register / Direct Value operations
		// CST4 : Gecko Registers operations
	case 0x3 :
	case 0x4 :
	{
		// subtype 3 uses value in .data subtype 4 uses register
		u32 right_val = (code.subtype & 0x1 ? code.data : gecko_register[code.data & 0xF]);
		if (code.y & 0x2)
			right_val = Memory::Read_U32(right_val);

		u32 left_val = geckreg;
		if (code.y & 0x1)
			left_val = Memory::Read_U32(left_val);

		if (false == MathOperation(geckreg, left_val, right_val, code.t))
			return false;
	}
		break;

		// CST5 : Memory Copy 1
		// CST6 : Memory Copy 2
	case 0x5 :
	case 0x6 :
	{
		const u8 src_gr = (code.z & 0xF);
		const u8 dst_gr = (code.n);

		u16 count = (u16)(code.address >> 8);

		// docs don't specify allowing 0xF for source and dest, but it can't hurt
		u32 src_addr = ((0xF == src_gr) ? (code.use_po ? pointer_address : base_address) : gecko_register[src_gr]);
		u32 dst_addr = ((0xF == dst_gr) ? (code.use_po ? pointer_address : base_address) : gecko_register[dst_gr]);

		if (0x5 == code.subtype)
			dst_addr += new_data;
		else
			src_addr += new_data;
		
		while (count--)
			Memory::Write_U8(Memory::Read_U8(src_addr++), dst_addr++);
	}
		break;

		// INVALID SUBTYPE
	default :
		return false;
		break;

	}

	return true;
}

// NOT COMPLETE, at least one op needs to be coded/fixed
bool MathOperation(u32& ret, const u32 left, const u32 right, const u8 type)
{
	// ? = T :
	switch (type)
	{
		// 0 : add (+)
	case 0x0 :
		ret = left + right;
		break;

		// 1 : mul (*)
	case 0x1 :
		ret = left * right;
		break;

		// 2 : or (|)
	case 0x2 :
		ret = left | right;
		break;

		// 3 : and (&)
	case 0x3 :
		ret = left & right;
		break;

		// 4 : xor (^)
	case 0x4 :
		ret = left ^ right;
		break;

		// 5 : slw (<<)
	case 0x5 :
		ret = left << right;
		break;

		// 6 : srw (>>)
	case 0x6 :
		ret = left >> right;
		break;

		// TODO: this one good?
		// 7 : rol (rotate left)
	case 0x7 :
		ret = (left << right) | (left >> (8 - right));
		break;

		// 8 : asr (arithmetic shift right)
	case 0x8 :
		ret = (s32)left >> right;
		break;

		// TODO: these float ops good?
		// A : fadds (single float add)
	case 0x9 :
		*(float*)&ret = *(float*)&left + *(float*)&right;
		break;

		// B : fmuls (single float mul)
	case 0xA :
		*(float*)&ret = *(float*)&left * *(float*)&right;
		break;

		// INVALID OPERATION TYPE
	default :
		return false;
		break;
	}

	return true;
}

// CT5: Special If codes (16bits)
// COMPLETE, maybe (ugly)
bool SpecialIf()
{
	// counter can modify the code :/
	GeckoCode::Code& code = *current_code;

	const bool is_endif = !!(code.address & 0x1);

	// if code_execution is off and this is an endif, decrease the execution counter
	if (false == CodeExecution())
	{
		if (is_endif)
			--code_execution_counter;
	}

	bool result = false;

	// TODO: should these be signed? probably?
	s16 left_val = 0, right_val = 0;

	// if code_execution is on, execute the conditional
	if (CodeExecution())
	{
		const u32 addr = code.GetAddress() & ~0x1;
		const u32& data = code.data;

		if (code.subtype ^ 0x4)
		{
			// CT5 Part1 : Unknown values comparison
			// A-______ NM00YYYY

			const u8 n = (u8)(data >> 28);
			const u8 m = (u8)((data >> 24) & 0xF);
			const u16 y = (u16)data;

			left_val = Memory::Read_U16(((0xF == n) ? addr : gecko_register[n]) & ~y);
			right_val = Memory::Read_U16(((0xF == m) ? addr : gecko_register[m]) & ~y);
		}
		else
		{
			// CT5 Part2 : 16bits Counter check
			// A-0ZZZZT MMMMXXXX

			left_val = (u16)(data) & ~(u16)(data >> 16);
			right_val = (u16)(addr >> 4);
		}

		switch (code.subtype & 0x3)
		{
			// CST0 : 16bits (endif, then) If equal
		case 0x0 :
			result = (left_val == right_val);
			break;

			// CST1 : 16bits (endif, then) If not equal
		case 0x1 :
			result = (left_val != right_val);
			break;

			// CST2 : 16bits (endif, then) If greater
		case 0x2 :
			result = (left_val > right_val);
			break;

			// CST3 : 16bits (endif, then) If lower
		case 0x3 :
			result = (left_val < right_val);
			break;
		}
	}	
	else if (code.subtype & 0x4)
	{
		// counters get reset if code execution is off
		code.address &= ~0x000FFFF0;
	}

	// if the conditional returned false, or it never ran because execution is off, increase the code execution counter
	if (false == result)
		++code_execution_counter;
	else if (code.subtype & 0x4)
	{
		// counters gets advanced if condition was true
		// right_val is the value of the counter

		code.address &= ~0x000FFFF0;
		code.address |= ((right_val+1) << 4);
	}

	return true;
}

// CT6 ASM Codes, On/Off switch and Address Range Check
// NOT COMPLETE, asm stuff not started
// fix the uglyness
bool AsmSwitchRange()
{
	// the switch subtype modifies the code :/
	GeckoCode::Code& code = *current_code;

	// only run if code_execution is set or this code is a switch or rangecheck subtype
	// the switch and rangecheck run if exectution_counter is 1 (directly inside the failed if) if they are an endif
	if (false == CodeExecution())
	{
		if (code.subtype < 0x6)
			return true;
		else if (1 != code_execution_counter)
			return true;
	}

	u32& data = code.data;

	switch (code.subtype)
	{
		// CST0 : Execute following ASM Code
	case 0x0 :
		// TODO:
		return false;	//
		break;

		// CST1 : Insert ASM code in the game
	case 0x1 :
		{
			const int number_of_codes = code.data;
			std::vector<u32>& asm_code = inserted_asm_codes[code.GetAddress()];
			if (asm_code.empty()) {
				for (int index = 0; index < number_of_codes; ++index) {
					// Reserved for padding
					if (current_code[index + 1].address != 0x60000000) {
						asm_code.push_back(current_code[index + 1].address);
					}

					// Reserved for b instruction
					if (current_code[index + 1].data != 0x00000000) {
						asm_code.push_back(current_code[index + 1].data);
					}
				}
			}

			// Though the next code starts at current_code+number_of_codes+1,
			// we add only number_of_codes. It is because the for statemet in
			// RunGeckoCode() increments current_code.
			current_code += number_of_codes;

			break;
		}

		// CST3 : Create a branch
	case 0x3 :
		// watever
		//if (code.data)
		return false;	//
		break;

		// CST6 : On/Off switch
	case 0x6 :
		// in the 1st bit of code.data, i store if code execution was previously off
		// in the 2nd bit of code.data, i store the switch's on/off state
		if (CodeExecution())
		{
			if (data & 0x1)
				data ^= 0x2;	// if code exec was previously off, flip the switch

			// mark code execution as previously on
			data &= ~0x1;
		}
		else
		{
			// mark code execution as previously off
			data |= 0x1;
		}

		// set code execution to the state of the switch
		code_execution_counter = !(data & 0x2);
		break;

		// CST7 : Address range check (If... code)
	case 0x7 :
	{
		const bool is_endif = !!(code.address & 0x1);
		if (is_endif)
			--code_execution_counter;

		if (CodeExecution())
		{
			const u32 addr = (code.use_po ? pointer_address : base_address);
			if (addr < (data & 0xFFFF0000) || addr > (data << 16))
				++code_execution_counter;
		}
	}
		break;

		// INVALID SUBTYPE
	default :
		return false;
		break;
	}

	return true;
}

// CT7 End of codes, Endif (Else)
// COMPLETE, maybe
bool EndCodes()
{
	const GeckoCode::Code& code = *current_code;
	const u32& data = code.data;

	const u32 x = (data & 0xFFFF0000);
	const u32 y = (data << 16);

	// these 2 do not happen in the "CST7 : End of Code", but in that subtype they will be 0
	if (x)
		base_address = x;
	if (y)
		pointer_address = y;

	switch (code.subtype)
	{
		// CST0 : Full Terminator
	case 0x0 :
		// clears the code execution status
		code_execution_counter = 0;
		break;

		// CST1 : Endif (+else)
	case 0x1 :
	{
		// apply endifs
		const u8 v = (u8)code.address;
		if (code_execution_counter >= v)
			code_execution_counter -= v;
		else
		{
			// too many endifs
			// no it's not, i gotta fix my code execution on/off stuff
			code_execution_counter = 0;
		}

		const bool is_else = !!(code.address & 0x00100000);
		// apply else
		if (is_else)
			if (code_execution_counter <= 1)
				code_execution_counter ^= 1;
	}
		break;

		// CST7 : End of Code
	case 0x7 :
		// tell the code handler that there are no more codes in the code list
		// TODO: should this always stop all codes, even if execution is off?
		if (CodeExecution())
			code_execution_counter = -1;	// silly maybe
		break;

		// INVALID SUBTYPE
	default :
		return false;
		break;
	}

	return true;
}

}	// namespace Gecko
