// ------------------------------------------------------------------------
/*
	\author: Jaap Suter

	Copyright (C) Electronic Arts Canada Inc. 2002.  All rights reserved.	
*/
// ------------------------------------------------------------------------

/*! \file */

#ifndef REVEAL_WIN32_PC_INPUT_STATE_IMPL_H
#define REVEAL_WIN32_PC_INPUT_STATE_IMPL_H

#include "reveal/input_state.h"
#include <assert.h>
#include <windows.h>

namespace Reveal {

// -------------------------------------------------------------------------------
/*!
	\brief Simple Win32 implementation of the InputState class for PC's
*/
// -------------------------------------------------------------------------------
class Win32PcInputStateImpl : public InputState
{
public:

	typedef enum
	{
		WINDOW_MUST_BE_ACTIVE,
		WORK_IN_BACKGROUND_TOO

	} CheckForActiveWindow;

	/*!
		\brief Constructor, does nothing special;
		\param hwnd Window Handle of the application window;
	*/
    Win32PcInputStateImpl( HWND hwnd, CheckForActiveWindow c = WINDOW_MUST_BE_ACTIVE ) : mMouseRelX( 0 ), mMouseRelY( 0 ), mDown( 0 ), mHwnd( hwnd ), mMustBeActive( c ) {}

	/*!
		\brief Assigns a new window handle;
		\param hwnd Handle of the application window;

		This function can be handle if you don't have the window handle available when
		you construct this object. This way you can set it later.
	*/
	void SetHwnd( HWND hwnd ) { mHwnd = hwnd; }

protected:

	//! Implementation of the pure base virtual;
    virtual int DoMouseRelX()  const { return mMouseRelX; }
	
	//! Implementation of the pure base virtual;
	virtual int DoMouseRelY()  const { return mMouseRelY; }

	//! Implementation of the pure base virtual;
	virtual int DoAuxRelX() const
	{
		int x = 0;
		if ( GetAsyncKeyState( VK_LEFT  ) ) x -= 255;
		if ( GetAsyncKeyState( VK_RIGHT ) ) x += 255;
		return x;		
	}

	//! Implementation of the pure base virtual;
	virtual int DoAuxRelY() const
	{
		int y = 0;
		if ( GetAsyncKeyState( VK_UP   ) ) y += 255;
		if ( GetAsyncKeyState( VK_DOWN ) ) y -= 255;
		return y;		
	}

	//! Implementation of the pure base virtual;
	virtual int DoLeftShoulder() const
	{
		if ( GetAsyncKeyState( 'Q' ) ) return 255; else return 0;
	}

	//! Implementation of the pure base virtual;
	virtual int DoRightShoulder() const
	{
		if ( GetAsyncKeyState( 'E' ) ) return 255; else return 0;
	}
	
	//! Implementation of the pure base virtual;
	virtual Reveal::uint32_t DoDown() const { return mDown;  }

private:
	
	//! Implementation of the base virtual;
	void DoUpdate()
	{
		mDown = 0;
		mMouseRelX = 0;
		mMouseRelY = 0;

		if ( mMustBeActive == WINDOW_MUST_BE_ACTIVE )
			if ( mHwnd != GetActiveWindow() )
				return;

		UpdateSingleButton( (0 != GetAsyncKeyState( VK_RETURN  ))
						 || (0 != GetAsyncKeyState( VK_SPACE   )), Reveal::InputState::CONFIRM );

		UpdateSingleButton( (0 != GetAsyncKeyState( VK_LBUTTON )), Reveal::InputState::LEFT_MOUSE  );
		UpdateSingleButton( (0 != GetAsyncKeyState( VK_RBUTTON )), Reveal::InputState::RIGHT_MOUSE );
		
		UpdateSingleButton( (0 != GetAsyncKeyState( 'B' ))
						 || (0 != GetAsyncKeyState( 'C' ))
						 || (0 != GetAsyncKeyState( VK_ESCAPE ))
						 || (0 != GetAsyncKeyState( VK_BACK   )), Reveal::InputState::BACK );

		UpdateSingleButton( (0 != GetAsyncKeyState( VK_RBUTTON ))
						 || (0 != GetAsyncKeyState( VK_SHIFT   )), Reveal::InputState::AUX_1 );
		UpdateSingleButton( (0 != GetAsyncKeyState( VK_CONTROL )), Reveal::InputState::AUX_2 );
		UpdateSingleButton( (0 != GetAsyncKeyState( 'Z' )), Reveal::InputState::AUX_3 );
		UpdateSingleButton( (0 != GetAsyncKeyState( 'X' )), Reveal::InputState::AUX_4 );
		UpdateSingleButton( (0 != GetAsyncKeyState( VK_MULTIPLY )), Reveal::InputState::SELECT);

		
		UpdateSingleButton( (0 != GetAsyncKeyState( VK_LEFT  ))
						 || (0 != GetAsyncKeyState( 'A'		 )), Reveal::InputState::LEFT );
		UpdateSingleButton( (0 != GetAsyncKeyState( VK_RIGHT  ))
						 || (0 != GetAsyncKeyState( 'D'		 )), Reveal::InputState::RIGHT );
		UpdateSingleButton( (0 != GetAsyncKeyState( VK_DOWN  ))
						 || (0 != GetAsyncKeyState( 'W'		 )), Reveal::InputState::UP );
		UpdateSingleButton( (0 != GetAsyncKeyState( VK_UP  ))
						 || (0 != GetAsyncKeyState( 'S'		 )), Reveal::InputState::DOWN );

		UpdateSingleButton( 0 != GetAsyncKeyState( '0' ), Reveal::InputState::ZERO );
		UpdateSingleButton( 0 != GetAsyncKeyState( '1' ), Reveal::InputState::ONE );
		UpdateSingleButton( 0 != GetAsyncKeyState( '2' ), Reveal::InputState::TWO );
		UpdateSingleButton( 0 != GetAsyncKeyState( '3' ), Reveal::InputState::THREE );
		UpdateSingleButton( 0 != GetAsyncKeyState( '4' ), Reveal::InputState::FOUR );
		UpdateSingleButton( 0 != GetAsyncKeyState( '5' ), Reveal::InputState::FIVE );
		UpdateSingleButton( 0 != GetAsyncKeyState( '6' ), Reveal::InputState::SIX );
		UpdateSingleButton( 0 != GetAsyncKeyState( '7' ), Reveal::InputState::SEVEN );
		UpdateSingleButton( 0 != GetAsyncKeyState( '8' ), Reveal::InputState::EIGHT );
		UpdateSingleButton( 0 != GetAsyncKeyState( '9' ), Reveal::InputState::NINE );

		RECT  windowRect = { 0 };
		GetWindowRect( mHwnd, &windowRect );
		
		POINT mouseLock = { 0 };
		mouseLock.x = (windowRect.left + windowRect.right ) / 2;
		mouseLock.y = (windowRect.top  + windowRect.bottom) / 2;
	
		POINT mousePos = { 0 };
		GetCursorPos( &mousePos );

		mMouseRelX = (mousePos.x - mouseLock.x) * 3 / 2;
		mMouseRelY = (mousePos.y - mouseLock.y) * 3 / 2;
		SetCursorPos( mouseLock.x, mouseLock.y );
	}

	void UpdateSingleButton( bool down, Reveal::uint32_t button )
	{
		assert( OnlyOneBitSet( button ) );
		if ( down ) mDown |= button; else mDown &= ~button;
	}

	int mMouseRelX;
	int mMouseRelY;
	int mDown;
	HWND mHwnd;
	CheckForActiveWindow mMustBeActive;
};

} // Namespace Reveal;

#endif
