/* **********************************************************************************************************************
   Included Header Files
   **********************************************************************************************************************/

#include "VDInput.h"



/* **********************************************************************************************************************
   Constructor
   **********************************************************************************************************************/

VDInput::VDInput() {
    directInput = NULL;
    keyboard = NULL;
    mouse = NULL;
    SecureZeroMemory(keys, sizeof(BYTE) * 256);
    SecureZeroMemory(&mouseState, sizeof(DIMOUSESTATE));
};



/* **********************************************************************************************************************
   Destructor
   **********************************************************************************************************************/

VDInput::~VDInput() {
    FreeMemory();
};



/* **********************************************************************************************************************
   FreeMemory
   ----------
   Releases all DirectInput COM objects.
   **********************************************************************************************************************/

void VDInput::FreeMemory() {
    if (mouse != NULL) {
        (void) mouse->Unacquire();  // Unacquire() returns DI_NOEFFECT if it fails
        (void) mouse->Release();
        mouse = NULL;
    };
    if (keyboard != NULL) {
        (void) keyboard->Unacquire();
        (void) keyboard->Release();
        keyboard = NULL;
    };
    if (directInput != NULL) {
        (void) directInput->Release();
        directInput = NULL;
    };
};



/* **********************************************************************************************************************
   InitializeInput
   ---------------
   Creates a new DirectInput device.
   #param  instance  The instance of the program.
   **********************************************************************************************************************/

void VDInput::InitializeInput(HINSTANCE instance) {
    HRESULT returnValue = DirectInput8Create(instance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void **) &directInput, NULL);
    if (returnValue != DI_OK) {
        FreeMemory();
        VDDirectXError("Failed to create DirectInput.", returnValue, __FILE__, __LINE__);
    };
};



/* **********************************************************************************************************************
   InitializeKeyboard
   ------------------
   Initializes the keyboard.
   #param  window  Handle to the main window.
   **********************************************************************************************************************/

void VDInput::InitializeKeyboard(HWND window) {
    if (directInput != NULL) {
        HRESULT returnValue = NULL;

        // create a new keyboard
        returnValue = directInput->CreateDevice(GUID_SysKeyboard, &keyboard, NULL);
        if (returnValue != DI_OK) {
            FreeMemory();
            VDDirectXError("Failed to create the keyboard device.", returnValue, __FILE__, __LINE__);
        };

        // set the data format to the keyboard
        returnValue = keyboard->SetDataFormat(&c_dfDIKeyboard);
        if (returnValue != DI_OK) {
            FreeMemory();
            VDDirectXError("Failed to set the data format for the keyboard device.", returnValue, __FILE__, __LINE__);
        };

        // establish how the keyboard will interact with the system
        returnValue = keyboard->SetCooperativeLevel(window, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
        if (returnValue != DI_OK) {
            FreeMemory();
            VDDirectXError("Failed to establish the cooperative level for the keyboard.", returnValue, __FILE__, __LINE__);
        };

        // get access to the keyboard
        returnValue = keyboard->Acquire();
        if (returnValue != DI_OK) {
            FreeMemory();
            VDDirectXError("Failed to obtain access to the keyboard.", returnValue, __FILE__, __LINE__);
        };
    };
};



/* **********************************************************************************************************************
   InitializeMouse
   ---------------
   Initializes the mouse.
   #param  window  Handle to the main window.
   **********************************************************************************************************************/

void VDInput::InitializeMouse(HWND window) {
    if (directInput != NULL) {
        HRESULT returnValue = NULL;

        // create a new mouse
        returnValue = directInput->CreateDevice(GUID_SysMouse, &mouse, NULL);
        if (returnValue != DI_OK) {
            FreeMemory();
            VDDirectXError("Failed to create the mouse device.", returnValue, __FILE__, __LINE__);
        };

        // set the data format to a basic mouse
        returnValue = mouse->SetDataFormat(&c_dfDIMouse);
        if (returnValue != DI_OK) {
            FreeMemory();
            VDDirectXError("Failed to set the data format for the mouse device.", returnValue, __FILE__, __LINE__);
        };

        // establish how the mouse will interact with the system
        returnValue = mouse->SetCooperativeLevel(window, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
        if (returnValue != DI_OK) {
            FreeMemory();
            VDDirectXError("Failed to obtain access to the mouse.", returnValue, __FILE__, __LINE__);
        };
        currentWindow = window;

        // get access to the mouse
        returnValue = mouse->Acquire();
        if (returnValue != DI_OK) {
            FreeMemory();
            VDDirectXError("Failed to obtain access to the mouse.", returnValue, __FILE__, __LINE__);
        };
    };
};



/* **********************************************************************************************************************
   ChangeMouseWindow
   -----------------
   Sets the mouse interaction to a new window.
   #param  newWindow  Handle to the new window.
   **********************************************************************************************************************/

void VDInput::ChangeMouseWindow(HWND newWindow) {
    HRESULT returnValue = NULL;

    // remove access from the mouse
    (void) mouse->Unacquire();

    // establish how the mouse will interact with the system
    returnValue = mouse->SetCooperativeLevel(newWindow, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
    if (returnValue != DI_OK) {
        FreeMemory();
        VDDirectXError("Failed to obtain access to the mouse.", returnValue, __FILE__, __LINE__);
    };
    currentWindow = newWindow;

    // get access to the mouse
    (void) mouse->Acquire();
};



/* **********************************************************************************************************************
   RetrieveLostInput
   -----------------
   Reaquires lost input devices.
   **********************************************************************************************************************/

void VDInput::RetrieveLostInput() {
    if (keyboard != NULL) {
        if (keyboard->Poll() == DIERR_INPUTLOST || keyboard->Poll() == DIERR_NOTACQUIRED) {
            (void) keyboard->Acquire();
        };
    };
    if (mouse != NULL) {
        if (mouse->Poll() == DIERR_INPUTLOST || mouse->Poll() == DIERR_NOTACQUIRED) {
            (void) mouse->Acquire();
        };
    };
};



/* **********************************************************************************************************************
   UpdateInput
   -----------
   Retrieves the device state of the input devices.
   **********************************************************************************************************************/

void VDInput::UpdateInput() {
    if (keyboard != NULL) {
        (void) keyboard->GetDeviceState(sizeof(BYTE[256]), (LPVOID) keys);
    };
    if (mouse != NULL) {
        (void) mouse->GetDeviceState(sizeof(DIMOUSESTATE), (LPVOID) &mouseState);
    };
};



/* **********************************************************************************************************************
   Accessors and Modifiers
   **********************************************************************************************************************/

BYTE *VDInput::GetKeyboardInput() {
    return keys;
};

DIMOUSESTATE VDInput::GetMouseInput() {
    return mouseState;
};

HWND VDInput::GetCurrentWindow() {
    return currentWindow;
};