/**
    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 "SceneMgr.h"
#include "DXHelper.h"
#include "TemporalAA.h"

SceneMgr                            g_sceneMgr;

/**
*/
struct ConstantBuf
{
    D3DXVECTOR4   g_f3EyePt;                        // Camera position
    D3DXMATRIX    g_f4x4World;                      // World matrix for object
    D3DXMATRIX    g_f4x4View;                       // View matrix
    D3DXMATRIX    g_f4x4WorldViewProjection;        // World * View * Projection matrix
    // char        padding[ 4 ];                    // 
};

/**
*/
SceneMgr::SceneMgr() : m_pVertexLayout( NULL ), m_pVS( NULL ), m_pPS( NULL ), m_pSampleStateLinear( NULL )
{
}

/**
*/
SceneMgr::~SceneMgr()
{
}

/**
*/
HRESULT SceneMgr::OnCreateDevice( ID3D11Device* pd3dDevice )
{
    HRESULT hr = S_OK;

    V_RETURN( m_mesh.Create( pd3dDevice, L"powerplant\\powerplant.sdkmesh" ) );

    ID3DBlob* pBlob = NULL;

    // vertex shader
    V_RETURN( CompileShaderFromFile( L"RenderScene.hlsl", "VS_Main", "vs_5_0", &pBlob ) );
    V_RETURN( pd3dDevice->CreateVertexShader( pBlob->GetBufferPointer(), pBlob->GetBufferSize(), NULL, &m_pVS ) );

    // input layout
    // Define the vertex data layout
    const D3D11_INPUT_ELEMENT_DESC layoutMesh[] =
    {
        { "POSITION",  0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,  D3D11_INPUT_PER_VERTEX_DATA, 0 },
        { "NORMAL",    0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
        { "TEXCOORD",  0, DXGI_FORMAT_R32G32_FLOAT,    0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    };
    V_RETURN( pd3dDevice->CreateInputLayout( layoutMesh, sizeof( layoutMesh ) / sizeof( layoutMesh[0] ), pBlob->GetBufferPointer(),
        pBlob->GetBufferSize(), &m_pVertexLayout ) );

    SAFE_RELEASE( pBlob );

    // pixel shader
    V_RETURN( CompileShaderFromFile( L"RenderScene.hlsl", "PS_Main", "ps_5_0", &pBlob ) );
    V_RETURN( pd3dDevice->CreatePixelShader( pBlob->GetBufferPointer(), pBlob->GetBufferSize(), NULL, &m_pPS ) );
    
    SAFE_RELEASE( pBlob );

    // Setup constant buffers
    D3D11_BUFFER_DESC Desc;
    Desc.Usage = D3D11_USAGE_DYNAMIC;
    Desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    Desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    Desc.MiscFlags = 0;    
    Desc.ByteWidth = sizeof( ConstantBuf );
    V_RETURN( pd3dDevice->CreateBuffer( &Desc, NULL, &m_pConstantBuf ) );

    // bilinear sampler state
    D3D11_SAMPLER_DESC SamplerDesc;
    ZeroMemory( &SamplerDesc, sizeof( SamplerDesc ) );
    SamplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
    SamplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
    SamplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
    SamplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
    V_RETURN( pd3dDevice->CreateSamplerState( &SamplerDesc, &m_pSampleStateLinear ) );

    return hr;
}

/**
*/
void SceneMgr::OnDestroyDevice()
{
    m_mesh.Destroy();

    SAFE_RELEASE( m_pVS );
    SAFE_RELEASE( m_pVertexLayout );
    SAFE_RELEASE( m_pPS );
    SAFE_RELEASE( m_pConstantBuf );
    SAFE_RELEASE( m_pSampleStateLinear );
}

/**
*/
void SceneMgr::OnRender( ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, const CBaseCamera* pCamera, const CameraInfo& cameraInfo )
{
    HRESULT hr;

    // constant buffer
    ConstantBuf cb;

    D3DXMATRIX mWorldViewProjection;
    D3DXMATRIX mWorld;
    D3DXMATRIX mView;
    D3DXMATRIX mProj;
    D3DXMatrixIdentity( &mWorld );
    //mProj = *pCamera->GetProjMatrix();
    mProj = g_temporalAA.GetProjectionJitterMatrix( cameraInfo );
    mView = *pCamera->GetViewMatrix();
    mWorldViewProjection = mWorld * mView * mProj;

    D3DXVECTOR3 vEye = *pCamera->GetEyePt();

    cb.g_f3EyePt = D3DXVECTOR4( vEye.x, vEye.y, vEye.z, 1 );
    D3DXMatrixTranspose( &cb.g_f4x4View, &mView );
    D3DXMatrixTranspose( &cb.g_f4x4World, &mWorld );
    D3DXMatrixTranspose( &cb.g_f4x4WorldViewProjection, &mWorldViewProjection );

    D3D11_MAPPED_SUBRESOURCE MappedResource;            
    V( pd3dImmediateContext->Map( m_pConstantBuf, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource ) );
    ConstantBuf* p = (ConstantBuf*)MappedResource.pData;
    *p = cb;
    pd3dImmediateContext->Unmap( m_pConstantBuf, 0 );
    ID3D11Buffer* ppCB[1] = { m_pConstantBuf };
    pd3dImmediateContext->VSSetConstantBuffers( 0, 1, ppCB );
    pd3dImmediateContext->PSSetConstantBuffers( 0, 1, ppCB );

    pd3dImmediateContext->IASetInputLayout( m_pVertexLayout );

    pd3dImmediateContext->VSSetShader( m_pVS, NULL, 0 );
    pd3dImmediateContext->PSSetShader( m_pPS, NULL, 0 );

    ID3D11SamplerState* aSamplers[] = { m_pSampleStateLinear };
    pd3dImmediateContext->PSSetSamplers( 0, 1, aSamplers );

    m_mesh.Render( pd3dImmediateContext, 0, 1 );
}