//-----------------------------------------------------------------------------
// Simple Navi
//-----------------------------------------------------------------------------
#define STRICT
#define DIRECTINPUT_VERSION 0x0800

#include <Windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <d3dx9.h>
#include <dxerr9.h>
#include <dinput.h>
#include <math.h>
#include <tchar.h>
#include <stdio.h>

#include "VAIBO.h"
#include "IpDlg.h"


//-----------------------------------------------------------------------------
// Defines, constants, and global variables
//-----------------------------------------------------------------------------
#define RELEASE(p)      { if(p) { (p)->Release();p=NULL;} }

#define IMAGETP_RECVMAX         (64 * 1024)     // 64KB

SIZE        g_sizeJPEGLOW       = { 204, 159 };
SIZE        g_sizeJPEGHIGH      = { 412, 318 };

SIZE        g_sizeWindowMode    = { 640, 480 };
SIZE        g_sizeFullMode      = { 640, 480 };

RECT        g_rectWindow;

enum WINDOWMODE {
    FULLSCREEN,
        WINDOW
};

WINDOWMODE  g_nScreenMode;

HINSTANCE   hInstApp    = NULL;
char        szAppName[] = "Simple Navi";
HWND        g_hWindow   = NULL;

bool        g_bActive   = false;


#define     DEFAULT_FONTSIZE  20
char        szDefaultFile[] = "StartPage.jpg";

// Direct 3D
LPDIRECT3D9             g_pD3D          = NULL;
LPDIRECT3DDEVICE9       g_pD3DDevice    = NULL;
LPDIRECT3DSURFACE9      g_pBackBuffer   = NULL;
LPDIRECT3DSURFACE9      g_pSurface      = NULL;
LPD3DXSPRITE            g_pD3DXSprite   = NULL;
LPDIRECT3DTEXTURE9      g_pD3DTexture   = NULL;
LPD3DXFONT              g_pD3DXFont     = NULL;
D3DPRESENT_PARAMETERS   g_D3DPP;

// Direct Input
const int LENGTH_DEV_NAME = 40;     // The maximum length of device names
const int BUTTON_DOWN     = 0x80;   // Mask for determining button state
const int NUM_OF_ACTIONS  = 11;     // Number of game action constants


// Convenience wrapper for device pointers
struct DeviceState
{
    LPDIRECTINPUTDEVICE8 pDevice;   // Pointer to the device
    TCHAR szName[LENGTH_DEV_NAME];  // Friendly name of the device
    bool  bAxisRelative;            // Relative x-axis data flag
    DWORD dwInput[NUM_OF_ACTIONS];  // Arrays of the current input values and
    DWORD dwPaint[NUM_OF_ACTIONS];  //   values when last painted
    bool  bMapped[NUM_OF_ACTIONS];  // Flags whether action was successfully
};

const int MAX_DEV  = 16;
#define DIDEVICE_BUFFERSIZE 100
LPDIRECTINPUT8          g_pDInput           = NULL;
LPDIRECTINPUTDEVICE8    pDevice;
int                     g_iDIDeviceNum      = 0;
DeviceState             g_aDevices[MAX_DEV] = {0};
int                     g_render            = 0;

// {C1576C8D-2A5B-480c-B8DA-731DF4B0DDF0}
GUID g_AppGuid = { 0xc1576c8d, 0x2a5b, 0x480c, { 0xb8, 0xda, 0x73, 0x1d, 0xf4, 0xb0, 0xdd, 0xf0 } };

// Input semantics used by this app
enum INPUT_SEMANTICS
{
    INPUT_UPDOWN_AXIS = 1,
    INPUT_LEFTRIGHT_AXIS,
    INPUT_TURNLEFT,
    INPUT_TURNRIGHT,
    INPUT_WALKFORWARD,
    INPUT_WALKBACKWARD,
    INPUT_L_KICK,
    INPUT_R_KICK,
    INPUT_STOP,
    INPUT_SHOOT,
    INPUT_ENABLESHIELD,
    INPUT_DISPLAYGAMEMENU,
    INPUT_QUITGAME,
    INPUT_POV,
};

// Action mapping
DIACTION g_GameAction[] =
{
    // joystick mapping
    {INPUT_LEFTRIGHT_AXIS, DIAXIS_MECHA_STEER,          0, "Right/Left Turn",       },
    {INPUT_UPDOWN_AXIS,    DIAXIS_MECHA_TORSO,          0, "Forward/Backward",      },
    {INPUT_SHOOT,          DIBUTTON_MECHA_FIRE,         0, "Shoot",                 },
    {INPUT_L_KICK,         DIBUTTON_MECHA_WEAPONS,      0, "Left Kick",             },
    {INPUT_R_KICK,         DIBUTTON_MECHA_TARGET,       0, "Right Kick",            },
    {INPUT_POV,            DIHATSWITCH_MECHA_GLANCE,    0, "Look around",           },
    {INPUT_DISPLAYGAMEMENU,DIBUTTON_MECHA_REVERSE,      0, "Display",               },
    {INPUT_QUITGAME,       DIBUTTON_MECHA_MENU,         0, "Quit Game",             },


    // Keyboard mapping
    {INPUT_TURNLEFT,       DIKEYBOARD_LEFT,             0, "Turn left",             },
    {INPUT_TURNRIGHT,      DIKEYBOARD_RIGHT,            0, "Turn right",            },
    {INPUT_WALKFORWARD,    DIKEYBOARD_UP,               0, "Walk Forward",          },
    {INPUT_WALKBACKWARD,   DIKEYBOARD_DOWN,             0, "Walk Backward",         },
    {INPUT_STOP,           DIKEYBOARD_A,                0, "Stop",                  },
    {INPUT_SHOOT,          DIKEYBOARD_S,                0, "Shoot",                 },
    {INPUT_L_KICK,         DIKEYBOARD_Z,                0, "Left Kick",             },
    {INPUT_R_KICK,         DIKEYBOARD_X,                0, "Right Kick",            },
    {INPUT_QUITGAME,       DIKEYBOARD_ESCAPE, DIA_APPFIXED, "Quit game",            },
};

#define NUMBER_OF_GAMEACTIONS    (sizeof(g_GameAction)/sizeof(DIACTION))
DIACTIONFORMAT g_DIActionFormat;

enum GAME_ACTIONS {
    UNKNOWN_ACTION,
    WALK_FORWARD,
    WALK_BACKWARD,
    WALK_LEFT,
    WALK_RIGHT,
    TURN_LEFT,
    TURN_RIGHT,
    WALK_STOP,
    KICK_LEFT,
    KICK_RIGHT,
    SHOOT,
    QUIT
};

// VAIBO
CVAIBO      *g_vaibo        = NULL;

enum ConnectionState {
    NONE_STATE = 0,
    NOT_CONNECT,
    REQ_CONNECT,
    READY_CONNECT,
    REQ_CONTROL,
    READY_CONTROL,
    REQ_IMAGE,
    READY_IMAGE
};

char *AIBOSTATE_NAME[] =
{
    "NONE_STATE",
    "Not Connected",
    "REQ_CONNECT",
    "READY_CONNECT",
    "REQ_CONTROL",
    "READY_CONTROL",
    "REQ_IMAGE",
    "READY_IMAGE",
};

char g_message[256];
bool g_bMessage = true;

enum WALK_STATE {
    NOW_WALK,
    NOT_WALK
};

int         g_aiboID        = 0;
int         g_aiboStatus    = NONE_STATE;
int         g_nPlayID       = -1;
int         g_walk          = NOT_WALK;
int         g_actionState   = UNKNOWN_ACTION;

char        hostname[64];

void MyOutputDebugString( LPCSTR pszFormat, ...)
{
    va_list argp;
    char pszBuf[ 256];
    va_start(argp, pszFormat);
    vsprintf( pszBuf, pszFormat, argp);
    va_end(argp);
    OutputDebugString( pszBuf);
}

#if defined(_DEBUG) || defined(DEBUG)
// for Debug
#define TRACE(x)                OutputDebugString(x)
#define TRACE0(x)               OutputDebugString(x)
#define TRACE1(x, a)            MyOutputDebugString(x, a)
#define TRACE2(x, a, b)         MyOutputDebugString(x, a, b)
#define TRACE3(x, a, b, c)      MyOutputDebugString(x, a, b, c)
#define TRACE4(x, a, b, c, d)   MyOutputDebugString(x, a, b, c, d)
#else
// for Release
#define TRACE(x)
#define TRACE0(x)
#define TRACE1(x, a)
#define TRACE2(x, a, b)
#define TRACE3(x, a, b, c)
#define TRACE4(x, a, b, c, d)
#endif

//---------------------------------------------------------------------------------------
// InitVAIBO
//---------------------------------------------------------------------------------------
HRESULT InitVAIBO()
{
    if ( g_vaibo == NULL )
    {
        g_vaibo = new CVAIBO( g_hWindow );
        if ( g_vaibo == NULL )
        {
            return E_FAIL;
        }
    }
    return S_OK;
}


//---------------------------------------------------------------------------------------
// ReleaseVAIBO
//---------------------------------------------------------------------------------------
void ReleaseVAIBO()
{
    if ( g_vaibo != NULL )
    {
        delete g_vaibo;
    }
}


//---------------------------------------------------------------------------------------
// GetRFWIP
//---------------------------------------------------------------------------------------
BOOL GetRFWIP()
{
    char   path[MAX_PATH];
    char   buf[256];
    FILE  *fp;

    sprintf( path, "SimpleNavi.cfg" );
    fp = fopen( path, "r" );
    if( fp )
    {
        while( fgets( buf, 256, fp ) )
        {
            sscanf( buf, "%s", hostname );
        }
        fclose( fp );
    } else
    {
        int nRet = DialogBox(hInstApp, MAKEINTRESOURCE(IDD_IP_DLG), g_hWindow, (DLGPROC)IpDlgProc);
        if ( nRet != IDOK ) return FALSE;
        strcpy( hostname, szIPAdd );
    }
    return TRUE;
}


//---------------------------------------------------------------------------------------
// ConnectAIBO
//---------------------------------------------------------------------------------------
void ConnectAIBO()
{
    if ( GetRFWIP() == FALSE ) return;

    if ( g_vaibo != NULL )
    {
        if ( g_vaibo->IsConnect() == FALSE )
        {
            g_aiboID = g_vaibo->Connect( hostname );
            g_aiboStatus = REQ_CONNECT;
        }
    }
}


//---------------------------------------------------------------------------------------
// DisconnectAIBO
//---------------------------------------------------------------------------------------
void DisconnectAIBO()
{
    if ( g_vaibo->IsConnect() == TRUE )
    {
        g_vaibo->ImageClose();
        g_vaibo->SendCmd( APPCMD_IMAGE_STOP );
        g_vaibo->InternalControl();
        g_vaibo->Disconnect();
    } else {
        PostQuitMessage(0);
    }
}


//---------------------------------------------------------------------------------------
// SendImageCmd
//---------------------------------------------------------------------------------------
void SendImageCmd( int com )
{
    if ( g_vaibo == NULL ) return;

    if ( g_aiboStatus == READY_IMAGE )
    {
        g_vaibo->SendCmd( APPCMD_IMAGE_SETPARAM, true, CMDSID_PROFILE, com );
    }
}


//---------------------------------------------------------------------------------------
// CheckSemData
//---------------------------------------------------------------------------------------
void CheckSemData( UINT msg, WPARAM wParam, LPARAM lParam )
{
    if ((unsigned) g_aiboID != HIWORD(wParam))
    {
        // This semantics data didn't come from my AIBO
        return;
    }

    if ( msg == WM_VAIBO_CONNECT || msg == WM_VAIBO_CLIENT_UPDATE )
    {
        g_aiboStatus = READY_CONNECT;
        g_vaibo->ExternalControl();
        g_aiboStatus = REQ_CONTROL;
    }

    if ( msg == WM_VAIBO_SEMANTICS )
    {
        if ( g_aiboStatus == REQ_CONTROL )
        {
            g_aiboStatus = READY_CONTROL;
            int nRet;
            nRet = g_vaibo->ImageInit( );
            nRet = g_vaibo->ImageOpen( );
            BOOL bRet = g_vaibo->RequestNetService( IMAGE_ID );
            g_aiboStatus = READY_IMAGE;
        }

        int subinfo[4];
        UINT semState = LOWORD( wParam );
        if ( semState == SEM_START )
        {
            if ( lParam == START_IMAGETP )
            {
                g_vaibo->SendCmd( APPCMD_IMAGE_PLAY );
                g_aiboStatus = REQ_IMAGE;
            }
        }
        if ( semState == SEM_RESULT )
        {
            g_vaibo->GetSemInfo( subinfo );
            switch (lParam) {
            case RESULT_COMPLETE :
                TRACE1("No.%d motion completed\n", subinfo[0]);
                break;
            case RESULT_INCOMPLETE :
                TRACE1("No.%d motion incomplete\n", subinfo[0]);
                break;
            default:
                TRACE2("No.%d lParam:%d ???\n", subinfo[0], lParam);
            }

            if ( g_nPlayID == subinfo[0] )      g_walk = NOT_WALK;
        }
    }

    if ( msg == WM_IMAGETP_UPDATE )
    {
        unsigned char buff[IMAGETP_RECVMAX];
        g_vaibo->ImageGetData(buff, IMAGETP_RECVMAX );
        D3DXLoadSurfaceFromFileInMemory(g_pSurface, NULL, NULL, buff, IMAGETP_RECVMAX, NULL,D3DX_DEFAULT,0,NULL);
        g_aiboStatus = READY_IMAGE;
        g_render = 0;
        g_bMessage = false;
        g_bActive = true;
    }
    if ( msg == WM_VAIBO_DISCONNECT ) {
        PostQuitMessage(0);
    }
}


//---------------------------------------------------------------------------------------
// DoAction
//---------------------------------------------------------------------------------------
void DoAction(int action)
{
    if ( g_vaibo == NULL ) return;
    if ( g_aiboStatus != READY_IMAGE ) return;

    int result = -1;
    switch ( action )
    {
    case WALK_FORWARD:
        if ( g_actionState != WALK_FORWARD )
        {
            result = g_vaibo->DoWalking( DW_FORWARD,    0, 0, FALSE );
            TRACE1( "WALK_FORWARD:result:%x\n", result );
            g_actionState = action;
            g_nPlayID = result;
            g_walk = NOW_WALK;
        }
        break;
    case WALK_BACKWARD:
        if ( g_actionState != WALK_BACKWARD )
        {
            result = g_vaibo->DoWalking( DW_BACK,       0, 0, FALSE );
            TRACE1( "WALK_BACKWARD:result:%x\n", result );
            g_actionState = action;
            g_nPlayID = result;
            g_walk = NOW_WALK;
        }
        break;
    case TURN_LEFT:
        if ( g_actionState != TURN_LEFT )
        {
            result = g_vaibo->DoWalking( DW_TURN_LEFT,  0, 0, FALSE );
            TRACE1( "TURN_LEFT:result:%x\n", result );
            g_actionState = action;
            g_nPlayID = result;
            g_walk = NOW_WALK;
        }
        break;
    case TURN_RIGHT:
        if ( g_actionState != TURN_RIGHT )
        {
            result = g_vaibo->DoWalking( DW_TURN_RIGHT, 0, 0, FALSE) ;
            TRACE1( "TURN_RIGHT:result:%x\n", result );
            g_actionState = action;
            g_nPlayID = result;
            g_walk = NOW_WALK;
        }
        break;
    case KICK_LEFT:
        if ( g_actionState != KICK_LEFT )
        {
            result = g_vaibo->DoKick( DK_LEFT,  FALSE );
            TRACE1( "KICK_LEFT:result:%x\n", result );
            g_actionState = action;
        }
        break;
    case KICK_RIGHT:
        if ( g_actionState != KICK_RIGHT )
        {
            result = g_vaibo->DoKick( DK_RIGHT,  FALSE );
            TRACE1( "KICK_RIGHT:result:%x\n", result );
            g_actionState = action;
        }
        break;
    case WALK_STOP:
        if ( g_actionState != WALK_STOP  && g_walk == NOW_WALK )
        {
            result = g_vaibo->DoWalking( DW_STOP,  FALSE );
            TRACE1( "WALK_STOP:result:%x\n", result );
            g_actionState = action;
        }
        break;
    case SHOOT:
        if ( g_actionState != SHOOT )
        {
            result = g_vaibo->PlayMotion( 0x21050500, 0 , FALSE, 0 );
            TRACE1( "SHOOT:result:%x\n", result );
            g_actionState = action;
        }
        break;
    }
}



//---------------------------------------------------------------------------------------
// DXDrawText
//---------------------------------------------------------------------------------------
void DXDrawText(char *str, int x, int y, D3DCOLOR color)
{
    RECT rect;
    rect.left   = x;
    rect.right  = g_sizeWindowMode.cy;
    rect.top    = y;
    rect.bottom = g_sizeWindowMode.cx;

    g_pD3DXFont->DrawTextA(NULL, str, -1, &rect, DT_LEFT | DT_EXPANDTABS, color);
}

//---------------------------------------------------------------------------------------
// InitFont
//---------------------------------------------------------------------------------------
HRESULT InitFont( int size )
{
    RELEASE(g_pD3DXFont);

    HRESULT hr;
    hr = D3DXCreateFont( g_pD3DDevice,  // D3D device
        size,                           // Height
        0,                              // Width
        FW_NORMAL,                      // Weight
        0,                              // MipLevels, 0 = autogen mipmaps
        FALSE,                          // Italic
        DEFAULT_CHARSET,                // CharSet
        OUT_DEFAULT_PRECIS,             // OutputPrecision
        DEFAULT_QUALITY,                // Quality
        DEFAULT_PITCH | FF_DONTCARE,    // PitchAndFamily
        "MS Sans Serif",                // pFaceName
        &g_pD3DXFont);                  // ppFont


    if ( FAILED(hr) )
        return E_FAIL;

    return S_OK;
}

//-----------------------------------------------------------------------------
// CleanupAll
//-----------------------------------------------------------------------------
void CleanupAll()
{
    ReleaseVAIBO();

    RELEASE( g_pD3DXFont );
    RELEASE( g_pD3DXSprite );
    RELEASE( g_pD3DTexture );
    RELEASE( g_pSurface );
    RELEASE( g_pBackBuffer );
    RELEASE( g_pD3DDevice );
    RELEASE( g_pD3D );
}

//-----------------------------------------------------------------------------
// InitObject
//-----------------------------------------------------------------------------
HRESULT InitObject()
{
    HRESULT hr;
    InitFont(DEFAULT_FONTSIZE);

    hr = g_pD3DDevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &g_pBackBuffer );
    if ( FAILED(hr) )
    {
        return E_FAIL;
    }

    D3DSURFACE_DESC desc;
    g_pBackBuffer->GetDesc( &desc );

    // Create offsreen surface
    hr = g_pD3DDevice->CreateOffscreenPlainSurface( g_sizeJPEGLOW.cx, g_sizeJPEGLOW.cy,
        desc.Format, D3DPOOL_DEFAULT,
        &g_pSurface, NULL );
    if ( FAILED(hr) )
    {
        return E_FAIL;
    }

    // Load Start Surface File
    hr = D3DXLoadSurfaceFromFile( g_pSurface, NULL, NULL, szDefaultFile,
        NULL, D3DX_DEFAULT, 0, NULL );
    if ( FAILED(hr) )
    {
        g_render = 1;
        return E_FAIL;
    }

    return S_OK;
}


//-----------------------------------------------------------------------------
// ReleaseObject()
//-----------------------------------------------------------------------------
void ReleaseObject()
{
    RELEASE( g_pD3DXFont   );
    RELEASE( g_pD3DXSprite );
    RELEASE( g_pD3DTexture );
    RELEASE( g_pSurface    );
    RELEASE( g_pBackBuffer );
}


//-----------------------------------------------------------------------------
// ChangeDisplayMode
//-----------------------------------------------------------------------------
void ChangeDisplayMode(void)
{
    if (g_pD3DXSprite) {
        g_pD3DXSprite->OnLostDevice();
    }

    ReleaseObject();

    if (g_nScreenMode == FULLSCREEN)
    {
        // Full Screen mode -> Window mode
        g_pD3DDevice->Reset(&g_D3DPP);
        g_nScreenMode = WINDOW;
        SetWindowLong(g_hWindow, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE);
        ShowCursor(TRUE);
    } else
    {
        // Window mode -> Full Screen mode
        D3DPRESENT_PARAMETERS   d3dppApp;
        ZeroMemory(&d3dppApp,sizeof(d3dppApp));
        d3dppApp.BackBufferWidth            = g_sizeFullMode.cx;
        d3dppApp.BackBufferHeight           = g_sizeFullMode.cy;
        d3dppApp.BackBufferFormat           = D3DFMT_X8R8G8B8;
        d3dppApp.Windowed                   = FALSE;  // Full Screen
        d3dppApp.BackBufferCount            = 1;
        d3dppApp.SwapEffect                 = D3DSWAPEFFECT_DISCARD;
        d3dppApp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
        d3dppApp.EnableAutoDepthStencil     = TRUE;
        d3dppApp.AutoDepthStencilFormat     = D3DFMT_D16;
        g_pD3DDevice->Reset(&d3dppApp);
        g_nScreenMode = FULLSCREEN;

        GetWindowRect(g_hWindow, &g_rectWindow);
        SetWindowLong(g_hWindow, GWL_STYLE, WS_POPUP);
        ShowCursor(FALSE);
    }

    SetWindowPos(g_hWindow, HWND_NOTOPMOST,
        g_rectWindow.left, g_rectWindow.top,
        g_rectWindow.right - g_rectWindow.left,
        g_rectWindow.bottom - g_rectWindow.top,
        SWP_DRAWFRAME|SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOSIZE);

    InitObject();
}


//-----------------------------------------------------------------------------
// InitD3D
//-----------------------------------------------------------------------------
HRESULT InitD3D( HWND hWnd )
{
    if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
    {
        MessageBox(g_hWindow,
            (LPCSTR)"Fail Direct3DCreate9( D3D_SDK_VERSION )",
            (LPCSTR)"SimpleNavi", MB_OK | MB_ICONEXCLAMATION);
        return E_FAIL;
    }

    D3DDISPLAYMODE dmode;
    if(FAILED(g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&dmode)))
    {
        MessageBox(g_hWindow,
            (LPCSTR)"Fail GetAdapterDisplayMode",
            (LPCSTR)"SimpleNavi", MB_OK | MB_ICONEXCLAMATION);
        return E_FAIL;
    }

    ZeroMemory( &g_D3DPP, sizeof(g_D3DPP) );
    g_D3DPP.BackBufferWidth   = g_sizeWindowMode.cx;
    g_D3DPP.BackBufferHeight  = g_sizeWindowMode.cy;
    g_D3DPP.BackBufferCount   = 1;
    g_D3DPP.Windowed          = TRUE;
    g_D3DPP.SwapEffect        = D3DSWAPEFFECT_DISCARD;
    g_D3DPP.BackBufferFormat  = dmode.Format;
    g_D3DPP.EnableAutoDepthStencil = TRUE;
    g_D3DPP.AutoDepthStencilFormat = D3DFMT_D16;
    g_D3DPP.Flags                  = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;

    // Create D3DDevice
    if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd,
        D3DCREATE_HARDWARE_VERTEXPROCESSING,&g_D3DPP,&g_pD3DDevice))) {
        if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
            D3DCREATE_SOFTWARE_VERTEXPROCESSING, &g_D3DPP, &g_pD3DDevice ) ) ) {
            if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_REF,hWnd,
                D3DCREATE_SOFTWARE_VERTEXPROCESSING,&g_D3DPP,&g_pD3DDevice))) {

                MessageBox(g_hWindow,
                    (LPCSTR)"The video card of this PC is incompatible.",
                    (LPCSTR)"SimpleNavi", MB_OK | MB_ICONEXCLAMATION);

                return E_FAIL;
            }
        }
    }

    InitObject();

    g_nScreenMode = WINDOW;

    return S_OK;
}

//-----------------------------------------------------------------------------
// UpdateInput
//-----------------------------------------------------------------------------
int UpdateInput()
{
    HRESULT hr;

    int     command = -1;
    int     FRNeutral = -1;
    int     LRNeutral = -1;

    for( int iDevice=0; iDevice < g_iDIDeviceNum; iDevice++ )
    {
        LPDIRECTINPUTDEVICE8 pdidDevice = g_aDevices[iDevice].pDevice;
        DIDEVICEOBJECTDATA rgdod[100];
        DWORD   dwItems = 100;

        hr = pdidDevice->Acquire();
        hr = pdidDevice->Poll();
        hr = pdidDevice->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), rgdod, &dwItems, 0 );
        if ( FAILED(hr) )
        {
            continue;
        }

        for( DWORD j=0; j<dwItems; j++ )
        {
            UINT_PTR dwAction = rgdod[j].uAppData;
            DWORD dwData = 0;

            switch( dwAction )
            {
            case INPUT_LEFTRIGHT_AXIS:
                dwData = rgdod[j].dwData;
                TRACE1( "LR_AXIS:%d\n", (int)dwData);
                if ( (int)dwData > 50 )
                {
                    command = TURN_RIGHT;
                    LRNeutral = 1;
                } else if ( (int)dwData < -50 )
                {
                    command = TURN_LEFT;
                    LRNeutral = 1;
                } else
                {
                    LRNeutral = 0;
                }
                break;

            case INPUT_UPDOWN_AXIS:
                dwData =  rgdod[j].dwData;
                if ( (int)dwData > 50 )
                {
                    TRACE1( "UD_AXIS:%d\n", (int)dwData);
                    command = WALK_BACKWARD;
                    FRNeutral = 1;
                } else if ( (int)dwData < -50 )
                {
                    TRACE1( "UD_AXIS:%d\n", (int)dwData);
                    command = WALK_FORWARD;
                    FRNeutral = 1;
                } else
                {
                    FRNeutral = 0;
                }
                break;

            case INPUT_L_KICK:
                command = KICK_LEFT;
                break;
            case INPUT_R_KICK:
                command = KICK_RIGHT;
                break;
            case INPUT_STOP:
                command = WALK_STOP;
                break;
            case INPUT_SHOOT:
                dwData = rgdod[j].dwData & BUTTON_DOWN ?    1 : 0;
                if ( dwData == 1 )
                {
                    command = SHOOT;
                }
                break;
            case INPUT_TURNLEFT:
                command = TURN_LEFT;
                break;
            case INPUT_TURNRIGHT:
                command = TURN_RIGHT;
                break;
            case INPUT_WALKFORWARD:
                command = WALK_FORWARD;
                break;
            case INPUT_WALKBACKWARD:
                command = WALK_BACKWARD;
                break;
            case INPUT_DISPLAYGAMEMENU:
                command = WALK_STOP;
                break;
            case INPUT_QUITGAME:
                command = WALK_STOP;
                break;
            default:
                {
                    dwData = rgdod[j].dwData & BUTTON_DOWN ?    1 : 0;
                    break;
                }
            }
            g_aDevices[iDevice].dwInput[dwAction] = dwData;
        }
    }

    if ( FRNeutral == 0 && LRNeutral == 0 ) {
        if ( g_walk == NOW_WALK       ) DoAction( WALK_STOP     );
    } else {
        if ( command == KICK_LEFT     ) DoAction( KICK_LEFT     );
        if ( command == KICK_RIGHT    ) DoAction( KICK_RIGHT    );
        if ( command == WALK_STOP     ) DoAction( WALK_STOP     );
        if ( command == SHOOT         ) DoAction( SHOOT         );
        if ( command == WALK_FORWARD  ) DoAction( WALK_FORWARD  );
        if ( command == WALK_BACKWARD ) DoAction( WALK_BACKWARD );
        if ( command == TURN_LEFT     ) DoAction( TURN_LEFT     );
        if ( command == TURN_RIGHT    ) DoAction( TURN_RIGHT    );
    }

    return 0;
}


//-----------------------------------------------------------------------------
// Name: Render()
//-----------------------------------------------------------------------------
HRESULT Render()
{
    if (!g_bActive)
        return S_OK;

    D3DXVECTOR2 RotationCenter(100.0,100.0);

    // Backbuffer clear
    g_pD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,
        D3DCOLOR_XRGB(128,128,128), 1.0f, 0 );

    // Begin Scene
    if (SUCCEEDED(g_pD3DDevice->BeginScene()))
    {
        if ( g_render == 0 )
        {
            // Draw backbuffer
            RECT SrcRect,DestRect;
            SetRect( &SrcRect,  0, 0, g_sizeJPEGLOW.cx,    g_sizeJPEGLOW.cy    );
            SetRect( &DestRect, 0, 0, g_sizeWindowMode.cx, g_sizeWindowMode.cy );
            g_pD3DDevice->StretchRect( g_pSurface, &SrcRect, g_pBackBuffer, &DestRect, D3DTEXF_NONE );
        }

        if ( g_bMessage == true )
        {
            wsprintf(g_message, "[W] Screen Change [C] Connect\n\nState[%s]",
                AIBOSTATE_NAME[g_aiboStatus] );
            DXDrawText(g_message, 120, 10, D3DCOLOR_RGBA(0,0,255,255));
        }
        g_pD3DDevice->EndScene();
    }

    g_pD3DDevice->Present( NULL, NULL, NULL, NULL );

    return S_OK;
}


//-----------------------------------------------------------------------------
// CALLBACK EnumDevicesBySemanticsCallback
//-----------------------------------------------------------------------------
BOOL CALLBACK EnumDevicesBySemanticsCallback( LPCDIDEVICEINSTANCE lpddi,
                                             LPDIRECTINPUTDEVICE8  lpdid,
                                             DWORD  dwFlags, DWORD  dwRemaining, LPVOID pvRef)
{
    HRESULT hr;

    if( g_iDIDeviceNum < MAX_DEV)
    {

        // Get Default Map
        hr = lpdid->BuildActionMap(&g_DIActionFormat, NULL, DIDBAM_HWDEFAULTS);
        if (FAILED(hr))
        {
            DXTRACE_ERR("Failed BuildActionMap", hr);
            return DIENUM_CONTINUE;
        }

        // Inspect the results
        for( UINT i=0; i < g_DIActionFormat.dwNumActions; i++ )
        {
            DIACTION *dia = &(g_DIActionFormat.rgoAction[i]);

            if( dia->dwHow != DIAH_ERROR && dia->dwHow != DIAH_UNMAPPED )
                g_aDevices[g_iDIDeviceNum].bMapped[dia->uAppData] = TRUE;
        }

        hr = lpdid->SetActionMap(&g_DIActionFormat, NULL, DIDSAM_FORCESAVE);
        if (FAILED(hr))
        {
            ZeroMemory( g_aDevices[g_iDIDeviceNum].bMapped,
                sizeof(g_aDevices[g_iDIDeviceNum].bMapped) );
            DXTRACE_ERR("Failed SetActionMap", hr);
            return DIENUM_CONTINUE;
        }

        g_aDevices[g_iDIDeviceNum].pDevice = lpdid;
        lpdid->AddRef();

        // Store the device's friendly name for display on the chart.
        _tcsncat( g_aDevices[g_iDIDeviceNum].szName, lpddi->tszInstanceName, LENGTH_DEV_NAME-5 );

        if( _tcslen( lpddi->tszInstanceName ) > LENGTH_DEV_NAME-5 )
            _tcscat( g_aDevices[g_iDIDeviceNum].szName, TEXT("...") );

        // Store axis absolute/relative flag
        DIPROPDWORD dipdw;
        dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
        dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
        dipdw.diph.dwObj        = 0;
        dipdw.diph.dwHow        = DIPH_DEVICE;

        hr = lpdid->GetProperty( DIPROP_AXISMODE, &dipdw.diph );
        if (SUCCEEDED(hr))
            g_aDevices[g_iDIDeviceNum].bAxisRelative = ( DIPROPAXISMODE_REL == dipdw.dwData );

        g_iDIDeviceNum++;    // Increment the global device index
    }

    return DIENUM_CONTINUE;
}


//-----------------------------------------------------------------------------
// Init DirectX Input
//-----------------------------------------------------------------------------
HRESULT InitDInput(void)
{
    HRESULT hr;

    hr = DirectInput8Create(hInstApp, DIRECTINPUT_VERSION,
        IID_IDirectInput8, (void**)&g_pDInput, NULL);
    if (FAILED(hr))
    {
        DXTRACE_ERR("InitDInput DirectInput8Create", hr);
        return E_FAIL;
    }

    ZeroMemory( &g_DIActionFormat, sizeof(DIACTIONFORMAT) );
    g_DIActionFormat.dwSize        = sizeof(DIACTIONFORMAT);
    g_DIActionFormat.dwActionSize  = sizeof(DIACTION);
    g_DIActionFormat.dwDataSize    = NUMBER_OF_GAMEACTIONS * sizeof(DWORD);
    g_DIActionFormat.dwNumActions  = NUMBER_OF_GAMEACTIONS;
    g_DIActionFormat.guidActionMap = g_AppGuid;
    g_DIActionFormat.dwGenre       = DIVIRTUAL_DRIVING_MECHA; //DIVIRTUAL_SPACESIM;
    g_DIActionFormat.rgoAction     = g_GameAction;
    g_DIActionFormat.lAxisMin      = -100;
    g_DIActionFormat.lAxisMax      = 100;
    g_DIActionFormat.dwBufferSize  = 100;
    lstrcpy(g_DIActionFormat.tszActionMap, szAppName);

    // Action Mapping
    hr = g_pDInput->EnumDevicesBySemantics(NULL, &g_DIActionFormat,
        EnumDevicesBySemanticsCallback, NULL, DIEDBSFL_ATTACHEDONLY);
    if (FAILED(hr))
    {
        DXTRACE_ERR("InitDInput EnumDevicesbysemantics ", hr);
        return E_FAIL;
    }

    return S_OK;
}


//-----------------------------------------------------------------------------
// Release DirectX Input
//-----------------------------------------------------------------------------
bool ReleaseDInput(void)
{
    // Release device pointers
    for( int i=0; i < g_iDIDeviceNum; i++ )
    {
        if( g_aDevices[i].pDevice )
        {
            g_aDevices[i].pDevice->Unacquire();
            g_aDevices[i].pDevice->Release();
            g_aDevices[i].pDevice = NULL;
        }
    }

    RELEASE(g_pDInput);

    return true;
}


//-----------------------------------------------------------------------------
// WM_KeyDown
//-----------------------------------------------------------------------------
void WM_KeyDown(HWND hWnd, UINT wParam)
{
    switch(wParam) {
    case VK_ESCAPE:
    case VK_F12:
        DisconnectAIBO();
        break;
    case 'M':
        g_bMessage = true;
        break;
    case 'W':
        ChangeDisplayMode();
        break;
    case 'C':
        ConnectAIBO();
        break;
    case '1':
        SendImageCmd( IMAGE_PROFILE_LOW );
        break;
    case '2':
        SendImageCmd( IMAGE_PROFILE_MID );
        break;
    case '3':
        SendImageCmd( IMAGE_PROFILE_HIGH );
        break;
    }
}


//-----------------------------------------------------------------------------
// WM_Timer
//-----------------------------------------------------------------------------
void WM_Timer(HWND hWnd, WPARAM wParam)
{
    InvalidateRect(hWnd, NULL, TRUE);
}


//-----------------------------------------------------------------------------
// Name: 1MsgProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    INITCOMMONCONTROLSEX ic;

    // Check Semantics Data
    CheckSemData( msg, wParam, lParam );

    switch( msg )
    {
    case WM_ACTIVATE:
        g_bActive = (LOWORD(wParam) != 0);
        break;
    case WM_KEYDOWN:
        WM_KeyDown(hWnd, wParam);
        break;
    case WM_CREATE:
        ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
        ic.dwICC = ICC_INTERNET_CLASSES;
        InitCommonControlsEx(&ic);
        SetTimer(hWnd, 1, 1000/2, NULL);
        break;
    case WM_CLOSE:
        DisconnectAIBO();
        break;
    case WM_DESTROY:
        KillTimer(hWnd, 1);     
        PostQuitMessage(0);
        break;
    case WM_TIMER:
        UpdateInput();
        break;
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}


//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
    HRESULT hr;
    hInstApp = hInst;

    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
        GetModuleHandle(NULL), NULL, NULL, NULL, NULL, szAppName, NULL };
    if ( !RegisterClassEx( &wc ))
        return false;

    g_hWindow = CreateWindow( szAppName, szAppName,
        WS_OVERLAPPEDWINDOW, 100, 100, g_sizeWindowMode.cx, g_sizeWindowMode.cy,
        GetDesktopWindow(), NULL, wc.hInstance, NULL );
    if (g_hWindow == NULL)
        return false;

    hr = InitVAIBO();
    if ( FAILED(hr) )
    {
        DXTRACE_ERR("WinMain INITVAIBO", hr );
    }

    hr = InitDInput();
    if ( FAILED( hr ) )
    {
        DXTRACE_ERR("WinMain InitDInput", hr );
    }

    hr = InitD3D(g_hWindow);
    if( SUCCEEDED( hr ) ) {
        ShowWindow( g_hWindow, SW_SHOWDEFAULT );
        UpdateWindow( g_hWindow );

        MSG msg;
        ZeroMemory( &msg, sizeof(msg) );
        while( msg.message!=WM_QUIT )
        {
            if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
            {
                TranslateMessage( &msg );
                DispatchMessage( &msg );
            } else {
                if( FAILED( Render() ) )
                    SendMessage( g_hWindow, WM_DESTROY, 0, 0 );
            }
        }
    }

    ReleaseDInput();
    CleanupAll();

    UnregisterClass( szAppName, wc.hInstance );
    return 0;
}
