#include "input_class.h"


input_class::input_class()
{
	m_directInput = 0;
	m_keyboard = 0;
	m_mouse = 0;
	m_previousTildeState = false;
}


input_class::input_class(const input_class& other)
{
}


input_class::~input_class()
{
}

bool input_class::Initialize(HINSTANCE hinstance, HWND hwnd, int screenWidth, int screenHeight)
{
	Logger::Get().Log("Initializing input class", __FILE__, __LINE__, Logger::LogLevel::Initialize);

	HRESULT result;
	int i;

	// Initialize all the keys to being released and not pressed.
	for (i = 0; i < 256; i++)
	{
		m_keys[i] = false;
	}

	// Store the screen size which will be used for positioning the mouse cursor.
	m_screenWidth = screenWidth;
	m_screenHeight = screenHeight;

	// Initialize the location of the mouse on the screen.
	m_mouseX = 0;
	m_mouseY = 0;

	// Initialize the main direct input interface.
	result = DirectInput8Create(hinstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&m_directInput, NULL);
	if (FAILED(result))
	{
		Logger::Get().Log("Failed to create direct input interface", __FILE__, __LINE__, Logger::LogLevel::Error);
		return false;
	}

	// Initialize the direct input interface for the keyboard.
	result = m_directInput->CreateDevice(GUID_SysKeyboard, &m_keyboard, NULL);
	if (FAILED(result))
	{
		Logger::Get().Log("Failed to create direct input interface for the keyboard", __FILE__, __LINE__, Logger::LogLevel::Error);
		return false;
	}

	// Set the data format. In this case since it is a keyboard we can use the predefined data format.
	result = m_keyboard->SetDataFormat(&c_dfDIKeyboard);
	if (FAILED(result))
	{
		Logger::Get().Log("Failed to set data format for the keyboard", __FILE__, __LINE__, Logger::LogLevel::Error);
		return false;
	}

	// Set the cooperative level of the keyboard to share with other programs.
	result = m_keyboard->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
	if (FAILED(result))
	{
		Logger::Get().Log("Failed to set cooperative level of the keyboard", __FILE__, __LINE__, Logger::LogLevel::Error);
		return false;
	}

	// Now acquire the keyboard.
	result = m_keyboard->Acquire();
	if (FAILED(result))
	{
		Logger::Get().Log("Failed to acquire the keyboard", __FILE__, __LINE__, Logger::LogLevel::Error);
		return false;
	}

	// Initialize the direct input interface for the mouse.
	result = m_directInput->CreateDevice(GUID_SysMouse, &m_mouse, NULL);
	if (FAILED(result))
	{
		Logger::Get().Log("Failed to create direct input interface for the mouse", __FILE__, __LINE__, Logger::LogLevel::Error);
		return false;
	}

	// Set the data format for the mouse using the pre-defined mouse data format.
	result = m_mouse->SetDataFormat(&c_dfDIMouse);
	if (FAILED(result))
	{
		Logger::Get().Log("Failed to set data format for the mouse", __FILE__, __LINE__, Logger::LogLevel::Error);
		return false;
	}

	// Set the cooperative level of the mouse to share with other programs.
	result = m_mouse->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
	if (FAILED(result))
	{
		Logger::Get().Log("Failed to set cooperative level of the mouse", __FILE__, __LINE__, Logger::LogLevel::Error);
		return false;
	}

	// Acquire the mouse.
	result = m_mouse->Acquire();
	if (FAILED(result))
	{
		Logger::Get().Log("Failed to acquire the mouse", __FILE__, __LINE__, Logger::LogLevel::Error);
		return false;
	}

	Logger::Get().Log("Input class initialized", __FILE__, __LINE__, Logger::LogLevel::Initialize);

	return true;
}


void input_class::KeyDown(unsigned int input)
{
	// If a key is pressed then save that state in the key array.
	Logger::Get().Log("Key down: " + std::to_string(input), __FILE__, __LINE__, Logger::LogLevel::Input);
	m_keys[input] = true;
	return;
}


void input_class::KeyUp(unsigned int input)
{
	// If a key is released then clear that state in the key array.
	m_keys[input] = false;
	return;
}


bool input_class::IsKeyDown(unsigned int key) const
{
	// Return what state the key is in (pressed/not pressed).
	return m_keys[key];
}

void input_class::Shutdown()
{
	Logger::Get().Log("Shutting down input class", __FILE__, __LINE__, Logger::LogLevel::Shutdown);

	// Release the mouse.
	if (m_mouse)
	{
		m_mouse->Unacquire();
		m_mouse->Release();
		m_mouse = 0;
	}

	// Release the keyboard.
	if (m_keyboard)
	{
		m_keyboard->Unacquire();
		m_keyboard->Release();
		m_keyboard = 0;
	}

	// Release the main interface to direct input.
	if (m_directInput)
	{
		m_directInput->Release();
		m_directInput = 0;
	}

	Logger::Get().Log("Input class shut down", __FILE__, __LINE__, Logger::LogLevel::Shutdown);

	return;
}

bool input_class::Frame()
{
	bool result;


	// Read the current state of the keyboard.
	result = ReadKeyboard();
	if (!result)
	{
		Logger::Get().Log("Failed to read keyboard state", __FILE__, __LINE__, Logger::LogLevel::Error);
		return false;
	}

	// Read the current state of the mouse.
	result = ReadMouse();
	if (!result)
	{
		Logger::Get().Log("Failed to read mouse state", __FILE__, __LINE__, Logger::LogLevel::Error);
		return false;
	}

	// Process the changes in the mouse and keyboard.
	ProcessInput();

	return true;
}

bool input_class::ReadKeyboard()
{
	HRESULT result;


	// Read the keyboard device.
	result = m_keyboard->GetDeviceState(sizeof(m_keyboardState), (LPVOID)&m_keyboardState);
	if (FAILED(result))
	{
		// If the keyboard lost focus or was not acquired then try to get control back.
		if ((result == DIERR_INPUTLOST) || (result == DIERR_NOTACQUIRED))
		{
			m_keyboard->Acquire();
		}
		else
		{
			Logger::Get().Log("Failed to get keyboard device state", __FILE__, __LINE__, Logger::LogLevel::Error);
			return false;
		}
	}

	return true;
}

bool input_class::ReadMouse()
{
	HRESULT result;


	// Read the mouse device.
	result = m_mouse->GetDeviceState(sizeof(DIMOUSESTATE), (LPVOID)&m_mouseState);
	if (FAILED(result))
	{
		// If the mouse lost focus or was not acquired then try to get control back.
		if ((result == DIERR_INPUTLOST) || (result == DIERR_NOTACQUIRED))
		{
			m_mouse->Acquire();
		}
		else
		{
			Logger::Get().Log("Failed to get mouse device state", __FILE__, __LINE__, Logger::LogLevel::Error);
			return false;
		}
	}

	return true;
}

void input_class::ProcessInput()
{
	// Update the location of the mouse cursor based on the change of the mouse location during the frame.
	m_mouseX += m_mouseState.lX;
	m_mouseY += m_mouseState.lY;

	//// Ensure the mouse location doesn't exceed the screen width or height.
	//if (m_mouseX < 0) { m_mouseX = 0; }
	if (m_mouseY < -m_screenHeight) { m_mouseY = -m_screenHeight; }

	//if (m_mouseX > m_screenWidth) { m_mouseX = m_screenWidth; }
	if (m_mouseY > m_screenHeight) { m_mouseY = m_screenHeight; }

	return;
}

bool input_class::IsEscapePressed() const
{
	// Do a bitwise and on the keyboard state to check if the escape key is currently being pressed.
	if (m_keyboardState[DIK_ESCAPE] & 0x80)
	{
		return true;
	}

	return false;
}

bool input_class::IsLeftArrowPressed() const
{
	if (m_keyboardState[DIK_LEFT] & 0x80)
	{
		return true;
	}

	return false;
}


bool input_class::IsRightArrowPressed() const
{
	if (m_keyboardState[DIK_RIGHT] & 0x80)
	{
		return true;
	}

	return false;
}

bool input_class::IsUpArrowPressed() const
{
	if (m_keyboardState[DIK_UP] & 0x80)
	{
		return true;
	}

	return false;
}


bool input_class::IsDownArrowPressed() const
{
	if (m_keyboardState[DIK_DOWN] & 0x80)
	{
		return true;
	}

	return false;
}

///////////////////////////////////////////////////
// Les touches correspondent aux claviers QWERTY //
///////////////////////////////////////////////////

bool input_class::IsAPressed() const
{
	// Touche A sur QWERTY, Q sur AZERTY
	if (m_keyboardState[DIK_A] & 0x80)
	{
		return true;
	}

	return false;
}

bool input_class::IsDPressed() const
{
	if (m_keyboardState[DIK_D] & 0x80)
	{
		return true;
	}

	return false;
}

bool input_class::IsWPressed() const
{
	// Touche W sur QWERTY, Z sur AZERTY
	if (m_keyboardState[DIK_W] & 0x80)
	{
		return true;
	}

	return false;
}

bool input_class::IsSPressed() const
{
	if (m_keyboardState[DIK_S] & 0x80)
	{
		return true;
	}

	return false;
}

bool input_class::IsQPressed() const
{
	// Touche Q sur QWERTY, A sur AZERTY
	if (m_keyboardState[DIK_Q] & 0x80)
	{
		return true;
	}

	return false;
}

bool input_class::IsEPressed() const
{
	if (m_keyboardState[DIK_E] & 0x80)
	{
		return true;
	}

	return false;
}

void input_class::GetMouseLocation(int& mouseX, int& mouseY) const
{
	mouseX = m_mouseX;
	mouseY = m_mouseY;
	return;
}

bool input_class::IsLeftMousePressed() const
{
	// Check the left mouse button state.
	if (m_mouseState.rgbButtons[0] & 0x80)
	{
		return true;
	}

	return false;
}

bool input_class::IsRightMousePressed() const
{
	// Check the left mouse button state.
	if (m_mouseState.rgbButtons[1] & 0x80)
	{
		return true;
	}

	return false;
}

bool input_class::IsScrollUp() const
{
	if (m_mouseState.lZ > 0)
	{
		return true;
	}

	return false;
}

bool input_class::IsScrollDown() const
{
	if (m_mouseState.lZ < 0)
	{
		return true;
	}

	return false;
}

bool input_class::is_key_pressed(const unsigned int key_code)
{
	if (m_keyboardState[key_code] & 0x80)
	{
		return true;
	}

	return false;
}
