// =======================================-====================================
// Filename: DIMouse.h
// Description: Header file for the DirectInput Mouse class
//
// This code corresponds to DirectX Techniques Series 1 Tutorial 5, at
// www.32bits.co.uk
// ============================================================================


#include "DIMouse.h"

// =============================================================================
// Constructor and Destructor
// =============================================================================

CDIMouse::CDIMouse()
{
	m_pDIObject = NULL;
	m_pDIMouseDevice = NULL;

	m_iCursorX = 0;
	m_iCursorY = 0;
	m_iHotSpotX = 0;
	m_iHotSpotY = 0;
}


CDIMouse::~CDIMouse()
{
	// All DInput devices must be properly released or problems will occur.
	if(m_pDIMouseDevice != NULL)
	{
		m_pDIMouseDevice->Unacquire();
		m_pDIMouseDevice->Release();
		m_pDIMouseDevice = NULL;
	}
}

// =============================================================================
// Initialisation
// =============================================================================

HRESULT CDIMouse::Initialise(HWND hWnd, int iWindowWidth, int iWindowHeight)
{
	HRESULT rslt = S_OK;

	// First, create the DirectInput object (exactly the same as Direct3DCreate9())
	rslt = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
							  IID_IDirectInput8, (void**)&m_pDIObject, NULL);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Could not create DInput object"); }
								 

	// Next, create a device for the mouse
	rslt = m_pDIObject->CreateDevice(GUID_SysMouse, &m_pDIMouseDevice, NULL);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Could not create DInput device"); }


	// Define the data format for the mouse device. c_dfDIMouse is a precreated struct
	// provided with DInput that is already configured for a generic mouse.
	rslt = m_pDIMouseDevice->SetDataFormat(&c_dfDIMouse);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Could not configure mouse data."); }

	// Set the cooperation level - how DInput should work with this device & the rest of Windows
	rslt = m_pDIMouseDevice->SetCooperativeLevel(hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Could not set cooperation."); }

	// All done, now we need to acquire the device (grab it for our own use)
	rslt = m_pDIMouseDevice->Acquire();
	if(FAILED(rslt)) { return D3DError(rslt, __LINE__, __FILE__, "Could not acquire mouse."); }

	
/*
	// This code is not needed for the tutorial sample. It's provided here to demonstrate how
	// to obtain the features of a DirectInput device. Search the SDK for
	// IDirectInputDevice8::GetCapabilities() and DIDEVCAPS for more info.

	DIDEVCAPS kMouseCaps;
	ZeroMemory(&kMouseCaps, sizeof(DIDEVCAPS));
	kMouseCaps.dwSize = sizeof(DIDEVCAPS);
	m_pDIMouseDevice->GetCapabilities(&kMouseCaps);
*/

	m_iWindowWidth = iWindowWidth;
	m_iWindowHeight = iWindowHeight;
	
	return S_OK;
}


// =============================================================================
// Updates the position of the cursor
// =============================================================================

BOOL CDIMouse::Update()
{
	if(m_pDIObject == NULL || m_pDIMouseDevice == NULL)
		return FALSE;

	// If we can't get the device state, we've lost the device so we need to try to get it back.
	// This is very similar to a lost D3D device.
	if(FAILED(m_pDIMouseDevice->GetDeviceState(sizeof(DIMOUSESTATE),(LPVOID)&m_kMouseState)))
	{
		if(FAILED(m_pDIMouseDevice->Acquire()))
		{
			return FALSE;
		}
		else
		{
			if(FAILED(m_pDIMouseDevice->GetDeviceState(sizeof(DIMOUSESTATE),(LPVOID)&m_kMouseState)))
				return FALSE;
		}
	}

	// DInput *always* provides relative movement. IE: DInput says "The mouse moved 5 units", not
	// "The mouse is now at X, Y". To get absolute coordinates, we have to track it manually.

	m_iCursorX += m_kMouseState.lX;

	if(m_iCursorX < 0)
		m_iCursorX = 0;
	
	if(m_iCursorX > m_iWindowWidth)
		m_iCursorX = m_iWindowWidth;
	
	m_iCursorY += m_kMouseState.lY;
	
	if(m_iCursorY < 0)
		m_iCursorY = 0;
	
	if(m_iCursorY > m_iWindowHeight)
		m_iCursorY = m_iWindowHeight;

	return TRUE;
}

// =============================================================================
// Draws the cursor on screen
// =============================================================================

HRESULT CDIMouse::Render(LPDIRECT3DDEVICE9 pDevice)
{
	// This is just a very simple way to display the mouse position. We use
	// Clear to draw a rect where the cursor is. Normally this would of course
	// be a sprite rendered at the mouse position (which is why we use a hotspot,
	// to allow different cursors to be used)
	
	D3DRECT rcCursor;
	rcCursor.x1 = m_iCursorX; rcCursor.y1 = m_iCursorY;
	rcCursor.x2 = m_iCursorX + 10; rcCursor.y2 = m_iCursorY + 10;
	pDevice->Clear(1, &rcCursor, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,150,200), 1.0f, 0);

	return S_OK;
}



// =============================================================================
// Accessor methods for mouse states
// =============================================================================

int CDIMouse::GetRelativeX()
{
	return m_kMouseState.lX;
}

int CDIMouse::GetRelativeY()
{
	return m_kMouseState.lY;
}

int CDIMouse::GetAbsoluteX()
{
	return m_iCursorX;
}

int CDIMouse::GetAbsoluteY()
{
	return m_iCursorY;
}

void CDIMouse::GetAbsolutePos(POINT* kMousePos)
{
	if(kMousePos != NULL)
	{
		kMousePos->x = m_iCursorX + m_iHotSpotX;
		kMousePos->y = m_iCursorY + m_iHotSpotY;
	}

	return;
}

void CDIMouse::SetHotSpot(int iX, int iY)
{
	m_iHotSpotX = iX;
	m_iHotSpotY = iY;
}


BOOL CDIMouse::IsMouseButtonDown(int iButton, POINT* kClickPoint)
{
	// Logical AND the button states to check if the mouse button flag is set. The iButton
	// parameter is a number representing the button to check, or use the #define's in
	// the header file (DIMOUSE_LEFTBUTTON etc).

	if(m_kMouseState.rgbButtons[iButton] & 0xF0)
	{
		// If it is, the button is clicked, so set kClickPoint to the current hotspot
		// location (ie: where the mouse was clicked).
		if(kClickPoint != NULL)
		{
			kClickPoint->x = m_iCursorX + m_iHotSpotX;
			kClickPoint->y = m_iCursorY + m_iHotSpotY;
		}

		return TRUE;
	}

	// We could else{} this call, but we might want to check for other
	// buttons later

	if(kClickPoint != NULL)
	{
		kClickPoint->x = -1;
		kClickPoint->y = -1;
	}

	return FALSE;
}
