/************************************************************************************
GamePad RT-Component
Copyright (c) 2010, Segway Japan, Ltd.
All rights reserved.

Contact us if you use this software for sell.
If you use this software not for sell, you can use this software under BSD lisence.
See the files LICENSE.TXT and LICENSE-BSD.TXT for more details.                     
************************************************************************************/
#define STRICT
#define DIRECTINPUT_VERSION 0x0800

#include <dinput.h>
#include "pad_win.h"

BOOL CALLBACK    EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext );
BOOL CALLBACK    EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext );
BOOL CALLBACK    CreateJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext );

LPDIRECTINPUT8       g_pDI              = NULL;         
LPDIRECTINPUTDEVICE8 g_pJoystick        = NULL;     
LPDIRECTINPUTDEVICE8 g_pKeyboard        = NULL;     



BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext )
{

    return DIENUM_CONTINUE;
}

BOOL CALLBACK CreateJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext )
{
    HRESULT hr;
	hr = g_pDI->CreateDevice( pdidInstance->guidInstance, &g_pJoystick, NULL );

	return DIENUM_STOP;
}



HRESULT InitDirectInput()
{
    HRESULT hr;

    if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, 
		IID_IDirectInput8, (VOID**)&g_pDI, NULL ) ) )
        return hr;

	if( FAILED( hr = g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL, 
                                         EnumJoysticksCallback,
										 NULL, DIEDFL_ATTACHEDONLY ) ) )
        return hr;

	return S_OK;
}

HRESULT StartDirectInput(HWND h)
{	
	HRESULT hr;

	int sel = 0;
    if( FAILED( hr = g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL, 
                                         CreateJoysticksCallback,
										 (LPVOID)&sel, DIEDFL_ATTACHEDONLY ) ) ) {
		 return hr;
	}

	//==================================
	//JOYSTICK
	//==================================
	if (g_pJoystick) {
		if( FAILED( hr = g_pJoystick->SetDataFormat( &c_dfDIJoystick2 ) ) )
			return hr;

		if( FAILED( hr = g_pJoystick->SetCooperativeLevel( h, DISCL_EXCLUSIVE | 
																DISCL_BACKGROUND ) ) )
			return hr;

		if( FAILED( hr = g_pJoystick->EnumObjects( EnumObjectsCallback, 
													(VOID*)NULL, DIDFT_ALL ) ) )
			return hr;
	}
	//==================================
	//KEY BOARD
	//==================================
	if ( FAILED(hr = g_pDI->CreateDevice(GUID_SysKeyboard, &g_pKeyboard, NULL)) )
		return hr;

	if (g_pKeyboard) {
		if ( FAILED(hr = g_pKeyboard->SetDataFormat( &c_dfDIKeyboard)) )
			return hr;

		if (FAILED( hr = g_pKeyboard->SetCooperativeLevel(h, DISCL_FOREGROUND | 
																DISCL_NONEXCLUSIVE)) )
			return hr;
	}
    return S_OK;
}



BOOL CALLBACK EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi,
                                   VOID* pContext )
{
    static int nSliderCount = 0;  // Number of returned slider controls
    static int nPOVCount = 0;     // Number of returned POV controls

    if( pdidoi->dwType & DIDFT_AXIS )
    {
        DIPROPRANGE diprg; 
        diprg.diph.dwSize       = sizeof(DIPROPRANGE); 
        diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER); 
        diprg.diph.dwHow        = DIPH_BYID; 
        diprg.diph.dwObj        = pdidoi->dwType; // Specify the enumerated axis
		diprg.lMin              = -GamePad::AXIS_RANGE; 
        diprg.lMax              = +GamePad::AXIS_RANGE; 
    
        // Set the range for the axis
        if( FAILED( g_pJoystick->SetProperty( DIPROP_RANGE, &diprg.diph ) ) ) 
            return DIENUM_STOP;
    }
    if (pdidoi->guidType == GUID_XAxis) {
    }
    if (pdidoi->guidType == GUID_YAxis) {
    }
    if (pdidoi->guidType == GUID_ZAxis) {
    }
    if (pdidoi->guidType == GUID_RxAxis) {
    }
    if (pdidoi->guidType == GUID_RyAxis) {
    }
    if (pdidoi->guidType == GUID_RzAxis) {
    }
    if (pdidoi->guidType == GUID_Slider) {
        nSliderCount++;
    }
    if (pdidoi->guidType == GUID_POV) {
        nPOVCount++;
    }

    return DIENUM_CONTINUE;
}



HRESULT UpdateInputState()
{
    HRESULT     hr;
    DIJOYSTATE2 js;           // DInput joystick state 

	ZeroMemory(&js, sizeof(js));

	//=========================
	// JOYSTICK
	//=========================
	if (g_pJoystick) {
		hr = g_pJoystick->Poll(); 
		if( FAILED(hr) )  
		{
			hr = g_pJoystick->Acquire();
			while( hr == DIERR_INPUTLOST ) 
				hr = g_pJoystick->Acquire();

			return S_OK; 
		}
		if( FAILED( hr = g_pJoystick->GetDeviceState( sizeof(DIJOYSTATE2), &js ) ) )
			return hr; // The device should have been acquired during the Poll()
	}

    js.rgdwPOV[0];

	//================================--
	// KEYBOARD
	//================================--
	BYTE    diks[256];   // DirectInput keyboard state buffer 

	ZeroMemory(diks, sizeof(diks));

#if 0
	if (g_pKeyboard) {
		ZeroMemory( diks, sizeof(diks) );
		hr = g_pKeyboard->GetDeviceState( sizeof(diks), diks );
		if( FAILED(hr) ) 
		{
			hr = g_pKeyboard->Acquire();
			while( hr == DIERR_INPUTLOST ) 
				hr = g_pKeyboard->Acquire();

			return S_OK; 
		}
	}
#endif
	//=========================
    // Inputへ反映
	//=========================
	GamePad::AxisL[GamePad::AXIS_X] = (abs(js.lX) > GamePad::AXIS_IDLE)? js.lX : 0; 
	GamePad::AxisL[GamePad::AXIS_Y] = (abs(js.lY) > GamePad::AXIS_IDLE)? js.lY : 0; 
	GamePad::AxisR[GamePad::AXIS_X] = (abs(js.lZ) > GamePad::AXIS_IDLE)? js.lZ : 0; 
	GamePad::AxisR[GamePad::AXIS_Y] = (abs(js.lRz) > GamePad::AXIS_IDLE)? js.lRz : 0; 

	static unsigned int PrevPad = 0;
	PrevPad = GamePad::Pad;
	GamePad::Pad = 0;
	GamePad::Pad |= ((js.rgbButtons[0] | diks[DIK_SPACE]) & 0x80)? GamePad::BUTTON_0 : 0;
	GamePad::Pad |= ((js.rgbButtons[1] | diks[DIK_Z])		& 0x80)? GamePad::BUTTON_1 : 0;
	GamePad::Pad |= ((js.rgbButtons[2] | diks[DIK_X])		& 0x80)? GamePad::BUTTON_2 : 0;
	GamePad::Pad |= ((js.rgbButtons[3] | diks[DIK_C])		& 0x80)? GamePad::BUTTON_3 : 0;
	GamePad::Pad |= ((js.rgbButtons[4] | diks[DIK_V])		& 0x80)? GamePad::BUTTON_4 : 0;
	GamePad::Pad |= ((js.rgbButtons[5] | diks[DIK_A])		& 0x80)? GamePad::BUTTON_5 : 0;
	GamePad::Pad |= ((js.rgbButtons[6] | diks[DIK_S])		& 0x80)? GamePad::BUTTON_6 : 0;
	GamePad::Pad |= ((js.rgbButtons[7] | diks[DIK_D])		& 0x80)? GamePad::BUTTON_7 : 0;
	GamePad::Pad |= ((js.rgbButtons[12] | diks[DIK_UP])	& 0x80)? GamePad::BUTTON_UP : 0;
	GamePad::Pad |= ((js.rgbButtons[14] | diks[DIK_DOWN]) & 0x80)? GamePad::BUTTON_DOWN : 0;
	GamePad::Pad |= ((js.rgbButtons[15] | diks[DIK_LEFT]) & 0x80)? GamePad::BUTTON_LEFT : 0;
	GamePad::Pad |= ((js.rgbButtons[13] | diks[DIK_RIGHT]) & 0x80)? GamePad::BUTTON_RIGHT : 0;

	GamePad::Push = GamePad::Pad & ~PrevPad;

    return S_OK;
}

VOID FreeDirectInput()
{
    if( g_pJoystick ) 
        g_pJoystick->Unacquire();
    
    if( g_pKeyboard ) 
        g_pKeyboard->Unacquire();

	if ( g_pJoystick ) {
		g_pJoystick->Release();
		g_pJoystick = NULL;
	}
	if (g_pKeyboard ) {
		g_pKeyboard->Release();
		g_pKeyboard = NULL;
	}
	if (g_pDI) {
		g_pDI->Release();
		g_pDI = NULL;
	}
}

//==========================================
// Input
//==========================================
unsigned int GamePad::Pad = 0;
unsigned int GamePad::Push = 0;
int  GamePad::AxisL[AXIS_NUM] = {0,0};
int  GamePad::AxisR[AXIS_NUM] = {0,0};


bool GamePad::Init()
{
	return (S_OK == InitDirectInput());
}

bool GamePad::Start(HWND h)
{
	Pad = 0;
	Push = 0;
	AxisL[AXIS_X] = 0;
	AxisL[AXIS_Y] = 0;
	AxisR[AXIS_X] = 0;
	AxisR[AXIS_Y] = 0;

	return (S_OK == StartDirectInput(h));
}

void GamePad::End()
{
	FreeDirectInput();
}


void GamePad::Update()
{
	UpdateInputState();
}

float GamePad::GetRangeL()
{
	float x = (float) AxisL[AXIS_X];
	float y = (float) AxisL[AXIS_Y];
	return x * x + y * y;
}

float GamePad::GetRangeR()
{
	float x = (float) AxisR[AXIS_X];
	float y = (float) AxisR[AXIS_Y];
	return x * x + y * y;
}




//------------------------------
//for pad_sdl compatibility
//------------------------------
HWND GetConsoleHwnd(void)
{
    #define MY_BUFSIZE 1024 // コンソール ウィンドウのタイトル用のバッファサイズ
    HWND hwndFound;         // 呼び出し側へ返される値
    char pszNewWindowTitle[MY_BUFSIZE];
                           // 作成されるウィンドウのタイトルが入ります
    char pszOldWindowTitle[MY_BUFSIZE]; // 元のウィンドウタイトルが入ります

    // 現在のウィンドウタイトルを取得します

    GetConsoleTitle(pszOldWindowTitle, MY_BUFSIZE);

    // 独自に、ウィンドウの新規タイトルをフォーマットします

    wsprintf(pszNewWindowTitle,"%d/%d",
             GetTickCount(),
             GetCurrentProcessId());

    // 現在のウィンドウタイトルを変更します

    SetConsoleTitle(pszNewWindowTitle);

    // ウィンドウタイトルのアップデートを確実なものにさせます

    Sleep(40);

    // ウィンドウの新規タイトルを探しにいきます

    hwndFound=FindWindow(NULL, pszNewWindowTitle);

    // 元のウィンドウタイトルへ戻します

    SetConsoleTitle(pszOldWindowTitle);

    return(hwndFound);
}

int pad_sdl_js_open(const char* p)
{
	GamePad::Init();

	//get console window handle
	HWND h = GetConsoleHwnd();
	GamePad::Start(h);
	return 0;
}

void pad_sdl_js_close()
{
	GamePad::End();
}

bool ok = true;
bool pad_sdl_js_isok()
{
	return ok;
}
void pad_sdl_js_setok(bool b)
{
	ok = b;
}




#ifdef _DEBUG
#include <stdio.h>
#endif

void pad_sdl_js_update()
{
	GamePad::Update();

#ifdef _DEBUG
	char buf[128];
	sprintf(buf, "%d %d\n", GamePad::AxisL[GamePad::AXIS_X], GamePad::AxisL[GamePad::AXIS_Y]);
	OutputDebugString(buf);
#endif


}


int pad_sdl_js_axis(int n)
{	
	int axis[4];
	axis[0] = GamePad::AxisL[GamePad::AXIS_X];
	axis[1] = GamePad::AxisL[GamePad::AXIS_Y];
	axis[2] = GamePad::AxisR[GamePad::AXIS_X];
	axis[3] = GamePad::AxisR[GamePad::AXIS_Y];
 
	if (n < 4) {
		//-1.0 - 1.0
		float a = (float)axis[n] / (float)GamePad::AXIS_RANGE;

		int sdl_axis = (int)(a * (float)PAD_SDL_JS_AXIS_MAX);

		return sdl_axis;
	}
	return 0;
}


int pad_sdl_js_button(int n)
{
	return (GamePad::Pad & (GamePad::BUTTON_0<<n))? 1 : 0;
}

int pad_sdl_js_hat(int n)
{
	return 0;
}
int pad_sdl_js_hat_push(int n)
{
	return 0;
}

/*!
  @brief return game pad pushed button status
  @param[in] button number
  @return    1:pushed 0:non-pushed
 */
int pad_sdl_js_button_push(int n)
{
	return (GamePad::Push & (GamePad::BUTTON_0<<n))? 1 : 0;
}


