// 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() {

	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++;
		}
	}
}

