// Burner DipSwitches Dialog module
#include "burner.h"
#include <vector>
#include <string.h>
using namespace std;

struct DIPItem {
	string description;
	vector<string> valueDescription;
	vector<unsigned char> value;
	vector<int> enabled;
	vector<unsigned char> mask;
};

struct DIPSetting {
	int dipID;
	unsigned char value;
	vector<DIPItem> dipItems;
};

extern vector<DIPSetting> arcradeSettings;

struct DipSwitchSetting {
		string description;
		string value;
};

extern vector <DipSwitchSetting> DipSwitchSettings;
extern int GetFileInteger(FILE * f);
extern std::string ConfigPath;
extern "C" int dprintf(char *format, ...);
static unsigned char nPrevDIPSettings[4];
static unsigned int nDIPGroup;
static int nDIPOffset;
static bool bOK;

static void InpDIPSWGetOffset()
{
	BurnDIPInfo bdi;

	nDIPOffset = 0;
	for (int i = 0; BurnDrvGetDIPInfo(&bdi, i) == 0; i++) {
		if (bdi.nFlags == 0xF0) {
			nDIPOffset = bdi.nInput;
			break;
		}
	}
}

void UpdateDIPS() {
	int i = 0;
	BurnDIPInfo bdi;
	struct GameInp* pgi;
	
	InpDIPSWGetOffset();
	
	//dprintf("Building DIP value and building menu start\n");
	DipSwitchSettings.clear();
	for(int n = 0; n < arcradeSettings.size(); n++) {
		arcradeSettings[n].value = 0;
		//dprintf("Arcade value before compute: %d\n", arcradeSettings[n].value);
		for(int k = 0; k < arcradeSettings[n].dipItems.size(); k++) {
			for(int e = 0; e < arcradeSettings[n].dipItems[k].valueDescription.size(); e++) {
				if(arcradeSettings[n].dipItems[k].enabled[e]) {
					DipSwitchSetting d;
					arcradeSettings[n].value |= arcradeSettings[n].dipItems[k].value[e];
					d.description = arcradeSettings[n].dipItems[k].description;
					d.value = arcradeSettings[n].dipItems[k].valueDescription[e];
					DipSwitchSettings.push_back(d);
					break;
				}
			}
		}
		//dprintf("Arcade value after compute: %d\n", arcradeSettings[n].value);
	}
	//dprintf("Building DIP value and building menu end\n");

	//dprintf("Setting DIP value start\n");
	for(int e = 0; e < arcradeSettings.size(); e++) {
		int i = 0;
		while (BurnDrvGetDIPInfo(&bdi, i) == 0) {
			if ((bdi.nFlags == 0xFF) &&
				(bdi.nInput == arcradeSettings[e].dipID)) {
					//dprintf("Setting Value Start\n");
					pgi = GameInp + bdi.nInput + nDIPOffset;
					pgi->Input.Constant.nConst = arcradeSettings[e].value;
					//dprintf("Setting Value End\n");
					break;
				}
				i++;
		}
	}
	//dprintf("Setting DIP value end\n");

	char fileName[260];
	sprintf(fileName,"%s%s%s%s", ConfigPath.c_str(), "\\dip\\", BurnDrvGetTextA(0), ".ini");
	FILE *f = fopen(fileName, "w+");
	if(f) {
		//dprintf("Writing file start\n");
		for(int n = 0; n < arcradeSettings.size(); n++) {
			for(int k = 0; k < arcradeSettings[n].dipItems.size(); k++) {
				for(int e = 0; e < arcradeSettings[n].dipItems[k].valueDescription.size(); e++) {
					//dprintf("Writing: %d\n", arcradeSettings[n].dipItems[k].enabled[e]);
					fprintf(f, "DIP=%d\n", arcradeSettings[n].dipItems[k].enabled[e]);
				}
			}
		}
		fclose(f);
		//dprintf("Writing file end\n");
	}
}

void SetDip(char* description, char* value) {
	for(int n = 0; n < arcradeSettings.size(); n++) {
		for(int k = 0; k < arcradeSettings[n].dipItems.size(); k++) {
			if((_tcscmp(description, arcradeSettings[n].dipItems[k].description.c_str()) == 0)) {
				//dprintf("Found the description\n");
				for(int e = 0; e < arcradeSettings[n].dipItems[k].valueDescription.size(); e++) {
					if((_tcscmp(value, arcradeSettings[n].dipItems[k].valueDescription[e].c_str()) == 0)) {
						//dprintf("Found the value description\n");
						arcradeSettings[n].dipItems[k].enabled[e] = 0;
						if((e + 1) > arcradeSettings[n].dipItems[k].enabled.size() - 1) {
							arcradeSettings[n].dipItems[k].enabled[0] = 1;
						}
						else {
							arcradeSettings[n].dipItems[k].enabled[e + 1] = 1;
						}
						//dprintf("Updating Dips\n");
						UpdateDIPS();
						return;
					}
				}
			}
		}
	}
}

bool PrintDip() {
	//try {
	int i = 0;
	BurnDIPInfo bdi;
	BurnDIPInfo bdiTemp;
	struct GameInp* pgi;

	InpDIPSWGetOffset();

	while (BurnDrvGetDIPInfo(&bdi, i) == 0) {
		if(bdi.nFlags == 0xFF) {
			if(i != 0) {
				BurnDrvGetDIPInfo(&bdiTemp, i - 1);
				if(bdiTemp.nFlags != 0xFF) {
					arcradeSettings.clear();
					//dprintf("Error 1 DIP\n");
					return false;
				}
			}
			DIPSetting newDIP;
			newDIP.dipID = bdi.nInput;
			newDIP.value = bdi.nSetting; 
			//dprintf("Initial Value: %d\n", newDIP.value);
			arcradeSettings.push_back(newDIP);
		}
		i++;
	}

	/*
	int nInput;
	unsigned char nFlags;
	unsigned char nMask;
	unsigned char nSetting;
	char* szText;
	*/
	//arcradeSettings[0].value = 0x00;
	//arcradeSettings[1].value = 0x00;
	//arcradeSettings[2].value = 0x60;

	i = 0;
	for(int n = 0; n < arcradeSettings.size(); n++) {
		while (BurnDrvGetDIPInfo(&bdi, i) == 0) {
			if(bdi.nFlags == 0xFE) {
				i++;
				BurnDrvGetDIPInfo(&bdi, i);
				if(bdi.nInput == arcradeSettings[n].dipID) {
					i--;
					BurnDrvGetDIPInfo(&bdi, i);
					DIPItem temp;
					if(!bdi.szText) {
						arcradeSettings.clear();
						//dprintf("Error 2 DIP\n");
						return false;
					}
					temp.description = bdi.szText;
					//dprintf("Description: %s\n", temp.description.c_str());
					int count = bdi.nSetting;
					for(int u = 0; u < count; u++) {
						i++;
						BurnDrvGetDIPInfo(&bdi, i);
						if(!bdi.szText) {
							arcradeSettings.clear();
							//dprintf("Error 3 DIP\n");
							return false;
						}
						temp.valueDescription.push_back(bdi.szText);
						temp.value.push_back(bdi.nSetting);
						temp.mask.push_back(bdi.nMask);

						if((arcradeSettings[n].value & bdi.nMask) == bdi.nSetting) {
							temp.enabled.push_back(1);
						}
						else {
							temp.enabled.push_back(0);
						}
					}
					arcradeSettings[n].dipItems.push_back(temp);
				}
				else {
					i--;
					break;
				}
			}
			i++;
		}
	}

	for(int n = 0; n < arcradeSettings.size(); n++) {
		if(arcradeSettings[n].dipItems.size() == 0) {
			arcradeSettings.clear();
			//dprintf("Error 4 DIP\n");
			return false;
		}
	}

	for(int n = 0; n < arcradeSettings.size(); n++) {
		//dprintf("DIP: %d\n", arcradeSettings[n].dipID);
		for(int k = 0; k < arcradeSettings[n].dipItems.size(); k++) {
			//dprintf("Description: %s\n", arcradeSettings[n].dipItems[k].description.c_str());
			for(int e = 0; e < arcradeSettings[n].dipItems[k].valueDescription.size(); e++) {
				//dprintf("Setting: %s\n", arcradeSettings[n].dipItems[k].valueDescription[e].c_str());
				//dprintf("Value: %d\n", arcradeSettings[n].dipItems[k].value[e]);
			}
		}
	}

	char fileName[260];
	sprintf(fileName,"%s%s%s%s", ConfigPath.c_str(), "\\dip\\", BurnDrvGetTextA(0), ".ini");
	FILE *f = fopen(fileName, "r");
	if(f) {
		for(int n = 0; n < arcradeSettings.size(); n++) {
			arcradeSettings[n].value = 0;
				for(int k = 0; k < arcradeSettings[n].dipItems.size(); k++) {
					for(int e = 0; e < arcradeSettings[n].dipItems[k].valueDescription.size(); e++) {
						arcradeSettings[n].dipItems[k].enabled[e] = GetFileInteger(f);
					}
				}
		}
		fclose(f);
	}
	
	////dprintf("Before UpdateDIPS\n");
	//UpdateDIPS();
	////dprintf("After UpdateDIPS\n");

	//dprintf("Build Display start\n");
	for(int n = 0; n < arcradeSettings.size(); n++) {
		for(int k = 0; k < arcradeSettings[n].dipItems.size(); k++) {
			for(int e = 0; e < arcradeSettings[n].dipItems[k].valueDescription.size(); e++) {
				if(arcradeSettings[n].dipItems[k].enabled[e]) {
					DipSwitchSetting d;
					d.description = arcradeSettings[n].dipItems[k].description;
					d.value = arcradeSettings[n].dipItems[k].valueDescription[e];
					DipSwitchSettings.push_back(d);
					break;
				}
			}
		}
	}
	//dprintf("Build Display end\n");

	//dprintf("\n");
	for(int i = 0; i < DipSwitchSettings.size(); i++) {
		//dprintf("%s: %s\n", DipSwitchSettings[i].description.c_str(), DipSwitchSettings[i].value.c_str());
	}
	//dprintf("\n");

	if(DipSwitchSettings.size() == 0) {
		arcradeSettings.clear();
		return false;
	}
	/*} catch(...) {
		arcradeSettings.clear();
	}*/
	return true;
}

/*
////dprintf("%s\n", bdi.szText); 
		
		
		//pgi = GameInp + bdi.nInput + nDIPOffset;
		//pgi->Input.Constant.nConst = (pgi->Input.Constant.nConst & ~bdi.nMask) | (bdi.nSetting & bdi.nMask);
		//}
*/
void InpDIPSWResetDIPs()
{
	int i = 0;
	BurnDIPInfo bdi;
	struct GameInp* pgi;

	InpDIPSWGetOffset();

	while (BurnDrvGetDIPInfo(&bdi, i) == 0) {
		if (bdi.nFlags == 0xFF) {
			pgi = GameInp + bdi.nInput + nDIPOffset;
			pgi->Input.Constant.nConst = (pgi->Input.Constant.nConst & ~bdi.nMask) | (bdi.nSetting & bdi.nMask);
		}
		i++;
	}
}

static bool CheckSetting(int i)
{
	BurnDIPInfo bdi;
	BurnDrvGetDIPInfo(&bdi, i);
	struct GameInp* pgi = GameInp + bdi.nInput + nDIPOffset;

	if ((pgi->Input.Constant.nConst & bdi.nMask) == bdi.nSetting) {
		unsigned char nFlags = bdi.nFlags;
		if ((nFlags & 0x0F) <= 1) {
			return true;
		} else {
			for (int j = 1; j < (nFlags & 0x0F); j++) {
				BurnDrvGetDIPInfo(&bdi, i + j);
				pgi = GameInp + bdi.nInput + nDIPOffset;
				if (nFlags & 0x80) {
					if ((pgi->Input.Constant.nConst & bdi.nMask) == bdi.nSetting) {
						return false;
					}
				} else {
					if ((pgi->Input.Constant.nConst & bdi.nMask) != bdi.nSetting) {
						return false;
					}
				}
			}
			return true;
		}
	}
	return false;
}

static int InpDIPSWInit()
{
	BurnDIPInfo bdi;
	struct GameInp *pgi;

	InpDIPSWGetOffset();

	for (int i = 0, j = 0; BurnDrvGetDIPInfo(&bdi, i) == 0; i++) {
		if (bdi.nInput >= 0  && bdi.nFlags == 0xFF) {
			pgi = GameInp + bdi.nInput + nDIPOffset;
			nPrevDIPSettings[j] = pgi->Input.Constant.nConst;
			j++;
		}
	}

	return 0;
}

static void InpDIPSWCancel()
{
	if (!bOK) {
		int i = 0, j = 0;
		BurnDIPInfo bdi;
		struct GameInp *pgi;
		while (BurnDrvGetDIPInfo(&bdi, i) == 0) {
			if (bdi.nInput >= 0 && bdi.nFlags == 0xFF) {
				pgi = GameInp + bdi.nInput + nDIPOffset;
				pgi->Input.Constant.nConst = nPrevDIPSettings[j];
				j++;
			}
			i++;
		}
	}
}

