#include "Gos.pch"
#include "User.h"


void (*GosUser::Initialize)( HWND, HINSTANCE ) = User::Initialize;
void (*GosUser::GetStatus)( DWORD *pButtons, long *pX, long *pY ) = User::GetStatus;
void (*GosUser::Poll)() = User::Poll;
void (*GosUser::Shutdown)() = User::Shutdown;

GosUser::KeyStatus (*GosUser::GetKeyStatus)( int ) = User::GetKeyStatus;
int (*GosUser::GetKey)() = User::GetKey;
float (*GosUser::GetAxis)( int , GosUser::JoystickAxis  ) = User::GetAxis;
bool (*GosUser::GetJoystickButtonStatus)( int Joystick, int  ) = User::GetJoystickButtonStatus;
void (*GosUser::SetJoystickPolling)( int , bool , float  ) = User::SetJoystickPolling;
void (*GosUser::GetJoystickInfo)( int , GosUser::JoystickInfo * ) = User::GetJoystickInfo;
DWORD (*GosUser::CountJoysticks)() = User::CountJoysticks;
void (*GosUser::KeyboardFlush)() = User::KeyboardFlush;
void (*GosUser::GetMouseInfo)( float* , float* , int* , int* , int* , DWORD* ) = User::GetMouseInfo;
void (*GosUser::SetMouse)( WORD, WORD, WORD ) = User::SetMouse;

float User::s_MouseX;
float User::s_MouseY;
int User::s_MouseXDelta;
int User::s_MouseYDelta;
WORD User::s_Buttons;

long User::s_GameXVal;
long User::s_GameYVal;
DWORD User::s_GameButtons;


IDirectInput8* User::s_pDirectInput = NULL;
IDirectInputDevice8* User::s_pGameDevice = NULL;



static GosUser::KeyStatus ButtonStatus[5] = { GosUser::KeyFree,GosUser::KeyFree,GosUser::KeyFree,GosUser::KeyFree,GosUser::KeyFree };

static const NumButtons = 3;

void User::GetStatus( DWORD *pButtons, long *pX, long *pY )
{
	LOG_API("User::GetStatus");
	if( pButtons )
		*pButtons = s_GameButtons;
	if( pX )
		*pX = s_GameXVal;
	if( pY )
		*pY = s_GameYVal;
}

void User::Poll()
{
	LOG_API("User::Poll");
    HRESULT     hr;
    DIJOYSTATE  js;           // DInput joystick state 

    if( NULL == s_pGameDevice ) 
        return;

    // Poll the device to read the current state
    hr = s_pGameDevice->Poll(); 
    if( FAILED(hr) )  
    {
        // DInput is telling us that the input stream has been
        // interrupted. We aren't tracking any state between polls, so
        // we don't have any special reset that needs to be done. We
        // just re-acquire and try again.
        hr = s_pGameDevice->Acquire();
        while( hr == DIERR_INPUTLOST ) 
            hr = s_pGameDevice->Acquire();

        // hr may be DIERR_OTHERAPPHASPRIO or other errors.  This
        // may occur when the app is minimized or in the process of 
        // switching, so just try again later 
        return; 
    }

    // Get the input's device state
    if( FAILED( hr = s_pGameDevice->GetDeviceState( sizeof(DIJOYSTATE), &js ) ) )
        return; // The device should have been acquired during the Poll()

	s_GameXVal = js.lX;
	s_GameYVal = js.lY;
	s_GameButtons = 0;
	for( int iButton=0; iButton<32; iButton++ )
		if( js.rgbButtons[iButton] & 0x80 )
			s_GameButtons |= (1<<iButton);

}


BOOL CALLBACK User::EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext )
{
	LOG_API("User::EnumJoysticksCallback");
    HRESULT hr;

    // Obtain an interface to the enumerated joystick.
    hr = s_pDirectInput->CreateDevice( pdidInstance->guidInstance, &s_pGameDevice, NULL );

    // If it failed, then we can't use this joystick. (Maybe the user unplugged
    // it while we were in the middle of enumerating it.)
    if( FAILED(hr) ) 
        return DIENUM_CONTINUE;

    // Stop enumeration. Note: we're just taking the first joystick we get. You
    // could store all the enumerated joysticks and let the user pick.
    return DIENUM_STOP;	// just want one for now
}



void User::Initialize( HWND hWnd, HINSTANCE hInst )
{
	LOG_API("User::Initialize");
	HRESULT rc = DirectInput8Create( hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&s_pDirectInput, NULL );
	if( FAILED(rc) )
	{
		_asm int 3;
	}
	s_pDirectInput->EnumDevices( DI8DEVCLASS_GAMECTRL, (LPDIENUMDEVICESCALLBACK)(EnumJoysticksCallback), NULL, DIEDFL_ATTACHEDONLY );

	if( !s_pGameDevice )
	{
		_asm int 3;
	}

    if( FAILED( rc = s_pGameDevice->SetDataFormat( &c_dfDIJoystick ) ) )
	{
		_asm int 3;
	}

    // Set the cooperative level to let DInput know how this device should
    // interact with the system and with other DInput applications.
    if( FAILED( rc = s_pGameDevice->SetCooperativeLevel( hWnd, DISCL_EXCLUSIVE | 
                                                             DISCL_FOREGROUND ) ) )
	{
		_asm int 3;
	}

    // Determine how many axis the joystick has (so we don't error out setting
    // properties for unavailable axis)
	DIDEVCAPS g_diDevCaps;
    g_diDevCaps.dwSize = sizeof(DIDEVCAPS);
    if ( FAILED( rc = s_pGameDevice->GetCapabilities(&g_diDevCaps) ) )
	{
		_asm int 3;
	}
#if 0
    // Enumerate the axes of the joyctick and set the range of each axis.
    if ( FAILED( rc = s_pGameDevice->EnumObjects( EnumAxesCallback, 
                                                (VOID*)hDlg, DIDFT_AXIS ) ) )
	{
		_asm int 3;
	}
#endif

}

void User::Shutdown()
{
	LOG_API("User::Shutdown");
	if( s_pGameDevice )
		s_pGameDevice->Release();
	if( s_pDirectInput )
		s_pDirectInput->Release();
}



GosUser::KeyStatus User::GetKeyStatus( int KeyIndex )
{
	LOG_API("User::GetKeyStatus");
	KeyIndex;
	if( (KeyIndex-1) < NumButtons )
		return ButtonStatus[ (KeyIndex-1) ];

	return GosUser::KeyPressed;
}

int	User::GetKey()
{
	return 0;
}

float User::GetAxis( int Joystick, GosUser::JoystickAxis Axis )
{
	Joystick;
	Axis;
	return 0.f;
}

bool User::GetJoystickButtonStatus( int Joystick, int Button )
{
	Joystick;
	Button;
	return false;
}

void User::SetJoystickPolling( int Joystick, bool yesNo, float howOften )
{
	Joystick;
	yesNo;
	howOften;
}

void User::GetJoystickInfo( int Joystick, GosUser::JoystickInfo *pInfo )
{
	Joystick;
	pInfo;
}

DWORD User::CountJoysticks()
{
	return 0;
}

void User::KeyboardFlush()
{
	(void)0;
}

void User::GetMouseInfo( float* pXPosition, float* pYPosition, int* pXDelta, int* pYDelta, int* pWheelDelta, DWORD* pButtonsPressed )
{
	pXPosition;pYPosition;pXDelta;pYDelta;pWheelDelta;pButtonsPressed;

	if( pXPosition )
		*pXPosition = s_MouseX;
	if( pYPosition )
		*pYPosition = s_MouseY;
	if( pXDelta )
		*pXDelta = s_MouseXDelta;
	if( pYDelta )
		*pYDelta = s_MouseYDelta;
	if( pWheelDelta )
		*pWheelDelta = 0;
	if( pButtonsPressed )
		*pButtonsPressed = (DWORD)s_Buttons;
}

WORD ButtonId[5] = { MK_LBUTTON, MK_RBUTTON, MK_MBUTTON } ; // , MK_XBUTTON1, MK_XBUTTON2 };

void User::SetMouse( WORD Buttons, WORD X, WORD Y )
{
	float nx = 0.f;
	float ny = 0.f;
	s_Buttons = Buttons;
	nx = (float)X / (float)Gos3D::Width();
	ny = (float)Y / (float)Gos3D::Height();
	s_MouseXDelta = (int)(( nx - s_MouseX ) * (float)Gos3D::Width());
	s_MouseX = nx;
	s_MouseYDelta = (int)(( ny - s_MouseY ) * (float)Gos3D::Height());
	s_MouseY = ny;

	for( int button = 0; button < NumButtons; button++ )
	{
		if( Buttons & ButtonId[button] )
		{
			switch( ButtonStatus[button] )
			{
			case GosUser::KeyFree :
				ButtonStatus[button] = GosUser::KeyPressed;
				break;
			case GosUser::KeyPressed :
				ButtonStatus[button] = GosUser::KeyHeld;
				break;
			default: ;
			}
		}
		else
		{
			switch( ButtonStatus[button] )
			{
			case GosUser::KeyHeld :
				ButtonStatus[button] = GosUser::KeyReleased;
				break;
			case GosUser::KeyReleased :
				ButtonStatus[button] = GosUser::KeyFree;
				break;
			default: ;
			}
		}
	}
}

