#include "rt_Skin.h"

#include "rt/hr.h"
#include "rt/rtgdi.h"
#include "rt/app.h"

//----------------------------------------------------------
// Defines, and constants
//----------------------------------------------------------
const LPCTSTR DXAPP_KEY = TEXT("Software\\Pahvant Technologies\\rt_Skin");

//----------------------------------------------------------
// Global access to the app (needed for the global WndProc())
//----------------------------------------------------------
static CMyD3DApplication *g_pApp  = NULL;

//----------------------------------------------------------
// WinMain()
// Entry point to the program. Initializes everything, and goes into a
//       message-processing loop. Idle time is used to render the scene.
//----------------------------------------------------------
INT WINAPI
WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, INT)
{
    CMyD3DApplication d3dApp;

    g_pApp  = &d3dApp;

    if (FAILED(d3dApp.Create(hInst)))
        return 0;

    return d3dApp.Run();
}

//----------------------------------------------------------
// CMyD3DApplication()
// Application constructor. Sets attributes for the app.
//----------------------------------------------------------
CMyD3DApplication::CMyD3DApplication()
    : m_mesh_bounds(),
    m_weight_dist(SKIN_WD_SINUSOIDAL),
    m_trans(SKIN_TR_AXIS_ROTATION),
    m_show_stats(false),
    m_loading_app(true),
    m_font(new CD3DFont(_T("Arial"), 12, D3DFONT_BOLD)),
    m_mesh(new CD3DMesh()),
    m_vertices(0),
    m_indices(0),
    m_vertex_shader(0),
    m_use_vertex_shader(false),
    m_rot_x(0.0f),
    m_rot_y(0.0f)
{
    m_dwCreationWidth           = 500;
    m_dwCreationHeight          = 375;
    m_strWindowTitle            = TEXT("rt_Skin");
    m_bUseDepthBuffer           = true;

    ::ZeroMemory(&m_user_input, sizeof(m_user_input));

    // Read settings from registry
    ReadSettings();
}

//----------------------------------------------------------
// ReadSettings()
// Read the app settings from the registry
//----------------------------------------------------------
void CMyD3DApplication::ReadSettings()
{
    HKEY hkey;
    if (ERROR_SUCCESS == ::RegCreateKeyEx(HKEY_CURRENT_USER, DXAPP_KEY, 
        0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL))
    {
        // Read the stored window width/height.  This is just an example,
        // of how to use DXUtil_Read*() functions.
        ::DXUtil_ReadIntRegKey(hkey, TEXT("Width"), &m_dwCreationWidth, m_dwCreationWidth);
        ::DXUtil_ReadIntRegKey(hkey, TEXT("Height"), &m_dwCreationHeight, m_dwCreationHeight);

        ::RegCloseKey(hkey);
    }
}

//----------------------------------------------------------
// WriteSettings()
// Write the app settings to the registry
//----------------------------------------------------------
void CMyD3DApplication::WriteSettings()
{
    HKEY hkey;
    DWORD dwType = REG_DWORD;
    DWORD dwLength = sizeof(DWORD);

    if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER, DXAPP_KEY, 
        0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL))
    {
        // Write the window width/height.  This is just an example,
        // of how to use DXUtil_Write*() functions.
        DXUtil_WriteIntRegKey(hkey, TEXT("Width"), m_rcWindowClient.right);
        DXUtil_WriteIntRegKey(hkey, TEXT("Height"), m_rcWindowClient.bottom);

        RegCloseKey(hkey);
    }
}

//----------------------------------------------------------
// RenderText()
// Renders stats and help text to the scene.
//----------------------------------------------------------
HRESULT CMyD3DApplication::RenderText()
{
    m_font->DrawText(2,  0, D3DCOLOR_ARGB(255,255,255,0), m_strFrameStats);
    m_font->DrawText(2, 20, D3DCOLOR_ARGB(255,255,255,0), m_strDeviceStats);

    if (m_use_vertex_shader)
        m_font->DrawText(2, 40, D3DCOLOR_ARGB(255,255,255,255), _T("Using vertex shader"));
    else
        m_font->DrawText(2, 40, D3DCOLOR_ARGB(255,255,255,255), _T("Using D3DRS_VERTEXBLEND"));

    return S_OK;
}

void
toggle_menu(HMENU menu, UINT item, bool &value)
{
    value = !value;
    rt::check_menu(menu, item, value);
}

LRESULT
CMyD3DApplication::on_wm_command(HWND window, UINT msg, WPARAM wp, LPARAM lp)
{
    LRESULT res = 0;
    HMENU menu = ::GetMenu(window);

    switch (LOWORD(wp))
    {
    case IDM_USEVERTEXSHADER:
        toggle_menu(menu, IDM_USEVERTEXSHADER, m_use_vertex_shader);
        break;

    case IDM_WD_SINUSOIDAL:
    case IDM_WD_SAWTOOTH:
    case IDM_WD_BRACKETED:
    case IDM_WD_ONE_MINUS_A:
    case IDM_WD_FRACTION:
    case IDM_WD_SIGMOID:
        {
            ::CheckMenuItem(menu,
                IDM_WD_SINUSOIDAL + m_weight_dist,
                MF_UNCHECKED);
            m_weight_dist =
                e_weight_distribution(LOWORD(wp)-IDM_WD_SINUSOIDAL);
            ::CheckMenuItem(menu,
                IDM_WD_SINUSOIDAL + m_weight_dist,
                MF_CHECKED);
        }
        compute_weights();
        m_mesh->InvalidateDeviceObjects();
        m_mesh->RestoreDeviceObjects(m_pd3dDevice);
        break;

    case IDM_TRANS_AXIS_ROTATION:
    case IDM_TRANS_CENTER_SCALING:
    case IDM_TRANS_Y_TRANSLATE:
    case IDM_TRANS_AXIS_ROTATION_LEFT:
    case IDM_TRANS_Y_ROTATION_LEFT:
    case IDM_TRANS_Y_ROTATION_CENTER:
        {
            ::CheckMenuItem(menu,
                IDM_TRANS_AXIS_ROTATION + m_trans,
                MF_UNCHECKED);
            m_trans = e_transformation(LOWORD(wp) - IDM_TRANS_AXIS_ROTATION);
            ::CheckMenuItem(menu,
                IDM_TRANS_AXIS_ROTATION + m_trans,
                MF_CHECKED);
        }
        THR(FrameMove());
        break;

    case IDM_OPTIONS_SHOW_STATISTICS:
        toggle_menu(menu, IDM_OPTIONS_SHOW_STATISTICS, m_show_stats);
        break;
    }
    return res;
}

//----------------------------------------------------------
// MsgProc()
// Overrrides the main WndProc, so the sample can do custom message
//       handling (e.g. processing mouse, keyboard, or menu commands).
//----------------------------------------------------------
LRESULT CMyD3DApplication::MsgProc(HWND hWnd, UINT msg, WPARAM wParam,
                                    LPARAM lParam)
{
    switch (msg)
    {
    case WM_PAINT:
        if (m_loading_app)
        {
            // Draw on the window tell the user that the app is loading
            rt::c_get_dc dc(hWnd);
            RECT rct;
            ::GetClientRect(hWnd, &rct);
            ::DrawText(dc, _T("Loading... Please wait"), -1, &rct,
                DT_CENTER | DT_VCENTER | DT_SINGLELINE);
        }
        break;

    case WM_COMMAND:
        on_wm_command(hWnd, msg, wParam, lParam);
        break;
    }

    return CD3DApplication::MsgProc(hWnd, msg, wParam, lParam);
}

//----------------------------------------------------------
// UpdateInput()
// Update the user input.  Called once per frame 
//----------------------------------------------------------
void CMyD3DApplication::UpdateInput(UserInput *pUserInput)
{
    pUserInput->bRotateUp    = ((GetAsyncKeyState(VK_UP)    & 0x8000) == 0x8000);
    pUserInput->bRotateDown  = ((GetAsyncKeyState(VK_DOWN)  & 0x8000) == 0x8000);
    pUserInput->bRotateLeft  = ((GetAsyncKeyState(VK_LEFT)  & 0x8000) == 0x8000);
    pUserInput->bRotateRight = ((GetAsyncKeyState(VK_RIGHT) & 0x8000) == 0x8000);
}

//----------------------------------------------------------
// OneTimeSceneInit()
// Called during initial app startup, this function performs all the
//       permanent initialization.
//----------------------------------------------------------
HRESULT CMyD3DApplication::OneTimeSceneInit()
{
    // Drawing loading status message until app finishes loading
    ::SendMessage(m_hWnd, WM_PAINT, 0, 0);

    m_loading_app = false;

    return S_OK;
}

//----------------------------------------------------------
// FinalCleanup()
// Called before the app exits, this function gives the app the chance
//       to cleanup after itself.
//----------------------------------------------------------
HRESULT CMyD3DApplication::FinalCleanup()
{
    // delete mesh
    SAFE_DELETE(m_mesh);

    // Cleanup D3D font
    SAFE_DELETE(m_font);

    // Write the settings to the registry
    WriteSettings();

    return S_OK;
}

//----------------------------------------------------------
// InvalidateDeviceObjects()
// Invalidates device objects.  
//----------------------------------------------------------
HRESULT CMyD3DApplication::InvalidateDeviceObjects()
{
    m_font->InvalidateDeviceObjects();
    m_mesh->InvalidateDeviceObjects();

    if (m_vertex_shader)
    {
        THR(m_pd3dDevice->DeleteVertexShader(m_vertex_shader));
    }
    m_vertices = 0;
    m_indices = 0;

    return S_OK;
}

//----------------------------------------------------------
// DeleteDeviceObjects()
// Called when the app is exiting, or the device is being changed,
//       this function deletes any device dependent objects.  
//----------------------------------------------------------
HRESULT CMyD3DApplication::DeleteDeviceObjects()
{
    m_font->DeleteDeviceObjects();
    m_mesh->Destroy();

    return S_OK;
}

