//	Darkstar Engine  (c) UnderWorld Interactive Studio (all coding by Svarog    2007)
//
//      Данный файл является частью Darkstar Engine. Любое использование кода файла
//      без согласия компании, является нарушением закона об авторском праве (в слу-
//      чае если не оговорено обратное).
//
//      Имя файла: keyboard.cpp
//      Краткое описание: стандартная клавиатура
//

#include <Darkstar/Input/inputDriver.hpp>
#include <Darkstar/Input/keyboard.hpp>
#include <Darkstar/Kernel/logh.hpp>

using ker::Log;

using namespace input;

Keyboard::Keyboard()
{
	keyboard = NULL;
	is_immediate = true;
	buffer_data_size = 0;
	cur_valid_num_buf = 0;
	memset(data_immediate, 1, sizeof(data_immediate));
};

Keyboard::~Keyboard()
{
	if(data_buffered)
	{
		data_buffered.reset(NULL);
	};
};

void Keyboard::setImmediateMode()
{
	is_immediate = true;
	buffer_data_size = 0;
};

void Keyboard::setBufferedMode(const unsigned int buf_data_size)
{
	buffer_data_size = buf_data_size;
	data_buffered.reset(new DIDEVICEOBJECTDATA[buffer_data_size]);
	is_immediate = false;
};

bool Keyboard::getIsImmediateMode() const
{
	return is_immediate;
};

unsigned int Keyboard::getBufferDataSize() const
{
	return buffer_data_size;
};

bool Keyboard::init(HWND hwnd)
{
	keyboard = InputDriver::ptr()->createDevice(GUID_SysKeyboard);
	if(!keyboard)
	{
		lerr << "Cannot create keyboard device";
		return false;
	};
	if(FAILED(keyboard->SetDataFormat(&c_dfDIKeyboard)))
	{
		lerr << "Cannot set data format for keyboard";
		return false;
	};
	if(FAILED(keyboard->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND)))
	{
		lerr << "Cannot set cooperative level for keyboard";
		return false;
	};
	DIPROPHEADER diph;
	diph.dwSize = sizeof(DIPROPDWORD);
	diph.dwHeaderSize = sizeof(DIPROPHEADER);
	diph.dwHow = DIPH_DEVICE;
	diph.dwObj = 0;
	if(FAILED(keyboard->SetProperty(DIPROP_BUFFERSIZE, &diph)))
	{
		lwar << "Cannot set property in keyboard device";
		return false;
	};
	if(FAILED(keyboard->Acquire()))
	{
		lerr << "Cannot acquire the keyboard";
		return false;
	};
	return true;
};

bool Keyboard::_tryToReacquire()
{
	HRESULT h = keyboard->Acquire();
	while(h == DIERR_INPUTLOST)
		h = keyboard->Acquire();
	if(FAILED(h))
	{
		lwar << "Cannot reacquire keyboard";
		return false;
	};
	return true;
};

bool Keyboard::updateBuffer()
{
	if(is_immediate)
	{
		lwar << "Try to update buffer in immediate mode";
		return false;
	};
	ZeroMemory(data_buffered.get(), sizeof(DIDEVICEOBJECTDATA)*buffer_data_size);
	cur_valid_num_buf = buffer_data_size;
	if(FAILED(keyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), data_buffered.get(), &cur_valid_num_buf, 0)))
	{
		_tryToReacquire();
		return false;
	};
	return true;
};

bool Keyboard::updateImmediateData()
{
	if(!is_immediate)
	{
		lwar << "Try to update immediate data in buffered mode";
		return false;
	};
	if(FAILED(keyboard->GetDeviceState(256, (void*)data_immediate)))
	{
		_tryToReacquire();
		return false;
	};
	return true;
};

bool Keyboard::getIsKeyDownBuffered(const unsigned char key_code) const
{
	if(is_immediate)
	{
		lwar << "Try to get is key down in buffer mode when set immediate mode";
		return false;
	};
	for(unsigned int i = 0; i < cur_valid_num_buf; ++i)
	{
		if(data_buffered[i].dwOfs == key_code)
			return (data_buffered[i].dwData & 0x80) != 0;
	};
	return false;
};

bool Keyboard::getIsKeyReleaseBuffered(const unsigned char key_code) const
{
	if(is_immediate)
	{
		lwar << "Try to get is key down in buffer mode when set immediate mode";
		return false;
	};
	for(unsigned int i = 0; i < cur_valid_num_buf; ++i)
	{
		if(data_buffered[i].dwOfs == key_code)
			return (data_buffered[i].dwData & 0x80) == 0;
	};
	return false;
};

bool Keyboard::getIsKeyDownImmediate(const unsigned char key_code) const
{
	if(!is_immediate)
	{
		lwar << "Try to get is key down in immediate mode when set buffer mode";
		return false;
	};
	return (data_immediate[key_code] & 0x80) != 0;
};

bool Keyboard::getIsKeyReleaseImmediate(const unsigned char key_code) const
{
	if(!is_immediate)
	{
		lwar << "Try to get is key down in immediate mode when set buffer mode";
		return false;
	};
	return (data_immediate[key_code] & 0x80) == 0;
};

