#include <windows.h>
#include "irrKlang.h"
#include "DXUT.h"
#include "DXUTmisc.h"
#include "Camera.h"
#include <io.h>
#include <iostream>
#include <fcntl.h>
#include "DiManager.h"
#include "InputLayouts.h"
#include "Scene.h"
#include "EffectManager.h"
#include "Lights.h"
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#define DEG2RAD( a ) ( a * D3DX_PI / 180.f )


#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>


using namespace std;
using namespace irrklang;

//--------------------------------------------------------------------------------------
// Global Variables
//--------------------------------------------------------------------------------------
ISoundEngine* soundEngine;

Camera*	cam;
EffectManager* effectManager;
DiManager* diManager;
Scene* scene;

D3DXMATRIX invProjMatrix;

ID3DX10Font* font;
ID3DX10Sprite* sprite;
FLOAT OriginalBlendFactor[4];  
UINT OriginalSampleMask = 0;  
ID3D10BlendState* pOriginalBlendState10=NULL;

ID3D10Texture2D* colorDepthBuffer[2];
ID3D10RenderTargetView* colorDepthBufferRTView[2];
ID3D10ShaderResourceView* colorDepthBufferRSCView[2];

ID3D10Texture2D* gaussBuffer;
ID3D10RenderTargetView* gaussBufferRTView;
ID3D10ShaderResourceView* gaussBufferRSCView;

ID3D10Texture2D* gaussBlurredBuffer;
ID3D10RenderTargetView* gaussBlurredBufferRTView;
ID3D10ShaderResourceView* gaussBlurredBufferRSCView;

ID3D10Texture2D* shadowMap;
ID3D10RenderTargetView* shadowMapRTView;
ID3D10ShaderResourceView* shadowMapRSCView;
ID3D10Texture2D* shadowMapDepthBuffer;
ID3D10DepthStencilView* shadowMapDepthStencilView;
D3D10_VIEWPORT shadowMapViewport;
D3D10_VIEWPORT defaultViewport;
UINT shadowMapSize = 4096;



UINT bbHeight;
UINT bbWidth;

D3DXVECTOR4 depthInfo;

D3DXVECTOR2 gaussVerticalSampleOffsets[16];
D3DXVECTOR4 gaussVerticalSampleWeights[16];
D3DXVECTOR2 gaussHorizontalSampleOffsets[16];
D3DXVECTOR4 gaussHorizontalSampleWeights[16];

D3DXVECTOR2 pcfOffsets[8];

float fadeTime = 1.0f;
float timeTillFullFadeOut=6.0f;
float timeToWaitTillStart = 7.0f;
float timeSinceFirstFrame =0.0f;

int currentIntroTex = 0;
int nrOfIntroStages = 4;

ID3D10ShaderResourceView* introTex[4];

bool soundStarted = false;
bool introFinished = false;

bool shadowMapGenerated = false;

//DXUT callbacks declarations
bool CALLBACK IsDeviceAcceptable( UINT Adapter, UINT Output, D3D10_DRIVER_TYPE DeviceType, DXGI_FORMAT BufferFormat, bool bWindowed, void* pUserContext );
HRESULT CALLBACK OnCreateDevice( ID3D10Device* pd3dDevice, const DXGI_SURFACE_DESC* pBufferSurfaceDesc, void* pUserContext );
HRESULT CALLBACK OnResizedSwapChain( ID3D10Device* pd3dDevice, IDXGISwapChain* pSwapChain, const DXGI_SURFACE_DESC* pBufferSurfaceDesc, void* pUserContext );
void CALLBACK OnFrameRender( ID3D10Device* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext );
void CALLBACK OnReleasingSwapChain( void* pUserContext );
void CALLBACK OnDestroyDevice( void* pUserContext );
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing, void* pUserContext );
void CALLBACK OnFrameMove( double fTime, float fElapsedTime, void* pUserContext );
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, void* pUserContext );

inline float GaussianDistribution( float x, float y, float rho )
{
    float g = 1.0f / sqrtf( 2.0f * D3DX_PI * rho * rho );
    g *= expf( -( x * x + y * y ) / ( 2 * rho * rho ) );

    return g;
}

inline void WritePCFOffsets()
{
	pcfOffsets[0] = D3DXVECTOR2(-1.0f/(float)shadowMapSize,-1.0f/(float)shadowMapSize);
	pcfOffsets[1] = D3DXVECTOR2(1.0f/(float)shadowMapSize,-1.0f/(float)shadowMapSize);
	pcfOffsets[2] = D3DXVECTOR2(-1.0f/(float)shadowMapSize,1.0f/(float)shadowMapSize);
	pcfOffsets[3] = D3DXVECTOR2(1.0f/(float)shadowMapSize,1.0f/(float)shadowMapSize);
	
	pcfOffsets[4] = D3DXVECTOR2(-0.5f/(float)shadowMapSize,-0.5f/(float)shadowMapSize);
	pcfOffsets[5] = D3DXVECTOR2(0.5f/(float)shadowMapSize,-0.5f/(float)shadowMapSize);
	pcfOffsets[6] = D3DXVECTOR2(-0.5f/(float)shadowMapSize,0.5f/(float)shadowMapSize);
	pcfOffsets[7] = D3DXVECTOR2(0.5f/(float)shadowMapSize,0.5f/(float)shadowMapSize);
}

HRESULT GetGaussianKernel( DWORD textureSize,
                                float gaussTexCoordOffset[15],
                                D3DXVECTOR4* gaussWeights,
                                float fDeviation, float fMultiplier )
{
    int i = 0;
    float tu = 1.0f / ( float )textureSize;

    // Fill the center texel
    float weight = 1.0f * GaussianDistribution( 0, 0, fDeviation );
    gaussWeights[0] = D3DXVECTOR4( weight, weight, weight, 1.0f );

    gaussTexCoordOffset[0] = 0.0f;

    // Fill the right side
    for( i = 1; i < 8; i++ )
    {
        weight = fMultiplier * GaussianDistribution( ( float )i, 0, fDeviation );
        gaussTexCoordOffset[i] = i * tu;

        gaussWeights[i] = D3DXVECTOR4( weight, weight, weight, 1.0f );
    }

    // Copy to the left side
    for( i = 8; i < 15; i++ )
    {
        gaussWeights[i] = gaussWeights[i - 7];
        gaussTexCoordOffset[i] = -gaussTexCoordOffset[i - 7];
    }

    return S_OK;
}

void loadIntroTextures()
{
	HRESULT hr = D3DX10CreateShaderResourceViewFromFile( DXUTGetD3D10Device(), L"../../textures/intro/intro1.jpg", NULL, NULL, &introTex[0], NULL );
	hr = D3DX10CreateShaderResourceViewFromFile( DXUTGetD3D10Device(), L"../../textures/intro/intro2.jpg", NULL, NULL, &introTex[1], NULL );
	hr = D3DX10CreateShaderResourceViewFromFile( DXUTGetD3D10Device(), L"../../textures/intro/intro3.jpg", NULL, NULL, &introTex[2], NULL );
	hr = D3DX10CreateShaderResourceViewFromFile( DXUTGetD3D10Device(), L"../../textures/intro/intro4.jpg", NULL, NULL, &introTex[3], NULL );
}

void createRenderTargets(ID3D10Device* device, const DXGI_SURFACE_DESC* backBufferSurfaceDesc)
{
	depthInfo.x = -10.0f;
	depthInfo.y = 10.0f;
	depthInfo.z = 100.0f;
	depthInfo.w = (float)bbWidth;

	HRESULT hr;
	/*
	SAFE_RELEASE(colorDepthBuffer[0]);
	SAFE_RELEASE(colorDepthBufferRTView[0]);
	SAFE_RELEASE(colorDepthBufferRSCView[0]);
	SAFE_RELEASE(colorDepthBuffer[1]);
	SAFE_RELEASE(colorDepthBufferRTView[1]);
	SAFE_RELEASE(colorDepthBufferRSCView[1]);

	SAFE_RELEASE(gaussBuffer);
	SAFE_RELEASE(gaussBufferRTView);
	SAFE_RELEASE(gaussBufferRSCView);

	SAFE_RELEASE(gaussBlurredBuffer);
	SAFE_RELEASE(gaussBlurredBufferRTView);
	SAFE_RELEASE(gaussBlurredBufferRSCView);

	SAFE_RELEASE(shadowMap);
	SAFE_RELEASE(shadowMapRTView);
	SAFE_RELEASE(shadowMapRSCView);
	SAFE_RELEASE(shadowMapDepthBuffer);
	SAFE_RELEASE(shadowMapDepthStencilView);*/

	bbHeight = backBufferSurfaceDesc->Height;
	bbWidth = backBufferSurfaceDesc->Width;
	
	shadowMapViewport.Width = shadowMapSize;
	shadowMapViewport.Height = shadowMapSize;
	shadowMapViewport.MinDepth = 0.0f;
	shadowMapViewport.MaxDepth = 1.0f;
	shadowMapViewport.TopLeftX = 0;
	shadowMapViewport.TopLeftY = 0;

	defaultViewport.Width = backBufferSurfaceDesc->Width;
	defaultViewport.Height = backBufferSurfaceDesc->Height;
	defaultViewport.MinDepth = 0.0f;
	defaultViewport.MaxDepth = 1.0f;
	defaultViewport.TopLeftX = 0;
	defaultViewport.TopLeftY = 0;

	D3D10_TEXTURE2D_DESC depthTextureDesc;
	depthTextureDesc.Width = shadowMapSize;
	depthTextureDesc.Height = shadowMapSize;
	depthTextureDesc.MipLevels = 1;
	depthTextureDesc.ArraySize = 1;
	depthTextureDesc.Format = DXGI_FORMAT_R32_TYPELESS;
	depthTextureDesc.SampleDesc.Count = 1;
	depthTextureDesc.SampleDesc.Quality = 0;
	depthTextureDesc.Usage = D3D10_USAGE_DEFAULT;
	depthTextureDesc.BindFlags = D3D10_BIND_DEPTH_STENCIL | D3D10_BIND_SHADER_RESOURCE;
	depthTextureDesc.CPUAccessFlags = 0;
	depthTextureDesc.MiscFlags = 0;

	V(device->CreateTexture2D(&depthTextureDesc, NULL, &shadowMapDepthBuffer));

	D3D10_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
	depthStencilViewDesc.Format = DXGI_FORMAT_D32_FLOAT;
	depthStencilViewDesc.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D;
	depthStencilViewDesc.Texture2D.MipSlice = 0;

	V(device->CreateDepthStencilView(shadowMapDepthBuffer, &depthStencilViewDesc, &shadowMapDepthStencilView));

	D3D10_TEXTURE2D_DESC colorDepthBufferDesc;
	colorDepthBufferDesc.Width = backBufferSurfaceDesc->Width;
	colorDepthBufferDesc.Height = backBufferSurfaceDesc->Height;
	colorDepthBufferDesc.MipLevels = 1;
	colorDepthBufferDesc.ArraySize = 1;
	colorDepthBufferDesc.Format = backBufferSurfaceDesc->Format;
	colorDepthBufferDesc.SampleDesc = backBufferSurfaceDesc->SampleDesc;
	colorDepthBufferDesc.Usage = D3D10_USAGE_DEFAULT;
	colorDepthBufferDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
	colorDepthBufferDesc.CPUAccessFlags = 0;
	colorDepthBufferDesc.MiscFlags = 0;;

	V(device->CreateTexture2D(&colorDepthBufferDesc, NULL, &colorDepthBuffer[0]));

	V(device->CreateTexture2D(&colorDepthBufferDesc, NULL, &gaussBuffer));
	V(device->CreateTexture2D(&colorDepthBufferDesc, NULL, &gaussBlurredBuffer));

	colorDepthBufferDesc.Format = DXGI_FORMAT_R32_FLOAT;

	V(device->CreateTexture2D(&colorDepthBufferDesc, NULL, &colorDepthBuffer[1]));

	colorDepthBufferDesc.Width = shadowMapSize;
	colorDepthBufferDesc.Height = shadowMapSize;

	V(device->CreateTexture2D(&colorDepthBufferDesc, NULL, &shadowMap));

	// Create the render target
	D3D10_RENDER_TARGET_VIEW_DESC renderTargetDesc;
	renderTargetDesc.Format = backBufferSurfaceDesc->Format;
	renderTargetDesc.ViewDimension = D3D10_RTV_DIMENSION_TEXTURE2D;
	renderTargetDesc.Texture2D.MipSlice = 0;
	V(device->CreateRenderTargetView(colorDepthBuffer[0], &renderTargetDesc, &colorDepthBufferRTView[0]));
	V(device->CreateRenderTargetView(gaussBuffer, &renderTargetDesc, &gaussBufferRTView));
	V(device->CreateRenderTargetView(gaussBlurredBuffer, &renderTargetDesc, &gaussBlurredBufferRTView));

	renderTargetDesc.Format = DXGI_FORMAT_R32_FLOAT ;

	V(device->CreateRenderTargetView(colorDepthBuffer[1], &renderTargetDesc, &colorDepthBufferRTView[1]));
	V(device->CreateRenderTargetView(shadowMap, &renderTargetDesc, &shadowMapRTView));

	// Create the shader-resource view
	D3D10_SHADER_RESOURCE_VIEW_DESC resourceDesc;
	resourceDesc.Format = backBufferSurfaceDesc->Format;
	resourceDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
	resourceDesc.Texture2D.MostDetailedMip = 0;
	resourceDesc.Texture2D.MipLevels = 1;
	V(device->CreateShaderResourceView(colorDepthBuffer[0], &resourceDesc, &colorDepthBufferRSCView[0]));

	V(device->CreateShaderResourceView(gaussBuffer, &resourceDesc, &gaussBufferRSCView));
	V(device->CreateShaderResourceView(gaussBlurredBuffer, &resourceDesc, &gaussBlurredBufferRSCView));

	resourceDesc.Format = DXGI_FORMAT_R32_FLOAT;

	V(device->CreateShaderResourceView(colorDepthBuffer[1], &resourceDesc, &colorDepthBufferRSCView[1]));
	V(device->CreateShaderResourceView(shadowMap, &resourceDesc, &shadowMapRSCView));
	
	float gaussVerticalFloatSampleOffsets[16];
	float gaussHorizontalFloatSampleOffsets[16];

	GetGaussianKernel(backBufferSurfaceDesc->Width,gaussVerticalFloatSampleOffsets,gaussVerticalSampleWeights,4.0f,2.2f);
	GetGaussianKernel(backBufferSurfaceDesc->Height,gaussHorizontalFloatSampleOffsets,gaussHorizontalSampleWeights,3.0f,2.0f);
    
	for(int i = 0; i < 16; i++ )
    {
        gaussVerticalSampleOffsets[i] = D3DXVECTOR2(gaussVerticalFloatSampleOffsets[i], 0.0f);
		gaussHorizontalSampleOffsets[i] = D3DXVECTOR2(0.0f, gaussHorizontalFloatSampleOffsets[i]);
    }

	WritePCFOffsets();

	cout<<"Rendertargets created" << endl;

	
}

void InitializeDebugConsole()
{	
    //Create a console for this application
    AllocConsole();
    //Redirect unbuffered STDOUT to the console
	HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    int SystemOutput = _open_osfhandle(intptr_t(ConsoleOutput), _O_TEXT);
    FILE *COutputHandle = _fdopen(SystemOutput, "w" );
    *stdout = *COutputHandle;
    setvbuf(stdout, NULL, _IONBF, 0);

    //Redirect unbuffered STDERR to the console
	HANDLE ConsoleError = GetStdHandle(STD_ERROR_HANDLE);
    int SystemError = _open_osfhandle(intptr_t(ConsoleError), _O_TEXT);
    FILE *CErrorHandle = _fdopen(SystemError, "w" );
    *stderr = *CErrorHandle;
    setvbuf(stderr, NULL, _IONBF, 0);

    //Redirect unbuffered STDIN to the console
	HANDLE ConsoleInput = GetStdHandle(STD_INPUT_HANDLE);
    int SystemInput = _open_osfhandle(intptr_t(ConsoleInput), _O_TEXT);
    FILE *CInputHandle = _fdopen(SystemInput, "r" );
    *stdin = *CInputHandle;
    setvbuf(stdin, NULL, _IONBF, 0);
    
    //make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog point to console as well
    ios::sync_with_stdio(true);
}

void ShutdownDebugConsole(void)
{
	//Write "Press any key to exit"
	HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	DWORD CharsWritten;
	WriteConsole(ConsoleOutput, L"\nPress any key to exit", 22, &CharsWritten, 0);
	//Disable line-based input mode so we can get a single character
	HANDLE ConsoleInput = GetStdHandle(STD_INPUT_HANDLE);
	SetConsoleMode(ConsoleInput, 0);
	//Read a single character
	TCHAR InputBuffer;
	DWORD CharsRead;
	ReadConsole(ConsoleInput, &InputBuffer, 1, &CharsRead, 0);
}


int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
	//show memoryleaks
	_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

	//InitializeDebugConsole();
	
	cam = Camera::getInstance();

	scene = new Scene();
	scene->read("../../temple.pos.gz");
	effectManager = EffectManager::getInstance();

	// Set DXUT callbacks
	DXUTSetCallbackD3D10DeviceAcceptable( IsDeviceAcceptable );
    DXUTSetCallbackD3D10DeviceCreated( OnCreateDevice );
    DXUTSetCallbackD3D10SwapChainResized( OnResizedSwapChain );
    DXUTSetCallbackD3D10SwapChainReleasing( OnReleasingSwapChain );
    DXUTSetCallbackD3D10DeviceDestroyed( OnDestroyDevice );
    DXUTSetCallbackD3D10FrameRender( OnFrameRender );
    DXUTSetCallbackMsgProc( MsgProc );
    DXUTSetCallbackFrameMove( OnFrameMove );
    DXUTSetCallbackDeviceChanging( ModifyDeviceSettings );

    DXUTInit( true, true, NULL ); // 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
	DXUTSetIsInGammaCorrectMode(false);
    DXUTCreateWindow( L"Echtzeitgrafik ArrivalToEarth" );
    DXUTCreateDevice( false);

	diManager = DiManager::getInstance();
	diManager->initDirectInput();

	soundEngine = createIrrKlangDevice();
	
    DXUTMainLoop(); // Enter into the DXUT render loop
	//ShutdownDebugConsole();

    return DXUTGetExitCode();
}

//--------------------------------------------------------------------------------------
// Reject any D3D10 devices that aren't acceptable by returning false
//--------------------------------------------------------------------------------------
bool CALLBACK IsDeviceAcceptable( UINT Adapter, UINT Output, D3D10_DRIVER_TYPE DeviceType,
                                       DXGI_FORMAT BufferFormat, bool bWindowed, void* pUserContext )
{
    return true;
}


//--------------------------------------------------------------------------------------
// Create any D3D10 resources that aren't dependant on the back buffer
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnCreateDevice( ID3D10Device* pd3dDevice, const DXGI_SURFACE_DESC* pBufferSurfaceDesc,
                                      void* pUserContext )
{

	D3DX10CreateFont( pd3dDevice, 15, 0, FW_REGULAR, 1, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, L"Arial", &font );

	D3DX10CreateSprite(pd3dDevice,512,&sprite);

	effectManager->initEffects();

	effectManager->getPhongEffect()->getEffectVariable()->GetVariableByName("light")->SetRawValue((void*)light,0,sizeof(PointLight)*NROFLIGHTS);
		
	scene->createVBOs(effectManager->getPhongEffect()->getEffectVariable());
	scene->loadTextures();
    // Set primitive topology
    pd3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	
	loadIntroTextures();

    return S_OK;
}


//--------------------------------------------------------------------------------------
// Create any D3D10 resources that depend on the back buffer
// Create and set the depth stencil texture if needed
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnResizedSwapChain( ID3D10Device* pd3dDevice, IDXGISwapChain* pSwapChain,
                                          const DXGI_SURFACE_DESC* pBufferSurfaceDesc, void* pUserContext )
{
    // Setup the projection parameters again
    float fAspect = static_cast<float>( pBufferSurfaceDesc->Width ) / static_cast<float>( pBufferSurfaceDesc->Height );
	cam->updateProjection(fAspect);
	cam->buildLightViewAndProjection();
	effectManager->getPhongEffect()->setProjectionMatrix(cam->getProjectionMatrix());
	
	createRenderTargets(pd3dDevice,pBufferSurfaceDesc);
	shadowMapGenerated = false;
    return S_OK;
}

void renderIntroTex(ID3D10Device* pd3dDevice, ID3D10RenderTargetView* screenRT,float fade)
{
	pd3dDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_POINTLIST);

	pd3dDevice->OMSetRenderTargets(1,&screenRT,NULL);
	pd3dDevice->RSSetViewports(1,&defaultViewport);

	effectManager->getIntroEffect()->setInputLayout();
	effectManager->getIntroEffect()->setFullScreenTexture(introTex[currentIntroTex]);
	effectManager->getIntroEffect()->setFadeValue((float)fade);

	D3D10_TECHNIQUE_DESC techDesc;
	effectManager->getIntroEffect()->getTechniqueVariable()->GetDesc( &techDesc );
    for( UINT p = 0; p < techDesc.Passes; ++p )
    {
		effectManager->getIntroEffect()->getTechniqueVariable()->GetPassByIndex(p)->Apply(0);
		pd3dDevice->Draw(1,0);
    }
}

void renderShadowMap(ID3D10Device* pd3dDevice)
{
	pd3dDevice->OMSetRenderTargets(1,&shadowMapRTView,shadowMapDepthStencilView);
	pd3dDevice->RSSetViewports(1,&shadowMapViewport);

	effectManager->getShadowMappingEffect()->setViewMatrix(cam->getLightViewMatrix());
	effectManager->getShadowMappingEffect()->setProjectionMatrix(cam->getLightProjectionMatrix());

	D3D10_TECHNIQUE_DESC techDesc;
	effectManager->getShadowMappingEffect()->getTechniqueVariable()->GetDesc( &techDesc );
    for( UINT p = 0; p < techDesc.Passes; ++p )
    {
       scene->render(effectManager->getShadowMappingEffect(),effectManager->getShadowMappingEffect()->getTechniqueVariable()->GetPassByIndex( p ));
    }
}

void renderLightPassAndDOFDepthMap(ID3D10Device* pd3dDevice,ID3D10DepthStencilView* lightPassDepthBuffer)
{
	pd3dDevice->OMSetRenderTargets(2,colorDepthBufferRTView,lightPassDepthBuffer);
	pd3dDevice->RSSetViewports(1,&defaultViewport);

	effectManager->getPhongEffect()->setViewMatrix(cam->getViewMatrix());
	effectManager->getPhongEffect()->setEyePoisition(cam->getEyePosition());

	effectManager->getPhongEffect()->setPCFOffsets(pcfOffsets);

    effectManager->getPhongEffect()->setLightViewMatrix(cam->getLightViewMatrix());
	effectManager->getPhongEffect()->setLightProjectionMatrix(cam->getLightProjectionMatrix());
	effectManager->getPhongEffect()->setShadowMapTexture(shadowMapRSCView);

	D3D10_TECHNIQUE_DESC techDesc;
	effectManager->getPhongEffect()->getTechniqueVariable()->GetDesc( &techDesc );
    for( UINT p = 0; p < techDesc.Passes; ++p )
    {
       scene->render(effectManager->getPhongEffect(),effectManager->getPhongEffect()->getTechniqueVariable()->GetPassByIndex( p ));
    }
}

void blurrLightPassTexture(ID3D10Device* pd3dDevice)
{
	effectManager->getPostProcessingEffect()->setDiffuseTexture(colorDepthBufferRSCView[0]);
	effectManager->getPostProcessingEffect()->setGaussOffsets((D3DXVECTOR4*)gaussVerticalSampleOffsets);
	effectManager->getPostProcessingEffect()->setGaussWeights((D3DXVECTOR2*)gaussVerticalSampleWeights);
	pd3dDevice->OMSetRenderTargets(1,&gaussBufferRTView,NULL);
	
	effectManager->getPostProcessingEffect()->setInputLayout();
	pd3dDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_POINTLIST);
	D3D10_TECHNIQUE_DESC techDesc;
	effectManager->getPostProcessingEffect()->getGaussTechniqueVariable()->GetDesc( &techDesc );
	for(UINT p=0; p<techDesc.Passes; p++)
	{
		effectManager->getPostProcessingEffect()->getGaussTechniqueVariable()->GetPassByIndex(p)->Apply(0);
		pd3dDevice->Draw(1,0);
	}
	
	effectManager->getPostProcessingEffect()->setDiffuseTexture(gaussBufferRSCView);
	effectManager->getPostProcessingEffect()->setGaussOffsets((D3DXVECTOR4*)gaussHorizontalSampleOffsets);
	effectManager->getPostProcessingEffect()->setGaussWeights((D3DXVECTOR2*)gaussHorizontalSampleWeights);
	pd3dDevice->OMSetRenderTargets(1,&gaussBlurredBufferRTView,NULL);
	
	effectManager->getPostProcessingEffect()->getGaussTechniqueVariable()->GetDesc( &techDesc );
	for(UINT p=0; p<techDesc.Passes; p++)
	{
		effectManager->getPostProcessingEffect()->getGaussTechniqueVariable()->GetPassByIndex(p)->Apply(0);
		pd3dDevice->Draw(1,0);
	}
}

void renderToScreenWithDOF(ID3D10Device* pd3dDevice,ID3D10RenderTargetView* screenRT)
{
	//pd3dDevice->GenerateMips(colorDepthBufferRSCView[0]);
	
	effectManager->getPostProcessingEffect()->setBlurredTexture(gaussBlurredBufferRSCView);
	effectManager->getPostProcessingEffect()->setDiffuseTexture(colorDepthBufferRSCView[0]);
	effectManager->getPostProcessingEffect()->setDepthTexture(colorDepthBufferRSCView[1]);
	effectManager->getPostProcessingEffect()->setDepthInfo(&depthInfo);
	pd3dDevice->OMSetRenderTargets(1,&screenRT,NULL);
	
	// render texture (to screen)
	D3D10_TECHNIQUE_DESC techDesc;
	effectManager->getPostProcessingEffect()->getTechniqueVariable()->GetDesc( &techDesc );
	for(UINT p=0; p<techDesc.Passes; p++)
	{
		effectManager->getPostProcessingEffect()->getTechniqueVariable()->GetPassByIndex(p)->Apply(0);
		pd3dDevice->Draw(1,0);
	}
}

void renderText(ID3D10Device* pd3dDevice)
{
	pd3dDevice->OMGetBlendState(&pOriginalBlendState10, OriginalBlendFactor, &OriginalSampleMask);  
	
	pd3dDevice->RSSetState(NULL);
	sprite->Begin( D3DX10_SPRITE_SORT_TEXTURE|D3DX10_SPRITE_SAVE_STATE );

	RECT rc;
	rc.left=5;
	rc.right=100;
	rc.top=5;
	rc.bottom=100;
	font->DrawText(sprite,DXUTGetFrameStats(true),100,&rc,DT_NOCLIP,D3DXCOLOR(1.0,1.0f,1.0,1.0));

	sprite->End();

	pd3dDevice->OMSetBlendState(pOriginalBlendState10, OriginalBlendFactor, OriginalSampleMask);
}

//--------------------------------------------------------------------------------------
// Render the scene using the D3D10 device
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameRender( ID3D10Device* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
	

	// Clear the back buffer
    float ClearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; // red, green, blue, alpha
    ID3D10RenderTargetView* pRTV = DXUTGetD3D10RenderTargetView();
    pd3dDevice->ClearRenderTargetView( pRTV, ClearColor );
	pd3dDevice->ClearRenderTargetView(colorDepthBufferRTView[0],ClearColor);
	pd3dDevice->ClearRenderTargetView(colorDepthBufferRTView[1],ClearColor);
	pd3dDevice->ClearRenderTargetView(gaussBufferRTView,ClearColor);
	pd3dDevice->ClearRenderTargetView(gaussBlurredBufferRTView,ClearColor);
	
    // Clear the depth stencil
    ID3D10DepthStencilView* pDSV = DXUTGetD3D10DepthStencilView();
    pd3dDevice->ClearDepthStencilView( pDSV, D3D10_CLEAR_DEPTH | D3D10_CLEAR_STENCIL, 1.0, 0 );
	pd3dDevice->ClearDepthStencilView( shadowMapDepthStencilView, D3D10_CLEAR_DEPTH | D3D10_CLEAR_STENCIL, 1.0, 0 );
	
	if(!soundStarted && timeSinceFirstFrame > timeToWaitTillStart)
	{
			soundEngine->play2D("../../sound/soundtrack.mp3");
			soundStarted=true;
	}

	if(timeSinceFirstFrame > timeToWaitTillStart)
	{
		

		if(!introFinished)
		{
			fadeTime -= fElapsedTime/timeTillFullFadeOut;
			if(fadeTime <0)
			{
				fadeTime=1.0f;
				currentIntroTex++;
				if(currentIntroTex == nrOfIntroStages)
					introFinished=true;
			}

			renderIntroTex(pd3dDevice,pRTV,fadeTime);
		}

		if(introFinished)
		{
			effectManager->getPhongEffect()->setInputLayout();
			pd3dDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
		    
				if(!shadowMapGenerated)
			{
				pd3dDevice->ClearRenderTargetView(shadowMapRTView,ClearColor);
				renderShadowMap(pd3dDevice);
				shadowMapGenerated=true;
			}

			renderLightPassAndDOFDepthMap(pd3dDevice,pDSV);
			
			blurrLightPassTexture(pd3dDevice);
			
			renderToScreenWithDOF(pd3dDevice,pRTV);
			
			//renderText(pd3dDevice);

			D3D10_TECHNIQUE_DESC techDesc;
			effectManager->getPhongEffect()->getTechniqueVariable()->GetDesc( &techDesc );
			
			effectManager->getPhongEffect()->setShadowMapTexture(NULL);

			for( UINT p = 0; p < techDesc.Passes; ++p )
			{
				effectManager->getPhongEffect()->getTechniqueVariable()->GetPassByIndex( p )->Apply(0);
			}
		}
	}
	
}


//--------------------------------------------------------------------------------------
// Release D3D10 resources created in OnD3D10ResizedSwapChain 
//--------------------------------------------------------------------------------------
void CALLBACK OnReleasingSwapChain( void* pUserContext )
{
	SAFE_RELEASE(colorDepthBuffer[0]);
	SAFE_RELEASE(colorDepthBufferRTView[0]);
	SAFE_RELEASE(colorDepthBufferRSCView[0]);
	SAFE_RELEASE(colorDepthBuffer[1]);
	SAFE_RELEASE(colorDepthBufferRTView[1]);
	SAFE_RELEASE(colorDepthBufferRSCView[1]);

	SAFE_RELEASE(gaussBuffer);
	SAFE_RELEASE(gaussBufferRTView);
	SAFE_RELEASE(gaussBufferRSCView);
	SAFE_RELEASE(gaussBlurredBuffer);
	SAFE_RELEASE(gaussBlurredBufferRTView);
	SAFE_RELEASE(gaussBlurredBufferRSCView);

	SAFE_RELEASE(shadowMap);
	SAFE_RELEASE(shadowMapRTView);
	SAFE_RELEASE(shadowMapRSCView);
	SAFE_RELEASE(shadowMapDepthBuffer);
	SAFE_RELEASE(shadowMapDepthStencilView);
}


//--------------------------------------------------------------------------------------
// Release D3D10 resources created in OnD3D10CreateDevice 
//--------------------------------------------------------------------------------------
void CALLBACK OnDestroyDevice( void* pUserContext )
{
	delete scene;
	EffectManager::releaseEffectManager();
	DiManager::releaseDiManager();
	SAFE_RELEASE(font);
	SAFE_RELEASE(sprite);
	SAFE_RELEASE(introTex[0]);
	SAFE_RELEASE(introTex[1]);
	SAFE_RELEASE(introTex[2]);
	SAFE_RELEASE(introTex[3]);

	soundEngine->drop();
}


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

	pDeviceSettings->d3d10.SyncInterval = 1;
    return true;
}


//--------------------------------------------------------------------------------------
// Handle updates to the scene.  This is called regardless of which D3D API is used
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameMove( double timeSinceStart, float timeSinceLastFrame, void* pUserContext )
{
	timeSinceFirstFrame += timeSinceLastFrame;
	diManager->update();
	
	if(introFinished)
		cam->update(timeSinceLastFrame);
	
	cam->buildLightViewAndProjection();

	if(diManager->isKeyPressed(DIK_NUMPAD7))
	{
		depthInfo.x += 1.0f;
	}

	if(diManager->isKeyPressed(DIK_NUMPAD1))
	{
		depthInfo.x -= 1.0f;
	}

	if(diManager->isKeyPressed(DIK_NUMPAD8))
	{
		depthInfo.y += 1.0f;
	}

	if(diManager->isKeyPressed(DIK_NUMPAD2))
	{
		depthInfo.y -= 1.0f;
	}

	if(diManager->isKeyPressed(DIK_NUMPAD9))
	{
		depthInfo.z += 1.0f;
	}

	if(diManager->isKeyPressed(DIK_NUMPAD3))
	{
		depthInfo.z -= 1.0f;
	}
}


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




