#define WINVER 0x0500                                  /* バージョン定義 Windows2000以上 */
#define _WIN32_WINNT WINVER

#include "ruby.h"
#ifndef RUBY_ST_H
#include "st.h"
#endif

#define DXRUBY_EXTERN 1

#include "dxruby.h"
#include "messagethread.h"
#include "font.h"
#include "imm.h"

static HANDLE hMessageThread;
static DWORD MessageThreadID;
static HANDLE hEventMainThreadStart;
int MainThreadError = 0;

static int InitWindow( void );
static int InitDXGraphics( void );
static void DXRelease();

int ime_buf_size;
int ime_str_length;
char *ime_buf;
char *ime_buf_old;
CRITICAL_SECTION ime_cs;
int ime_compositing;

int ime_vk_push_buf_size;
int ime_vk_push_str_length;
char *ime_vk_push_buf;
char *ime_vk_push_buf_old;

int ime_vk_release_buf_size;
int ime_vk_release_str_length;
char *ime_vk_release_buf;
char *ime_vk_release_buf_old;

char *ERR_MESSAGE[ERR_MAX] = 
{
    "正常",
    "メモリが確保できませんでした",
    "内部エラー",
    "スクリーンモードが変更できませんでした",
    "ウィンドウの生成に失敗しました",
    "DirectX Graphicsの初期化でエラーが発生しました",
    "DirectInputの初期化でエラーが発生しました"
};

void DXRuby_raise( int errorcode, char *msg )
{
    char buf[1024];
    buf[0] = '\0';

    strcat( buf, ERR_MESSAGE[errorcode] );
    strcat( buf, " - " );
    strcat( buf, msg );

    /* bufの中身をメッセージとしてeDXRubyError例外を投げる */
#ifdef HAVE_RB_ENC_STR_NEW
    rb_funcall( rb_cObject, rb_intern( "raise" ), 2, eDXRubyError, rb_funcall( rb_str_new2( buf ), rb_intern( "force_encoding" ), 1, rb_str_new2( sys_encode ) ) );
#else
    rb_raise( eDXRubyError, buf );
#endif
}

/*--------------------------------------------------------------------
  （内部関数）スクリーンサイズ変更
 ---------------------------------------------------------------------*/
static int DDChangeSize( void )
{
    D3DVIEWPORT9 vp;
    HRESULT hr;
    int i;
    D3DDISPLAYMODE d3ddm;

    g_D3DPP.BackBufferWidth    = g_WindowInfo.width;
    g_D3DPP.BackBufferHeight   = g_WindowInfo.height;

    if( g_WindowInfo.windowed == Qnil || g_WindowInfo.windowed == Qfalse )
    {
        /* フルスクリーン時（32bitColor固定/リフレッシュレートはあればそれ、無ければ一番高いもの) */
        int count , max, set_refreshrate;

        max = g_pD3D->lpVtbl->GetAdapterModeCount( g_pD3D, D3DADAPTER_DEFAULT, D3DFMT_X8R8G8B8 );
        set_refreshrate = 0;
        g_sync = 0;
        g_D3DPP.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

        for( count = 0 ; count < max ; count++ )
        {
            g_pD3D->lpVtbl->EnumAdapterModes( g_pD3D, D3DADAPTER_DEFAULT, D3DFMT_X8R8G8B8 , count , &d3ddm);
            /* 解像度が違ったら無視 */
            if( g_WindowInfo.width != d3ddm.Width || g_WindowInfo.height != d3ddm.Height )
            {
                continue;
            }

            /* fps指定があり、同じものがあったらそれに決定 */
            if( g_WindowInfo.fps != 0 && d3ddm.RefreshRate == g_WindowInfo.fps )
            {
                set_refreshrate = d3ddm.RefreshRate;
                g_sync = 1; // 垂直同期モード
                g_D3DPP.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
                break;
            }

            /* 無い場合には一番大きい奴にする */
            if( d3ddm.RefreshRate > set_refreshrate )
            {
                set_refreshrate = d3ddm.RefreshRate;
            }
        }

        g_D3DPP.BackBufferFormat           = D3DFMT_X8R8G8B8;
        g_D3DPP.Windowed                   = FALSE;
        g_D3DPP.FullScreen_RefreshRateInHz = set_refreshrate;
    }
    else
    {
        /* ウィンドウモード時 */

        /* 現在のモードを取得 */
        g_pD3D->lpVtbl->GetAdapterDisplayMode( g_pD3D, D3DADAPTER_DEFAULT , &d3ddm );

        g_sync = 0; // 垂直非同期モード
        g_D3DPP.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;
        g_D3DPP.BackBufferFormat           = D3DFMT_UNKNOWN;
        g_D3DPP.Windowed                   = TRUE;
        g_D3DPP.FullScreen_RefreshRateInHz = 0;
    }

    /* D3DXSpriteをロストさせる */
    if( g_pD3DXSprite )
    {
        g_pD3DXSprite->lpVtbl->OnLostDevice( g_pD3DXSprite );
    }

    /* メインレンダーターゲットの解放 */
    {
        struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
        RELEASE( rt->surface );
    }

    /* ユーザーレンダーターゲットの解放 */
//    ppD3DTexture = (LPDIRECT3DTEXTURE9 *)alloca( g_RenderTargetList.count * sizeof(LPDIRECT3DTEXTURE9 *) );
    for( i = 0; i < g_RenderTargetList.count; i++ )
    {
        struct DXRubyRenderTarget *rt = (struct DXRubyRenderTarget *)g_RenderTargetList.pointer[i];

//        ppD3DTexture[i] = to_image( rt );
        RELEASE( rt->surface );
        RELEASE( rt->texture->pD3DTexture );
    }

    /* シェーダのロスト */
    for( i = 0; i < g_ShaderCoreList.count; i++ )
    {
        struct DXRubyShaderCore *core = (struct DXRubyShaderCore *)g_ShaderCoreList.pointer[i];
        core->pD3DXEffect->lpVtbl->OnLostDevice( core->pD3DXEffect );
    }

    /* 設定変更 */
    hr = g_pD3DDevice->lpVtbl->Reset( g_pD3DDevice, &g_D3DPP );
    if( FAILED( hr ) ) return 1;

    /* メインレンダーターゲットの復元 */
    {
        struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
        g_pD3DDevice->lpVtbl->GetRenderTarget( g_pD3DDevice, 0, &rt->surface );
    }

    /* ユーザーレンダーターゲットの復元 */
    for( i = 0; i < g_RenderTargetList.count; i++ )
    {
        struct DXRubyRenderTarget *rt = (struct DXRubyRenderTarget *)g_RenderTargetList.pointer[i];

        /* テクスチャオブジェクトを作成する */
        hr = D3DXCreateTexture( g_pD3DDevice, rt->texture->width, rt->texture->height,
                                1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT,
                                &rt->texture->pD3DTexture);
        if( FAILED( hr ) ) return 2;

        hr = rt->texture->pD3DTexture->lpVtbl->GetSurfaceLevel( rt->texture->pD3DTexture, 0, &rt->surface );
        if( FAILED( hr ) ) return 3;
    }

    /* シェーダの復帰 */
    for( i = 0; i < g_ShaderCoreList.count; i++ )
    {
        struct DXRubyShaderCore *core = (struct DXRubyShaderCore *)g_ShaderCoreList.pointer[i];
        core->pD3DXEffect->lpVtbl->OnResetDevice( core->pD3DXEffect );
    }

    /* D3DXSprite復帰 */
    if( g_pD3DXSprite )
    {
        g_pD3DXSprite->lpVtbl->OnResetDevice( g_pD3DXSprite );
    }

    return 0;
}

/*--------------------------------------------------------------------
   ウィンドウの生成と初期化
 ---------------------------------------------------------------------*/
static int ChangeSize()
{
    HRESULT hr;
    RECT rect;
    VALUE vrender_target;
    struct DXRubyRenderTarget *rt;
    int ret;

    /* ウィンドウのサイズ設定 */
    rect.top     = 0;
    rect.left    = 0;
    rect.right   = g_WindowInfo.width * g_WindowInfo.scale;
    rect.bottom  = g_WindowInfo.height * g_WindowInfo.scale;

    /* ウィンドウのサイズ修正 */
    if( g_WindowInfo.windowed == Qnil || g_WindowInfo.windowed == Qfalse )
    {   /* フルスクリーン */
        SetWindowLong( g_hWnd, GWL_STYLE, WS_POPUP );
        SetWindowPos( g_hWnd, HWND_TOP, 0, 0, g_WindowInfo.width, g_WindowInfo.height, SWP_NOZORDER);
    }
    else
    {   /* ウィンドウモード */
        SetWindowLong( g_hWnd, GWL_STYLE, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX);
        AdjustWindowRect( &rect, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, FALSE );

        /* 幅と高さ計算 */
        rect.right   = rect.right - rect.left;
        rect.bottom  = rect.bottom - rect.top;

        /* ウィンドウ移動/サイズ設定 */
        if( g_WindowInfo.x == CW_USEDEFAULT )
        {   /* 位置がデフォルトの場合 */
            SetWindowPos( g_hWnd, HWND_TOP, 0, 0, rect.right, rect.bottom, SWP_NOZORDER | SWP_NOMOVE);
        }
        else
        {   /* 位置指定の場合 */
            SetWindowPos( g_hWnd, HWND_TOP, g_WindowInfo.x, g_WindowInfo.y, rect.right, rect.bottom, SWP_NOZORDER );
        }
    }

    /* DirectXのスクリーンサイズ変更 */
    ret = DDChangeSize();
    if( ret != 0 )
    {
        return ret;
    }

    /* ウィンドウ表示 */
    ShowWindow( g_hWnd, SW_SHOWNORMAL );
    InvalidateRect( NULL, NULL, TRUE );
    UpdateWindow( g_hWnd );

    {
        struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );

        /* シーンのクリア */
        g_pD3DDevice->lpVtbl->SetRenderTarget( g_pD3DDevice, 0, rt->surface );
        g_pD3DDevice->lpVtbl->Clear( g_pD3DDevice, 0, NULL, D3DCLEAR_TARGET,
                                     D3DCOLOR_XRGB( rt->r, rt->g, rt->b ), 0, 0 );
        g_pD3DDevice->lpVtbl->Present( g_pD3DDevice, NULL, NULL, NULL, NULL );

        rt->width = g_WindowInfo.width;
        rt->height = g_WindowInfo.height;
    }

    g_WindowInfo.created = Qtrue;

    SetCursor( LoadCursor( NULL, IDC_ARROW ) );

    return 0;
}


static int reset( void )
{
    HRESULT hr;
    hr = g_pD3DDevice->lpVtbl->TestCooperativeLevel( g_pD3DDevice );
    if( hr == D3DERR_DEVICENOTRESET ) /* デバイスはロスト状態であるがリセット可能である */
    {
        int i;
        /* ここへ来た時はデバイスがリセット可能状態である */
        /* D3DXSpriteをロストさせる */
        if( g_pD3DXSprite )
        {
            g_pD3DXSprite->lpVtbl->OnLostDevice( g_pD3DXSprite );
        }

        /* レンダーターゲットの解放 */
        {
            struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
            RELEASE( rt->surface );
        }
        for( i = 0; i < g_RenderTargetList.count; i++ )
        {
            struct DXRubyRenderTarget *rt = (struct DXRubyRenderTarget *)g_RenderTargetList.pointer[i];
            RELEASE( rt->surface );
            RELEASE( rt->texture->pD3DTexture );
        }

        /* シェーダのロスト */
        for( i = 0; i < g_ShaderCoreList.count; i++ )
        {
            struct DXRubyShaderCore *core = (struct DXRubyShaderCore *)g_ShaderCoreList.pointer[i];
            core->pD3DXEffect->lpVtbl->OnLostDevice( core->pD3DXEffect );
        }

        hr = g_pD3DDevice->lpVtbl->Reset( g_pD3DDevice, &g_D3DPP ); /* 復元を試みる */
        if( FAILED( hr ) )
        {
            if( hr == D3DERR_DEVICELOST )
            {
                return 0; /* またロストした */
            }
            if( hr == D3DERR_INVALIDCALL )
            {
                return 10;
            }
            return 1;
        }

        /* レンダーターゲットの復元 */
        {
            struct DXRubyRenderTarget *rt = DXRUBY_GET_STRUCT( RenderTarget, g_WindowInfo.render_target );
            g_pD3DDevice->lpVtbl->GetRenderTarget( g_pD3DDevice, 0, &rt->surface );
        }
        for( i = 0; i < g_RenderTargetList.count; i++ )
        {
            struct DXRubyRenderTarget *rt = (struct DXRubyRenderTarget *)g_RenderTargetList.pointer[i];

            /* テクスチャオブジェクトを作成する */
            hr = D3DXCreateTexture( g_pD3DDevice, rt->texture->width, rt->texture->height,
                                    1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT,
                                    &rt->texture->pD3DTexture);
            if( FAILED( hr ) ) return 2;

            hr = rt->texture->pD3DTexture->lpVtbl->GetSurfaceLevel( rt->texture->pD3DTexture, 0, &rt->surface );
            if( FAILED( hr ) ) return 3;
        }

        /* シェーダの復帰 */
        for( i = 0; i < g_ShaderCoreList.count; i++ )
        {
            struct DXRubyShaderCore *core = (struct DXRubyShaderCore *)g_ShaderCoreList.pointer[i];
            core->pD3DXEffect->lpVtbl->OnResetDevice( core->pD3DXEffect );
        }

        if( g_pD3DXSprite ) /* D3DXSprite復帰 */
        {
            g_pD3DXSprite->lpVtbl->OnResetDevice( g_pD3DXSprite );
        }
    }
    else if( hr == D3DERR_DEVICELOST )  /* デバイスはロスト状態である */
    {
        return 0; /* 次の機会に */
    }
    else /* DirectXの内部エラー */
    {
        return 4;
    }

    return 0;
}


/*--------------------------------------------------------------------
   スレッドメイン
 ---------------------------------------------------------------------*/
static DWORD WINAPI MessageThreadProc( LPVOID lpParameter )
{
    MSG msg;
    msg.message = WM_NULL;

    CoInitializeEx( NULL, COINIT_MULTITHREADED );

    /* ウィンドウ作成(この時点では非表示) */
    InitWindow();
    if( MainThreadError != 0 )
    {
        SetEvent( hEventMainThreadStart );
        ExitThread( 0 );
    }

    /* DirectX Graphicsの初期化 */
    InitDXGraphics();
    if( MainThreadError != 0 )
    {
        SetEvent( hEventMainThreadStart );
        ExitThread( 0 );
    }

    /* DirectInput初期化 */
    InitDirectInput();
    if( MainThreadError != 0 )
    {
        SetEvent( hEventMainThreadStart );
        ExitThread( 0 );
    }

    /* 初期化が終わったことの通知 */
    SetEvent( hEventMainThreadStart );

    /* メッセージループ */
    /* WM_QUITが届くまでメッセージ処理をし続ける */
    while( msg.message != WM_QUIT )
    {
        if( GetMessage( &msg, 0, 0, 0 ) != 0)
//        if( PeekMessage( &msg, g_hWnd, 0, 0, PM_REMOVE ) != 0)
        {
            /* メッセージがある時 */
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
        g_byMouseState_L_buf = GetKeyState( VK_LBUTTON );
        g_byMouseState_M_buf = GetKeyState( VK_MBUTTON );
        g_byMouseState_R_buf = GetKeyState( VK_RBUTTON );
    }

    DXRelease();

    CoUninitialize();

    // スレッド終了
    ExitThread( 0 );
}

/*--------------------------------------------------------------------
   （内部関数）ウィンドウプロシージャ
 ---------------------------------------------------------------------*/
LRESULT CALLBACK MessageThreadWndProc( HWND hWnd, UINT msg, UINT wParam, LONG lParam )
{
    RECT rect;
    VALUE temp;

    switch( msg )
    {
    case WM_CREATE:
        break;

    case WM_ACTIVATE:
        /* ウィンドウアクティブ／非アクティブ化 */
        g_WindowInfo.active = (LOWORD(wParam) != 0);
        break;

    case WM_CLOSE:
        g_WindowInfo.requestclose = 1;
        return 0;

    case WM_IME_STARTCOMPOSITION:
        ime_compositing = 1;
        return 0;

    case WM_IME_ENDCOMPOSITION:
        ime_compositing = 0;
        return 0;

    case WM_DESTROY:
        /* ウィンドウ破棄 */
        PostQuitMessage( 0 );
        g_hWnd = NULL;
        return 0;

    case WM_MOUSEWHEEL:
        g_WindowInfo.mousewheelpos += (short)HIWORD(wParam);
        break;

    case WM_KEYDOWN:
        EnterCriticalSection( &ime_cs );
        if( ime_vk_push_str_length+1 >= ime_vk_push_buf_size-1 ) /* バッファオーバーしたらクリアしちゃう */
        {
            ime_vk_push_str_length = 0;
        }
        ime_vk_push_buf[ime_vk_push_str_length++] = (char)wParam;
        LeaveCriticalSection( &ime_cs );
        return 0;
        break;

    case WM_KEYUP:
        EnterCriticalSection( &ime_cs );
        if( ime_vk_release_str_length+1 >= ime_vk_release_buf_size-1 ) /* バッファオーバーしたらクリアしちゃう */
        {
            ime_vk_release_str_length = 0;
        }
        ime_vk_release_buf[ime_vk_release_str_length++] = (char)wParam;
        LeaveCriticalSection( &ime_cs );
        return 0;
        break;

    case WM_CHAR:
        if( wParam < 32 || wParam >= 127)
        {
            return 0;
        }

        EnterCriticalSection( &ime_cs );
        if( ime_str_length+1 >= ime_buf_size-1 ) /* バッファオーバーしたらクリアしちゃう */
        {
            ime_str_length = 0;
        }
        ime_buf[ime_str_length++] = (char)wParam;
        ime_buf[ime_str_length] = 0;
        LeaveCriticalSection( &ime_cs );
        return 0;
        break;

    case WM_IME_CHAR:
        if( ((char)(wParam >> 8) & 0xff) != 0 )
        {
            EnterCriticalSection( &ime_cs );
            if( ime_str_length+2 >= ime_buf_size-1 ) /* バッファオーバーしたらクリアしちゃう */
            {
                ime_str_length = 0;
            }
            ime_buf[ime_str_length++] = (char)(wParam >> 8) & 0xff;
            ime_buf[ime_str_length++] = (char)wParam;
            ime_buf[ime_str_length] = 0;
            LeaveCriticalSection( &ime_cs );
            return 0;
        }
        break;

    case WM_APP + 0:
        /* デバイスのリセット */
        return reset();
        break;

    case WM_APP + 1:
        /* ウィンドウの設定変更 */
        return ChangeSize();
        break;

    case WM_APP + 2:
        /* カーソルの表示 */
        while( ShowCursor( TRUE ) < 0 );
        return 0;
        break;

    case WM_APP + 3:
        /* カーソルの非表示 */
        while( ShowCursor( FALSE ) >= 0 );
        return 0;
        break;

    case WM_APP + 4:
        /* カーソルの変更 */
        SetCursor( LoadCursor( NULL, (LPSTR)lParam ));
        return 0;
        break;

    case WM_APP + 5:
        {/* IMEのカーソル位置設定 */
            COMPOSITIONFORM com = {0};
            HIMC hIMC;

            hIMC = ImmGetContext( g_hWnd );

            com.dwStyle = CFS_POINT;
            com.ptCurrentPos.x = (int)wParam;
            com.ptCurrentPos.y = (int)lParam;
            ImmSetCompositionWindow( hIMC, &com );

            ImmReleaseContext( g_hWnd, hIMC );
            return 0;
            break;
        }

    case WM_APP + 6:
        /* IMEの有効化/無効化 */
        WINNLSEnableIME( g_hWnd, (int)lParam );
        return 0;
        break;

    case WM_APP + 7:
        /* IMEの状態を返す */
        return WINNLSGetEnableStatus( g_hWnd );
        break;

    case WM_APP + 8:
        /* IMEのフォント設定 */
        {
            HIMC hIMC;

            hIMC = ImmGetContext( g_hWnd );
            ImmSetCompositionFont( hIMC, (LOGFONT*)lParam );
            ImmReleaseContext( g_hWnd, hIMC );
            return 0;
            break;
        }
    }


    /* デフォルト処理 */
    return DefWindowProc( hWnd, msg, wParam, lParam );
}

/*--------------------------------------------------------------------
  （内部関数）ウィンドウの生成
 ---------------------------------------------------------------------*/
static int InitWindow( void )
{
    WNDCLASSEX wcex;
    RECT rect;

    /* ウインドウ・クラスの登録 */
    wcex.cbSize        = sizeof( WNDCLASSEX );
    wcex.style         = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc   = (WNDPROC)MessageThreadWndProc;
    wcex.cbClsExtra    = 0;
    wcex.cbWndExtra    = 0;
    wcex.hInstance     = g_hInstance;
    wcex.hIcon         = NULL;
    wcex.hIconSm       = NULL;
    wcex.hCursor       = NULL;//LoadCursor( NULL, IDC_ARROW );
    wcex.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1 );
    wcex.lpszMenuName  = NULL;
    wcex.lpszClassName = "DXRuby";

    if( !RegisterClassEx( &wcex ) )
    {
        MainThreadError = 1;
        return MainThreadError;
    }

    /* メイン・ウインドウ作成(ウインドウ・モード用) */
    rect.top     = 0;
    rect.left    = 0;
    rect.right   = g_WindowInfo.width;
    rect.bottom  = g_WindowInfo.height;

    AdjustWindowRect( &rect, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, FALSE );

    rect.right   = rect.right - rect.left;
    rect.bottom  = rect.bottom - rect.top;
    rect.left    = 0;
    rect.top     = 0;

    g_hWnd = CreateWindow( TEXT("DXRuby"), TEXT("DXRuby Application"),
                           WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
                           CW_USEDEFAULT, CW_USEDEFAULT,
                           rect.right - rect.left, rect.bottom - rect.top,
                           NULL, NULL, g_hInstance, NULL );
    if( g_hWnd == NULL )
    {
        MainThreadError = 2;
        return MainThreadError;
    }

    GetWindowRect( g_hWnd, &rect );
    g_WindowInfo.x        = rect.left;
    g_WindowInfo.y        = rect.top;

    return 0;
}


/*--------------------------------------------------------------------
  （内部関数）DirectX Graphics初期化
 ---------------------------------------------------------------------*/
static int InitDXGraphics( void )
{
    D3DVIEWPORT9 vp;
    HRESULT hr;

    /* Direct3Dオブジェクトの作成 */
    g_pD3D = Direct3DCreate9( D3D_SDK_VERSION );

    if( g_pD3D == NULL )
    {
        MainThreadError = 3;
        return MainThreadError;
    }

    /* D3DDeviceオブジェクトの作成(ウインドウ・モード) */
    ZeroMemory( &g_D3DPP, sizeof( g_D3DPP ) );

    g_D3DPP.BackBufferWidth            = 0;
    g_D3DPP.BackBufferHeight           = 0;
    g_D3DPP.BackBufferFormat           = D3DFMT_UNKNOWN;
    g_D3DPP.BackBufferCount            = 2;
    g_D3DPP.MultiSampleType            = D3DMULTISAMPLE_NONE;
    g_D3DPP.MultiSampleQuality         = 0;
    g_D3DPP.SwapEffect                 = D3DSWAPEFFECT_DISCARD;
    g_D3DPP.hDeviceWindow              = g_hWnd;
    g_D3DPP.Windowed                   = TRUE;
    g_D3DPP.EnableAutoDepthStencil     = TRUE;
    g_D3DPP.AutoDepthStencilFormat     = D3DFMT_D24S8;
    g_D3DPP.Flags                      = 0;
    g_D3DPP.FullScreen_RefreshRateInHz = 0;
    g_D3DPP.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;

    hr = g_pD3D->lpVtbl->CreateDevice( g_pD3D, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd,
                                       D3DCREATE_MIXED_VERTEXPROCESSING, &g_D3DPP, &g_pD3DDevice );

    if( FAILED( hr ) )
    {
        hr = g_pD3D->lpVtbl->CreateDevice( g_pD3D, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd,
                                           D3DCREATE_SOFTWARE_VERTEXPROCESSING, &g_D3DPP, &g_pD3DDevice );

        if( FAILED( hr ) )
        {
            MainThreadError = 4;
            return MainThreadError;
        }
    }

    /* ビューポートの設定 */
    vp.X       = 0;
    vp.Y       = 0;
    vp.Width   = g_D3DPP.BackBufferWidth;
    vp.Height  = g_D3DPP.BackBufferHeight;
    vp.MinZ    = 0.0f;
    vp.MaxZ    = 1.0f;

    hr = g_pD3DDevice->lpVtbl->SetViewport( g_pD3DDevice, &vp );

    if( FAILED( hr ) )
    {
        MainThreadError = 5;
        return MainThreadError;
    }

    g_pD3DDevice->lpVtbl->Clear( g_pD3DDevice, 0, NULL, D3DCLEAR_TARGET,
                                 D3DCOLOR_XRGB(0,0,0), 1.0f, 0 );

    /* D3DXSpriteオブジェクト作成 */
    hr = D3DXCreateSprite( g_pD3DDevice, &g_pD3DXSprite );

    if( FAILED( hr ) )
    {
        MainThreadError = 6;
        return MainThreadError;
    }

    return 0;
}

/*--------------------------------------------------------------------
   DirectXオブジェクトを解放する
 ---------------------------------------------------------------------*/
static void DXRelease()
{
    int i;
    HRESULT hr;

    /* D3DXSpriteオブジェクトの使用終了 */
    if( g_pD3DXSprite )
    {
        g_pD3DXSprite->lpVtbl->OnLostDevice( g_pD3DXSprite );
    }

    /* DirectInput解放 */
    Input_release();

    /* D3DXSpriteオブジェクト破棄 */
    RELEASE( g_pD3DXSprite );

    /* Direct3D Deviceオブジェクトの破棄 */
    RELEASE( g_pD3DDevice );

	/* Direct3Dオブジェクトの破棄 */
    RELEASE( g_pD3D );
}


void InitMessageThread( void )
{
    hEventMainThreadStart = CreateEvent( NULL, FALSE, FALSE, NULL );

    ime_buf_size = 1024;
    ime_str_length = 0;
    ime_buf = malloc( 1024 );
    ime_buf[0] = 0;
    ime_buf_old = malloc( 1024 );
    ime_buf_old[0] = 0;
    InitializeCriticalSection( &ime_cs );
    ime_compositing = 0;

    ime_vk_push_buf_size = 1024;
    ime_vk_push_str_length = 0;
    ime_vk_push_buf = malloc( 1024 );
    ime_vk_push_buf[0] = 0;
    ime_vk_push_buf_old = malloc( 1024 );
    ime_vk_push_buf_old[0] = 0;

    ime_vk_release_buf_size = 1024;
    ime_vk_release_str_length = 0;
    ime_vk_release_buf = malloc( 1024 );
    ime_vk_release_buf[0] = 0;
    ime_vk_release_buf_old = malloc( 1024 );
    ime_vk_release_buf_old[0] = 0;

    hMessageThread = CreateThread( NULL, 0, &MessageThreadProc, 0, 0, &MessageThreadID );
    {
        unsigned long result = WaitForSingleObject( hEventMainThreadStart, INFINITE );
        switch( MainThreadError )
        {
        case 0:
            break;
        case 1:
            DXRuby_raise( ERR_WINDOWCREATE, "RegisterClassEx" );
            break;
        case 2:
            DXRuby_raise( ERR_WINDOWCREATE, "CreateWindow" );
            break;
        case 3:
            DXRuby_raise( ERR_D3DERROR, "Direct3DCreate9" );
            break;
        case 4:
            DXRuby_raise( ERR_D3DERROR, "CreateDevice" );
            break;
        case 5:
            DXRuby_raise( ERR_D3DERROR, "SetViewport" );
            break;
        case 6:
            DXRuby_raise( ERR_D3DERROR, "D3DXCreateSprite" );
            break;
        case 7:
            DXRuby_raise( ERR_D3DERROR, "GetSwapChain" );
            break;
        case 10:
            DXRuby_raise( ERR_DINPUTERROR, "DirectInput8Create" );
            break;
        case 11:
            DXRuby_raise( ERR_DINPUTERROR, "CreateDevice" );
            break;
        case 12:
            DXRuby_raise( ERR_DINPUTERROR, "SetDataFormat" );
            break;
        case 13:
            DXRuby_raise( ERR_DINPUTERROR, "SetCooperativeLevel" );
            break;
        case 14:
            DXRuby_raise( ERR_DINPUTERROR, "EnumDevices" );
            break;
        case 15:
            DXRuby_raise( ERR_DINPUTERROR, "SetDataFormat" );
            break;
        case 16:
            DXRuby_raise( ERR_DINPUTERROR, "SetCooperativeLevel" );
            break;
        case 17:
            DXRuby_raise( ERR_DINPUTERROR, "GetProperty" );
            break;
        case 18:
            DXRuby_raise( ERR_DINPUTERROR, "EnumObjects" );
            break;
        }
    }
}

void ExitMessageThread( void )
{
    DWORD exitcode;

    if( g_hWnd != NULL )
    {
        SendMessage( g_hWnd, WM_DESTROY, 0, 0 );
    }

//    do
//    {
//        if( !GetExitCodeThread( hMessageThread, &exitcode ) )
//        {
//            rb_raise( eDXRubyError, "Therad exit error - ExitMessageThread" );
//        }
//        Sleep(1);
//    }
//    while( exitcode == STILL_ACTIVE );

    free( ime_buf );
    free( ime_buf_old );
    free( ime_vk_push_buf );
    free( ime_vk_push_buf_old );
    free( ime_vk_release_buf );
    free( ime_vk_release_buf_old );
    DeleteCriticalSection( &ime_cs );

    /* ウインドウ・クラスの登録解除 */
    UnregisterClass( "DXRuby", g_hInstance );

    CloseHandle( hMessageThread );
}


void WindowCreateMessage( void )
{
    int ret;

    ret = SendMessage( g_hWnd, WM_APP + 1, 0, 0 );
    switch( ret )
    {
    case 0:
        break;
    case 1:
        DXRuby_raise( ERR_NOEXISTSCREENMODE, "Reset" );
        break;
    case 2:
        DXRuby_raise( ERR_INTERNAL, "D3DXCreateTexture" );
        break;
    case 3:
        DXRuby_raise( ERR_INTERNAL, "GetSurfaceLevel" );
        break;
    case 4:
        DXRuby_raise( ERR_INTERNAL, "TestCooperativeLevel" );
        break;
    }
}

void ResetMessage( void )
{
    int ret;
    /* デバイスロストの場合の処理 */
    ret = SendMessage( g_hWnd, WM_APP + 0, 0, 0 );
    switch( ret )
    {
    case 0:
        break;
    case 1:
        DXRuby_raise( ERR_INTERNAL, "Reset" );
        break;
    case 2:
        DXRuby_raise( ERR_INTERNAL, "D3DXCreateTexture" );
        break;
    case 3:
        DXRuby_raise( ERR_INTERNAL, "GetSurfaceLevel" );
        break;
    case 4:
        DXRuby_raise( ERR_INTERNAL, "TestCooperativeLevel" );
        break;
    case 10:
        DXRuby_raise( ERR_INTERNAL, "Reset_InvaridCall" );
        break;
    }
    Sleep(100); /* とりあえずwait */
}

void ShowCursorMessage( void )
{
    SendMessage( g_hWnd, WM_APP + 2, 0, 0 );
}

void HideCursorMessage( void )
{
    SendMessage( g_hWnd, WM_APP + 3, 0, 0 );
}

void SetCursorMessage( int cursorname )
{
    SendMessage( g_hWnd, WM_APP + 4, 0, cursorname );
}

void SetImeCursor( int x, int y )
{
    SendMessage( g_hWnd, WM_APP + 5, x, y );
}

void SetImeEnable( int flag )
{
    SendMessage( g_hWnd, WM_APP + 6, 0, flag );
}

int GetImeEnable( void )
{
    return SendMessage( g_hWnd, WM_APP + 7, 0, 0 );
}

void SetImeFont( VALUE vfont )
{
    LOGFONT logfont;
    GetObject(DXRUBY_GET_STRUCT( Font, vfont )->hFont, sizeof(LOGFONT), &logfont);
    SendMessage( g_hWnd, WM_APP + 8, 0, (LPARAM)&logfont );
}

