#include "stdafx.h"
#include "AllegroInput5.h"
#include "LogCommon.h"

bool AllegroInput5::Init( bool keyboard, bool mouse, bool gamepad )
{
	m_hasKeyboard = keyboard;
	m_hasMouse = mouse;
	m_hasGamepad = gamepad;

	m_queue = al_create_event_queue();

	if ( keyboard )
	{
		if ( !al_install_keyboard() )
			GetLog().Log( CommonLog(), LL_CRITICAL, "AllegroInput: Failed to install keyboard driver" );
		else
		{
			GetLog().Log( CommonLog(), LL_INFO, "AllegroInput: Installed keyboard driver" );
			al_register_event_source( m_queue, al_get_keyboard_event_source() );
		}
	}

	if ( mouse )
	{
		if ( !al_install_mouse() )
			GetLog().Log( CommonLog(), LL_CRITICAL, "AllegroInput: Failed to install mouse driver" );
		else
		{
			GetLog().Log( CommonLog(), LL_INFO, "AllegroInput: Installed mouse driver" );		
			al_register_event_source( m_queue, al_get_mouse_event_source() );
		}
	}

	if ( gamepad )
	{
		if ( !al_install_joystick() )
			GetLog().Log( CommonLog(), LL_CRITICAL, "AllegroInput: Failed to install gamepad driver" );
		else
		{
			 // Note: only the first joystick is supported. This should be fixed. Sometime.
			ALLEGRO_JOYSTICK *pJoystick = al_get_joystick( 0 );
			if ( !pJoystick )
				GetLog().Log( CommonLog(), LL_CRITICAL, "AllegroInput: Joystick #0 not found" );
			else
			{
				GetLog().Log( CommonLog(), LL_INFO, "AllegroInput: Installed gamepad driver" );
				al_register_event_source( m_queue, al_get_joystick_event_source() );
			}
		}
	}

	return true;
}

AllegroInput5::~AllegroInput5()
{
	al_destroy_event_queue( m_queue );
}

// Simulates behaviour of Allegro 4.x function readkey() for debugging purposes
void AllegroInput5::WaitAnyKey()
{
	Clear();
	while( true )
	{		
		Update(0);
		BeginRead();
		
		while( true )
		{
			InputEvent ev = GetEvent();
			InputEvent::EType type = ev.m_type;
			if ( type == InputEvent::EV_KEYBOARD && ev.m_keyboardEvent.m_type == KeyboardEvent::KBD_KEY_UP )
			{
				Clear();
				return;
			}
			else if ( type == InputEvent::EV_INVALID )
				break;

			PopEvent();
		}
	}
}

// Pushes new event into queue
void AllegroInput5::AddEvent( InputEvent ev )
{
	m_events.push_back( ev );
	if ( m_events.size() > MAX_EVENTS )
		m_events.erase( m_events.begin() );
}

void AllegroInput5::Update( int dt )
{
	if ( m_doubleClickTimer > 0 )
		m_doubleClickTimer -= dt;
	if ( m_repeatTimer > 0 )
		m_repeatTimer -= dt;

	ALLEGRO_EVENT ev;
	while( al_get_next_event( m_queue, &ev ) )
	{
		if ( m_hasKeyboard )
		{
			if ( ev.any.type == ALLEGRO_EVENT_KEY_DOWN )
				AddEvent( InputEvent::KeyboardEvent( KeyboardEvent::KBD_KEY_DOWN, ev.keyboard.keycode, ev.keyboard.unichar, ev.keyboard.modifiers ) );
			else if ( ev.any.type == ALLEGRO_EVENT_KEY_UP )
				AddEvent( InputEvent::KeyboardEvent( KeyboardEvent::KBD_KEY_UP, ev.keyboard.keycode, ev.keyboard.unichar, ev.keyboard.modifiers ) );
			else if ( ev.any.type == ALLEGRO_EVENT_KEY_CHAR && m_repeatTimer <= 0 )
			{
				  // Please note that some event params (like modifiers) are ONLY accessible in KEY_CHAR event!
				AddEvent( InputEvent::KeyboardEvent( KeyboardEvent::KBD_KEY_REPEAT, ev.keyboard.keycode, ev.keyboard.unichar, ev.keyboard.modifiers ) );
				m_repeatTimer = 100;
			}
		}

		if ( m_hasMouse )
		{
			if ( ev.any.type == ALLEGRO_EVENT_MOUSE_AXES )
			{
				if ( ev.mouse.dx != 0 || ev.mouse.dy != 0 )
					AddEvent( InputEvent::MouseEvent( MouseEvent::MOUSE_MOVE, ev.mouse.x, ev.mouse.y, ev.mouse.dw ) );
				if ( ev.mouse.dw != 0 )
					AddEvent( InputEvent::MouseEvent( MouseEvent::MOUSE_WHEEL, ev.mouse.x, ev.mouse.y, ev.mouse.dw ) );
			}
			else if ( ev.any.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN )
			{
				MouseEvent::EType type = MouseEvent::MOUSE_B1_DOWN;
				if ( ev.mouse.button == 1 )
				{
					if ( m_doubleClickTimer > 0 )
					{
						m_doubleClickTimer = 0;
						type = MouseEvent::MOUSE_B1_DOUBLECLICK;
					}
					else
						type = MouseEvent::MOUSE_B1_DOWN;
				}
				else if ( ev.mouse.button == 2 )
					type = MouseEvent::MOUSE_B2_DOWN;
				else if ( ev.mouse.button == 3 )
					type = MouseEvent::MOUSE_B3_DOWN;
				AddEvent( InputEvent::MouseEvent( type, ev.mouse.x, ev.mouse.y, 0 ) );
			}
			else if ( ev.any.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP )
			{
				MouseEvent::EType type = MouseEvent::MOUSE_B1_UP;
				if ( ev.mouse.button == 1 )
				{
					m_doubleClickTimer = 250;
					type = MouseEvent::MOUSE_B1_UP;
				}
				else if ( ev.mouse.button == 2 )
					type = MouseEvent::MOUSE_B2_UP;
				else if ( ev.mouse.button == 3 )
					type = MouseEvent::MOUSE_B3_UP;
				AddEvent( InputEvent::MouseEvent( type, ev.mouse.x, ev.mouse.y, 0 ) );
			}
		}
	}

	if ( m_hasKeyboard )
	{
		  // Since Allegro events don't have KEY_PRESSED, we do it other way.
		ALLEGRO_KEYBOARD_STATE state;
		al_get_keyboard_state( &state );

		for ( int i = 0; i < ALLEGRO_KEY_MAX; ++i )
		{
			  // We don't know key code for state :(
			if ( al_key_down( &state, i ) && m_keyStates[ i ] )
				AddEvent( InputEvent::KeyboardEvent( KeyboardEvent::KBD_KEY_PRESSED, i, 0, 0 ) );

			m_keyStates[ i ] = al_key_down( &state, i )  != 0;
		}
	}
}

InputEvent AllegroInput5::GetEvent()
{
	if ( m_eventIndex >= m_events.size() )
		return InputEvent();

	InputEvent ev = m_events[ m_eventIndex ];
	return ev;
}

void AllegroInput5::NextEvent()
{
	++m_eventIndex;
}

void AllegroInput5::PopEvent()
{
	if ( m_eventIndex < m_events.size() )
		m_events.erase( m_events.begin() + m_eventIndex );
}
