// =======================================-====================================
 // 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;
 }