/**
    Copyright (c) 2010 yakiimo02
    Distributed under the New BSD License.
    See included license.txt or http://www.yakiimo3d.com/NewBSDLicense.txt
**/

#include "DXUT.h"
#include "DXUTcamera.h"
#include "WaitDlg.h"

#include "SceneMgr.h"
#include "TemporalAA.h"
#include "Gui.h"
#include "CaptureFrameBuffer.h"

CFirstPersonCamera          g_camera;
CameraInfo                  g_cameraInfo;       // aspect ratio, fov, CameraInfo for TemporalAA

//--------------------------------------------------------------------------------------
// Reject any D3D11 devices that aren't acceptable by returning false
//--------------------------------------------------------------------------------------
bool CALLBACK IsD3D11DeviceAcceptable( const CD3D11EnumAdapterInfo *AdapterInfo, UINT Output, const CD3D11EnumDeviceInfo *DeviceInfo,
                                       DXGI_FORMAT BackBufferFormat, bool bWindowed, void* pUserContext )
{
    return true;
}


//--------------------------------------------------------------------------------------
// Called right before creating a D3D9 or D3D11 device, allowing the app to modify the device settings as needed
//--------------------------------------------------------------------------------------
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, void* pUserContext )
{
    return true;
}


//--------------------------------------------------------------------------------------
// Create any D3D11 resources that aren't dependant on the back buffer
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnD3D11CreateDevice( ID3D11Device* pd3dDevice, const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc,
                                      void* pUserContext )
{
    D3DXVECTOR3 vecEye( 100.0f, 5.0f, 5.0f );
    D3DXVECTOR3 vecAt( 0.0f,0.0f,0.0f );
    D3DXVECTOR3 vMin = D3DXVECTOR3( -1000.0f, -1000.0f, -1000.0f );
    D3DXVECTOR3 vMax = D3DXVECTOR3( 1000.0f, 1000.0f, 1000.0f );

    g_camera.SetViewParams( &vecEye, &vecAt );
    g_camera.SetRotateButtons(TRUE, FALSE, FALSE);
    g_camera.SetScalers( 0.01f, 10.0f );
    g_camera.SetDrag( true );
    g_camera.SetEnableYAxisMovement( true );
    g_camera.SetClipToBoundary( TRUE, &vMin, &vMax );
    g_camera.FrameMove( 0 );

    g_sceneMgr.OnCreateDevice( pd3dDevice );

    g_temporalAA.OnCreateDevice( pd3dDevice, pBackBufferSurfaceDesc );

    g_gui.Create( pd3dDevice );

    return S_OK;
}


//--------------------------------------------------------------------------------------
// Create any D3D11 resources that depend on the back buffer
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnD3D11ResizedSwapChain( ID3D11Device* pd3dDevice, IDXGISwapChain* pSwapChain,
                                          const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext )
{
    g_temporalAA.OnResizedSwapChain( pd3dDevice, pSwapChain, pBackBufferSurfaceDesc );

    g_gui.ResizedSwapChain( pd3dDevice, pBackBufferSurfaceDesc );

    float fAspectRatio = pBackBufferSurfaceDesc->Width / static_cast< float >( pBackBufferSurfaceDesc->Height );
    g_camera.SetProjParams( D3DX_PI / 4, fAspectRatio, 0.05f, 2000.0f );
    
    g_cameraInfo.fAspect = fAspectRatio;
    g_cameraInfo.fFov = D3DX_PI / 4;
    g_cameraInfo.fNear = 0.05f;
    g_cameraInfo.fFar = 2000.0f;
    g_cameraInfo.fBackBufferW = static_cast< float >( pBackBufferSurfaceDesc->Width );
    g_cameraInfo.fBackBufferH = static_cast< float >( pBackBufferSurfaceDesc->Height );

    return S_OK;
}


//--------------------------------------------------------------------------------------
// Handle updates to the scene.  This is called regardless of which D3D API is used
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameMove( double fTime, float fElapsedTime, void* pUserContext )
{
    g_camera.FrameMove( fElapsedTime );
}


//--------------------------------------------------------------------------------------
// Render the scene using the D3D11 device
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D11FrameRender( ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext,
                                  double fTime, float fElapsedTime, void* pUserContext )
{
    // Clear render target and the depth stencil 
    float ClearColor[4] = { 0.176f, 0.196f, 0.667f, 0.0f };
    //float ClearColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
    
    ID3D11RenderTargetView* pRTV = DXUTGetD3D11RenderTargetView();
    ID3D11DepthStencilView* pDSV = DXUTGetD3D11DepthStencilView();
    pd3dImmediateContext->ClearRenderTargetView( pRTV, ClearColor );
    pd3dImmediateContext->ClearDepthStencilView( pDSV, D3D11_CLEAR_DEPTH, 1.0, 0 );
    const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc = DXUTGetDXGIBackBufferSurfaceDesc();

    // Render current frame.
    g_temporalAA.BeginRenderScene( pd3dDevice, pd3dImmediateContext, pBackBufferSurfaceDesc );
    g_sceneMgr.OnRender( pd3dDevice, pd3dImmediateContext, &g_camera, g_cameraInfo );
    g_temporalAA.EndTemporalBlendPass( pd3dDevice, pd3dImmediateContext );

    // Blend prev frame and current frame.
    g_temporalAA.BeginTemporalBlendPass( pd3dDevice, pd3dImmediateContext, pBackBufferSurfaceDesc );
    g_temporalAA.DoTemporalBlendPass( pd3dDevice, pd3dImmediateContext, pBackBufferSurfaceDesc );
    g_temporalAA.EndTemporalBlendPass( pd3dDevice, pd3dImmediateContext );
    
    g_temporalAA.SwapFrame();

    // Render GUI.
    g_gui.FrameRender( fElapsedTime );
}


//--------------------------------------------------------------------------------------
// Release D3D11 resources created in OnD3D11ResizedSwapChain 
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D11ReleasingSwapChain( void* pUserContext )
{
    g_temporalAA.OnReleasingSwapChain();
    g_gui.ReleasingSwapChain();
}


//--------------------------------------------------------------------------------------
// Release D3D11 resources created in OnD3D11CreateDevice 
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D11DestroyDevice( void* pUserContext )
{
    g_sceneMgr.OnDestroyDevice();
    g_temporalAA.OnDestroyDevice();
    g_gui.Destroy();
    DXUTGetGlobalResourceCache().OnDestroyDevice();
}


//--------------------------------------------------------------------------------------
// Handle messages to the application
//--------------------------------------------------------------------------------------
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
                          bool* pbNoFurtherProcessing, void* pUserContext )
{
    LRESULT result = g_gui.MsgProc( hWnd, uMsg, wParam, lParam, pbNoFurtherProcessing );
    if( *pbNoFurtherProcessing ) {
        return result;
    }

    // Pass all remaining windows messages to camera so it can respond to user input
    g_camera.HandleMessages( hWnd, uMsg, wParam, lParam );
    return 0;
}


//--------------------------------------------------------------------------------------
// Handle key presses
//--------------------------------------------------------------------------------------
void CALLBACK OnKeyboard( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext )
{
    /// Toggle gui display with H key.
    if( nChar == 'H' && bKeyDown ) {
        g_gui.SetbDisplayGUI( !g_gui.GetbDisplayGUI() );
    }
    else if( nChar == 'P' && bKeyDown ) {
        g_captureFrameBuffer.Capture( L"temporal_aa.bmp" );
    }
}


//--------------------------------------------------------------------------------------
// Handle mouse button presses
//--------------------------------------------------------------------------------------
void CALLBACK OnMouse( bool bLeftButtonDown, bool bRightButtonDown, bool bMiddleButtonDown,
                       bool bSideButton1Down, bool bSideButton2Down, int nMouseWheelDelta,
                       int xPos, int yPos, void* pUserContext )
{
}


//--------------------------------------------------------------------------------------
// Call if device was removed.  Return true to find a new device, false to quit
//--------------------------------------------------------------------------------------
bool CALLBACK OnDeviceRemoved( void* pUserContext )
{
    return true;
}


//--------------------------------------------------------------------------------------
// Initialize everything and go into a render loop
//--------------------------------------------------------------------------------------
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
    // Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

    // DXUT will create and use the best device (either D3D9 or D3D11) 
    // that is available on the system depending on which D3D callbacks are set below
    // Set general DXUT callbacks
    DXUTSetCallbackFrameMove( OnFrameMove );
    DXUTSetCallbackKeyboard( OnKeyboard );
    DXUTSetCallbackMouse( OnMouse );
    DXUTSetCallbackMsgProc( MsgProc );
    DXUTSetCallbackDeviceChanging( ModifyDeviceSettings );
    DXUTSetCallbackDeviceRemoved( OnDeviceRemoved );

    // Set the D3D11 DXUT callbacks. Remove these sets if the app doesn't need to support D3D11
    DXUTSetCallbackD3D11DeviceAcceptable( IsD3D11DeviceAcceptable );
    DXUTSetCallbackD3D11DeviceCreated( OnD3D11CreateDevice );
    DXUTSetCallbackD3D11SwapChainResized( OnD3D11ResizedSwapChain );
    DXUTSetCallbackD3D11FrameRender( OnD3D11FrameRender );
    DXUTSetCallbackD3D11SwapChainReleasing( OnD3D11ReleasingSwapChain );
    DXUTSetCallbackD3D11DeviceDestroyed( OnD3D11DestroyDevice );

    // Perform any application-level initialization here
    g_gui.RegistGuiItems();

    DXUTInit( true, true, L"-forcevsync:1" );   // Parse the command line, show msgboxes on error, no extra command line params
    DXUTSetCursorSettings( true, true );        // Show the cursor and clip it when in full screen
    DXUTCreateWindow( L"Yakiimo3D - Perspective Matrix Jittering TemporalAA" );
    CWaitDlg CompilingShadersDlg;
    if ( DXUT_EnsureD3D11APIs() )
        CompilingShadersDlg.ShowDialog( L"Compiling Shaders and loading models." );
    DXUTCreateDevice( D3D_FEATURE_LEVEL_11_0, true, 640, 480 );
    CompilingShadersDlg.DestroyDialog();
    DXUTMainLoop(); // Enter into the DXUT render loop
    
    // Perform any application-level cleanup here
    return DXUTGetExitCode();
}


