#include "global.h"
#define DINPUT_VERSION 0x0700
#define DIRECTINPUT_VERSION 0x0700
#define INITGUID
#include <dinput.h>

// Make sure the dx libs are included
#pragma comment (lib, "dinput.lib")		// link with directinput lib
#pragma comment (lib, "dxguid.lib")		// link with dxguid lib

// Just really convenient
#define IS_HIGH_BIT_SET(b) ( (((BYTE)b)&0x80) != 0 )

// Defines the amount that a keypress influences the input.turnamount variable
//#define KEYBOARD_TURNAMOUNT 10
// Defines the scale factor for the mouse -- perhaps not necessary?
#define GLOBAL_MOUSE_SENSITIVITY_SCALE 0.05


// Local structures definitions:
//------------------------------------------------------------
// Defines the input configuration:
//   key config, mouse config
struct InputConfig_type
{
	short int left;
	short int right;
	short int thrust;
	short int brake;
	short int fire;
	short int shield;
	short int score;
	short int chat;

	float mouseSensitivity;
	bool  mouseInvert;
};

// Some handy status flags
bool mouseAcquired;
bool keyboardAcquired;
bool wantKbAcquire;
bool wantMouseAcquire;

// Externalized global variables:
//------------------------------------------------------------
// Contains the status of the inputs at all times
struct InputStatus_type input;

// File-specific (static) global variables:
//------------------------------------------------------------
// Defines the keyboard configuration, mouse config
static struct InputConfig_type inputConfig;
// The DirectInput7 variables:
static IDirectInput7		*pDI;
static IDirectInputDevice7	*pKeyboard, *pMouse;

// Useful functions
static void clearInput(bool resetPos);

void checkInput(void)
{
	clearInput(false);

	if (mouseAcquired)
	{
		DIMOUSESTATE2 mouse;
		pMouse->GetDeviceState(sizeof(mouse), &mouse);

		long& dx = mouse.lX;
		long& dy = mouse.lY;
		long& dz = mouse.lZ;
		// high order bit of each of 8 bytes indicates pressed status
		for (int i=0; i < 8; i++)
			input.mousebuttons[i] = IS_HIGH_BIT_SET(mouse.rgbButtons[i]);

		input.turnAmount = dx * inputConfig.mouseSensitivity;

		if (input.mousebuttons[0])			input.fire |= true;
		if (input.mousebuttons[1])			input.thrust |= true;
		if (input.mousebuttons[2])			input.shield |= true;

		input.mousex += dx;
		input.mousey -= dy;
		input.mousez += dz;
	}
	if (keyboardAcquired)
	{
		BYTE state[256];
		pKeyboard->GetDeviceState(sizeof(state), &state);

		input.left   |= IS_HIGH_BIT_SET(state[inputConfig.left]);
		input.right  |= IS_HIGH_BIT_SET(state[inputConfig.right]);
		input.thrust |= IS_HIGH_BIT_SET(state[inputConfig.thrust]);
		input.brake  |= IS_HIGH_BIT_SET(state[inputConfig.brake]);
		input.fire   |= IS_HIGH_BIT_SET(state[inputConfig.fire]);
		input.shield |= IS_HIGH_BIT_SET(state[inputConfig.shield]);
		input.score  |= IS_HIGH_BIT_SET(state[inputConfig.score]);
		input.chat   |= IS_HIGH_BIT_SET(state[inputConfig.chat]);

		input.escKey    = IS_HIGH_BIT_SET(state[DIK_ESCAPE]);
		input.upkey     = IS_HIGH_BIT_SET(state[DIK_UP]);
		input.downkey   = IS_HIGH_BIT_SET(state[DIK_DOWN]);
		input.selectkey = IS_HIGH_BIT_SET(state[DIK_RETURN]);
		input.pause     = IS_HIGH_BIT_SET(state[DIK_PAUSE]);
		input.pause    |= IS_HIGH_BIT_SET(state[DIK_P]);
	}
}

void glutMouse(int button, int state, int x, int y)
{
	InputEvent_type e = { IE_MOUSE, 0, button, state, x, y };
	game.HandleInput(e);
}

extern LARGE_INTEGER lasttime;

void glutKeyboard(unsigned char key, int x, int y)
{
	InputEvent_type e = { IE_KB, key, 0, 0, x, y };
	bool handled = game.HandleInput(e);

	if (!handled)
	{
		//printf("\nDefault keyboard handler for [%c] (%d)\n",key,(int)key);
		switch (key)
		{
			case 'k':
			case 'K':
				keyboardAcquire(!isKeyboardAcquired());
				printf(" Setting KB focus to: %s\n",isKeyboardAcquired()?"Acquired":"Unacquired");
				wantKbAcquire = isKeyboardAcquired();
				break;
			case 'm':
			case 'M':
				mouseAcquire(!isMouseAcquired());
				printf(" Setting mouse focus to: %s\n",isMouseAcquired()?"Acquired":"Unacquired");
				wantMouseAcquire = isMouseAcquired();
				break;
			case 'p':
			case 'P':
				game.SetState(GS_PLAYING);
				game.world.Reset();
				game.world.tank[0].Spawn();
				break;
			case 'i':
				if (!game.netclient.Init())
					printf("Could not init net client!!\n");
				break;

			default:
				//InputEvent_type e = { IE_KB, key, 0, 0, x, y };
				//game.HandleInput(e);
				break;
		}
	}
}


//
//---------------------------------------------------------------------
//-----------------------   UTILITY FUNCTIONS   -----------------------
//---------------------------------------------------------------------
//

bool setupInput(void)
{
	// Set default key mappings
	inputConfig.left    = DIK_LEFT;
	inputConfig.right   = DIK_RIGHT;
	inputConfig.thrust  = DIK_UP;
	inputConfig.brake   = DIK_DOWN;
	inputConfig.shield  = DIK_Z;
	inputConfig.fire    = DIK_LSHIFT;
	inputConfig.score   = DIK_TAB;
	inputConfig.chat    = DIK_T;

	inputConfig.mouseSensitivity = 2.0 * GLOBAL_MOUSE_SENSITIVITY_SCALE;
	inputConfig.mouseInvert = false;

	InitDirectInput();

	return true;
}

void cleanupInput(void)
{
	ExitDirectInput();
}

// Handle gaining/losing window focus
void focusChange(bool hasFocus)
{
	if (wantKbAcquire)
	{
		keyboardAcquire(hasFocus);
		wantKbAcquire = true;
	}
	if (wantMouseAcquire)
	{
		mouseAcquire(hasFocus);
		wantMouseAcquire = true;
	}
}

void clearInput(bool resetPos)
{
	if (resetPos)
		memset(&input,0,sizeof(input));
	else
		memset(&input,0,sizeof(input)-3*sizeof(int));		// Everything but the last two vars
}

// Initialise DirectInput and get the keyboard
void InitDirectInput()
{
	HWND hWndMain = glutarConfig.hWnd;

	DirectInputCreateEx(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
		IID_IDirectInput7, (void **)&pDI, NULL);

	pDI->CreateDeviceEx(GUID_SysKeyboard, IID_IDirectInputDevice7,
		(void **)&pKeyboard, NULL);
	pKeyboard->SetDataFormat(&c_dfDIKeyboard);

	// Set the cooperative level 
	pKeyboard->SetCooperativeLevel(hWndMain, 
		DISCL_BACKGROUND | DISCL_NONEXCLUSIVE); 

	pDI->CreateDeviceEx(GUID_SysMouse, IID_IDirectInputDevice7,
		(void **)&pMouse, NULL);
	pMouse->SetDataFormat(&c_dfDIMouse2);

	// Set the cooperative level 
	pMouse->SetCooperativeLevel(hWndMain, 
		DISCL_FOREGROUND | DISCL_EXCLUSIVE);

	mouseAcquired = false;
	keyboardAcquired = false;
}

void ExitDirectInput()
{
	if (pMouse)
	{
		//if (mouseAcquired)
			pMouse->Unacquire();
		pMouse->Release();
		pMouse = NULL;
	}

	if (pKeyboard)
	{
		//if (keyboardAcquired)
			pKeyboard->Unacquire();
		pKeyboard->Release();
		pKeyboard = NULL;
	}

	if (pDI)
	{
		pDI->Release();
		pDI = NULL;
	}
}

bool keyboardAcquire(bool acquire)
{
	if (!pKeyboard)
		return keyboardAcquired = false;

	wantKbAcquire = acquire;

	if (acquire)
	{
		HRESULT ret = pKeyboard->Acquire();
		keyboardAcquired = (ret==DI_OK || ret==S_FALSE);
	}
	else
	{
		HRESULT ret = pKeyboard->Unacquire();
		keyboardAcquired = !(ret==DI_OK || ret==DI_NOEFFECT);
	}

	return keyboardAcquired;
}
bool isKeyboardAcquired()
{
	return keyboardAcquired;
}
bool mouseAcquire(bool acquire)
{
	if (!pMouse)
		return mouseAcquired = false;

	wantMouseAcquire = acquire;

	if (acquire)
	{
		HRESULT ret = pMouse->Acquire();
		mouseAcquired = (ret==DI_OK || ret==S_FALSE);
	}
	else
	{
		HRESULT ret = pMouse->Unacquire();
		mouseAcquired = !(ret==DI_OK || ret==DI_NOEFFECT);
	}

	return mouseAcquired;
}
bool isMouseAcquired()
{
	return mouseAcquired;
}
