// Run module
#include "burner.h"
#include <iostream>
#include <vector>
#include <xtl.h>
#include "xbinput.h"
using namespace std;
extern int nAppVirtualFps;
extern int bDrvOkay;
int bRunPause = 0;
int bAltPause = 0;
static int nGameWidth = 0, nGameHeight = 0;				// Game screen size
static int nGameImageWidth, nGameImageHeight;
static unsigned char* pVidSFullImage = NULL;
unsigned int (__cdecl *VidHighCol) (int r, int g, int b, int i);
int nRotateGame = 0;
int bAlwaysDrawFrames = 0;
static bool bShowFPS = false;
//extern int MakeScreenShot();
static unsigned int nDoFPS = 0;
int kNetGame = 0;							// Non-zero if Kaillera is being used
static unsigned int nNormalLast = 0;		// Last value of timeGetTime()
static int nNormalFrac = 0;					// Extra fraction we did
static bool bAppDoStep = 0;
static bool bAppDoFast = 0;
static int nFastSpeed = 6;
extern "C" int dprintf(char *format, ...);
unsigned char* pVidImage = NULL;				// Memory buffer
int nVidImageWidth = 304;		// Memory buffer size
int nVidImageHeight = 224;		//
int nVidImageLeft = 0, nVidImageTop = 0;		// Memory buffer visible area offsets
int nVidImagePitch = 0, nVidImageBPP = 0;		// Memory buffer pitch and bytes per pixel
int nVidImageDepth = 0;	
extern void CheckInput();
float fps = 0;
int nVidRotationAdjust = 2;
int nDSoundFps = 0;
extern void ReadInput();
extern XBGAMEPAD g_Gamepads[4];
extern int DxSoundStop();
extern void* osd_malloc(int n);
extern void GameOptionMenu();
extern bool exitGameMenu;
extern int DxSoundStop();
extern void InitFilter();
extern void ResizeTexture();
extern void DirectScreen();
extern int DefaultController;
extern float RotationDegrees;
extern unsigned char* _2xSaiBuffer;
extern IDirect3DDevice8 *Device;
extern IDirect3DTexture8 *TexScreen;
extern void MakeVertexList();
extern int SetupMatricesEmulation();
extern bool setDisplay;
extern void Present();
extern int FlickerFilter;
extern int Soften;
extern bool PrintDip();
struct Macro {
	bool enabled;
	int buttonSet;
	std::vector<int> buttons;
	std::string desc;
};
extern std::vector<Macro> MacroSet[4];
extern std::vector<string> OptionEmulationVideoValueItems;
extern void ReturnToGUI();

static int GetInput(bool bCopy)
{
	ReadInput();
	if (g_Gamepads[DefaultController].wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) {
		exitGameMenu = false;
		DxSoundStop();
		GameOptionMenu();
	}
	for(int n = 0; n < XGetPortCount(); n++) {
		if(g_Gamepads[n].hDevice) {
			for(int i = 0; i < MacroSet[n].size(); i++) {
				if(MacroSet[n][i].enabled) {
					if(g_Gamepads[n].bAnalogButtons[MacroSet[n][i].buttonSet]) {
						for(int d = 0; d < MacroSet[n][i].buttons.size(); d++) {
							g_Gamepads[n].bAnalogButtons[MacroSet[n][i].buttons[d]] = (BYTE)1;
						}
						g_Gamepads[n].bAnalogButtons[MacroSet[n][i].buttonSet] = (BYTE)0;
					}
				}
			}
		}
	}

	InputMake(bCopy);
	return 0;
}

static void CalculateFPS() {
	static time_t fpstimer;
	static unsigned int nPreviousFrames;
	time_t temptime = clock();
	fps = (float)(nFramesRendered - nPreviousFrames) * CLOCKS_PER_SEC / (temptime - fpstimer);
	fpstimer = temptime;
	nPreviousFrames = nFramesRendered;
	//dprintf("FPS: %f\n", fps);
}

static int RunFrame(int bDraw, int bPause)
{
	nFramesEmulated++;
	nCurrentFrame++;
	GetInput(true);					// Update inputs
	if (bDraw) {
		dprintf("Start Draw\n");
		nFramesRendered++;
		if(!pVidImage)
			dprintf("Error pVidImage\n");
		pBurnDraw = pVidImage;
		BurnDrvFrame();
		DirectScreen();
	} else {								// frame skipping
		pBurnDraw = NULL;					// Make sure no image is drawn
		BurnDrvFrame();
	}
	if (nDoFPS < nFramesRendered) {
		CalculateFPS();
		nDoFPS = nFramesRendered + 60;
	}
	return 0;
}

static int RunGetNextSound(int bDraw)
{
	dprintf("RunGetNextSound Start");
	if (nAudNextSound == NULL) {
		return 1;
	}
	if (bRunPause) {
		if (bAppDoStep) {
			RunFrame(bDraw, 0);
			memset(nAudNextSound, 0, nAudSegLen << 2);	// Write silence into the buffer
		} else {
			RunFrame(bDraw, 1);
		}
		bAppDoStep = 0;									// done one step
		return 0;
	}
	if (bAppDoFast) {									// do more frames
		for (int i = 0; i < nFastSpeed; i++) {
			RunFrame(0, 0);
		}
	}
	pBurnSoundOut = nAudNextSound;
	RunFrame(bDraw, 0);
	if (bAppDoStep) {
		memset(nAudNextSound, 0, nAudSegLen << 2);		// Write silence into the buffer
	}
	bAppDoStep = 0;										// done one step
	dprintf("RunGetNextSound End");
	return 0;
}

// The main message loop
int RunMessageLoop()
{
	dprintf("RunMessageLoop() Start\n");
	nFramesEmulated = 0;
	nCurrentFrame = 0;
	nNormalLast = 0; nNormalFrac = 0;
	// Reset FPS display
	nDoFPS = 0;
	int nMemLen = 0;
	// Allocate an extra line above and below the image to accomodate effects
	nRotateGame = 0;
	nVidRotationAdjust = 2;
	if (bDrvOkay) {
		BurnDrvGetVisibleSize(&nGameWidth, &nGameHeight);

		if (BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) {
			if (nVidRotationAdjust & 1) {
				int n = nGameWidth;
				nGameWidth = nGameHeight;
				nGameHeight = n;
				nRotateGame |= (nVidRotationAdjust & 2);
			} else {
				nRotateGame |= 1;
			}
		}

		if (BurnDrvGetFlags() & BDF_ORIENTATION_FLIPPED) {
			nRotateGame ^= 2;
		}
	}
	else {
		dprintf("Driver Error\n");
		ReturnToGUI();
	}
	if(nRotateGame == 1) {
		RotationDegrees = 90;
		OptionEmulationVideoValueItems[8] = "90 Degrees";
	}
	if(nRotateGame == 3) {
		RotationDegrees = 270;
		OptionEmulationVideoValueItems[8] = "270 Degrees";
	}

	if (nRotateGame & 1) {
		nVidImageWidth = nGameHeight;
		nVidImageHeight = nGameWidth;
	} else {
		nVidImageWidth = nGameWidth;
		nVidImageHeight = nGameHeight;
	}
	dprintf("ResizeTexture()\n");
	nVidImageDepth = 16;
	nVidImageBPP = (nVidImageDepth + 7) >> 3;
	nBurnBpp = nVidImageBPP;								// Set Burn library Bytes per pixel
	// Use our callback to get colors:
	SetBurnHighCol(nVidImageDepth);
	nVidImagePitch = nVidImageWidth * ((nVidImageDepth + 7) >> 3);
	nMemLen = ((nVidImageHeight + 2) * nVidImagePitch) * 2;
	dprintf("pVidSFullImage = (unsigned char*)osd_malloc(nMemLen);\n");
	pVidSFullImage = (unsigned char*)osd_malloc(nMemLen);
	memset(pVidSFullImage, 0, nMemLen);
	pVidImage = pVidSFullImage + nVidImagePitch;
	if(!pVidImage)
		dprintf("Error pVidImage\n");
	pBurnDraw = pVidImage;
	nBurnPitch = nVidImagePitch;

	_2xSaiBuffer = (unsigned char*)osd_malloc(nVidImageWidth * nVidImageHeight * 2);
	ZeroMemory(_2xSaiBuffer, nVidImageWidth * nVidImageHeight * 2);
	_2xSaiBuffer += nVidImageBPP;
	ResizeTexture();
	SetupMatricesEmulation();
	MakeVertexList();
	Device->SetTexture(0,TexScreen);


	AudSetCallback(RunGetNextSound);
	AudSoundPlay();
	MEMORYSTATUS memStat;
	size_t newCacheSize;
	dprintf("InitFilter\n");
	InitFilter();
	dprintf("while(1)\n");

	Device->Clear(0,NULL,D3DCLEAR_TARGET,0,1.0f,0);
	Present();
	setDisplay = false;
	Device->SetFlickerFilter(FlickerFilter);
	Device->SetSoftDisplayFilter(Soften);
	while(1) {
		GetInput(true);	
		if (bAudPlaying) {
			AudSoundCheck();
		}	
		else {
			dprintf("No sound\n");
		}
	}
	return 0;
}

