#include "dxstdafx.h"
#include "SkyBox.h"
#include "TransformMatrixesManager.h"

#pragma warning(push)			// Pushing a warning disabler...
#pragma warning(disable:4995)	// to disable deprecated warning...
#include <iostream>				// ..for those old C++ libs
#include <fstream>				//
using namespace std;			//
#pragma warning(pop)			// Restoring the previous warning states

#pragma once


//------------------------------------------------------------------------------------------
// Constructor
// load the skybox model and we then load a texture
//------------------------------------------------------------------------------------------
SkyBox::SkyBox()
{
	// Nullify every class data
	m_pSkyBoxMesh = NULL;
	m_pSkyBoxCubeTexture = NULL;
	m_pMeshMaterials = NULL;
	m_dwNumMaterials = 0;
	
	m_pEffect = NULL;
	m_hSkyBoxTechnique =
	m_hMatCurrentWV =
	m_hMatPreviousWV =
	m_hMatProj =
	m_hSkyBoxCubeTexture = NULL;

	// Retrieve a pointer to the transform matrix manager
	m_pTransMatMgr = TransformMatrixesManager::GetInstance();

	// Retrieve the D3D device and quit if no good
	LPDIRECT3DDEVICE9 pd3dDevice = DXUTGetD3DDevice();
	if(!pd3dDevice) return;

	//------------LOADING AN D3DXMESH FROM AN XFILE-------------//
	// Creating a generic D3DXBuffer to retrieve materials
	LPD3DXBUFFER pD3DXMtrlBuffer = NULL;

	// Load the mesh from the specified file
	if(FAILED(D3DXLoadMeshFromX(L"./Models/SkyBoxes/alley_skybox.x", D3DXMESH_32BIT|D3DXMESH_MANAGED, pd3dDevice, NULL, &pD3DXMtrlBuffer, NULL, &m_dwNumMaterials, &m_pSkyBoxMesh)))
		MessageBox(NULL, L"./Models/SkyBoxes/alley_skybox.x", L"Error loading X-File", MB_OK);
	else
	{
		// We need to extract the material properties and texture names from the pD3DXMtrlBuffer
		D3DXMATERIAL * d3dxMaterials = (D3DXMATERIAL*)pD3DXMtrlBuffer->GetBufferPointer();
		m_pMeshMaterials = new D3DMATERIAL9[m_dwNumMaterials];
		if(m_pMeshMaterials == NULL) MessageBox(NULL, L"Out of Memory", L"Framework", MB_OK);

		for(DWORD i=0; i<m_dwNumMaterials; ++i)
		{
			// Copy the material
			m_pMeshMaterials[i] = d3dxMaterials[i].MatD3D;

			// Set the ambient color for the material (D3DX does not do this)
			m_pMeshMaterials[i].Ambient = m_pMeshMaterials[i].Diffuse;
		}

		// We just finished dealing with the material buffer
		SAFE_RELEASE(pD3DXMtrlBuffer);
	}
	
	// Loading the texture
	// TODO : a parametrizable texture loading
	if(FAILED(D3DXCreateCubeTextureFromFile(pd3dDevice, L"./Models/SkyBoxes/uffizi_cross.dds", &m_pSkyBoxCubeTexture)))
		MessageBox(NULL, L"./Models/SkyBoxes/uffizi_cross.dds", L"Error loading the skybox texture", MB_OK);

	// Compile the effect
	if( SUCCEEDED(CompileEffect(L"./FXFiles/RealTime/SkyBox.fx")) )
		SetupHandles();
}


//------------------------------------------------------------------------------------------
// Constructor
// load the skybox model and we then load a texture
//------------------------------------------------------------------------------------------
SkyBox::~SkyBox()
{	
	// Destroy the materials
	delete [] m_pMeshMaterials;
	m_pMeshMaterials = NULL;

	// Nullify every class data
	SAFE_RELEASE(m_pSkyBoxCubeTexture);
	SAFE_RELEASE(m_pSkyBoxMesh);
	SAFE_RELEASE(m_pEffect);
}


//--------------------------------------------------------------------------------------
// Compile an effect file and store the binary code in m_pEffect
// Return a log file with error compilation messages if some problems are detected
//--------------------------------------------------------------------------------------
HRESULT SkyBox::CompileEffect(LPCWSTR filePath)
{
	// Flushing the current effect
	SAFE_RELEASE(m_pEffect);

	// Load the effect
	ID3DXBuffer *pBuffer = NULL; //to store the compilation error messages
	if(FAILED(D3DXCreateEffectFromFile(DXUTGetD3DDevice(), filePath, NULL, NULL, D3DXFX_NOT_CLONEABLE|D3DXSHADER_PREFER_FLOW_CONTROL, NULL, &m_pEffect, &pBuffer)))
	{
		// Dumping message if there are some
		if(pBuffer != NULL)
		{
			ofstream fileStream("shader_dump.txt");
			fileStream << "Failed compiling shader\n";
			char * compileString = (char*)(pBuffer->GetBufferPointer());
			for(unsigned int i=0; i<pBuffer->GetBufferSize(); ++i)
				fileStream << compileString[i] ;

			fileStream.close();
			pBuffer->Release();
		}
		return E_FAIL;
	}

	return S_OK;
}


//--------------------------------------------------------------------------------------
// Prepare the common handles
//--------------------------------------------------------------------------------------
void SkyBox::SetupHandles()
{
	// If the effect has been successfully compiled
	if(m_pEffect)
	{
		// Retrieve the two WorldView matrixes
		m_hMatCurrentWV = m_pEffect->GetParameterByName(NULL, "g_mCurrentWorldView");
		m_hMatPreviousWV = m_pEffect->GetParameterByName(NULL, "g_mPreviousWorldView");
		
		m_hMatProj = m_pEffect->GetParameterByName(NULL, "g_mProj");
		
		m_hSkyBoxCubeTexture = m_pEffect->GetParameterByName(NULL, "g_SkyBoxTex");

		// Prepare the handle for the technique
		m_hSkyBoxTechnique = m_pEffect->GetTechniqueByName("RenderSkyBox");
	}
}


//--------------------------------------------------------------------------------------
// Send World matrix to the effect
// Modify the view matrix to render the skybox from its center
// Then compute WorldView and send it to the effect
//--------------------------------------------------------------------------------------
void SkyBox::SetupEffectMatrixes()
{
	// Update the world, proj and worldviewproj matrixes in the effect
	m_pEffect->SetMatrix(m_hMatProj, &(m_pTransMatMgr->m_matProj));
	
	// Prepare the view matrix...
	// Centering the view matrix to 0 (the center of the skybox)
	D3DXMATRIXA16 mTemp = m_pTransMatMgr->m_matView;
	mTemp._41 = mTemp._42 = mTemp._43 = 0.0f;

	// Scaling "a bit" the view (completly arbitrary but permit to avoid skybox clipping)
	D3DXMatrixScaling(&(m_pTransMatMgr->m_matCurrentFrameSkyboxWV), 100.0f, 100.0f, 100.0f);

	// Combining the result
	m_pTransMatMgr->m_matCurrentFrameSkyboxWV *= mTemp;

	// Setup to the effect
	m_pEffect->SetMatrix(m_hMatCurrentWV, &(m_pTransMatMgr->m_matCurrentFrameSkyboxWV));
	m_pEffect->SetMatrix(m_hMatPreviousWV, &(m_pTransMatMgr->m_matPreviousFrameSkyboxWV));
}


//--------------------------------------------------------------------------------------
// Render the skybox
//--------------------------------------------------------------------------------------
HRESULT SkyBox::RenderSkyBox()
{
	HRESULT hr;

	// Setup te skybow matrixes
	SetupEffectMatrixes();
	
	// Quick pointers validity checking 
	if(!m_pSkyBoxMesh || !m_pEffect) return D3DERR_NOTAVAILABLE;

	// Effect pass init
	UINT iPass, cPasses;
	V_RETURN(m_pEffect->Begin(&cPasses, 0));

	for(iPass = 0; iPass < cPasses; ++iPass)
	{
		// Begin the effet pass
		V_RETURN(m_pEffect->BeginPass(iPass));

		// For every mesh material
		for(unsigned int i=0; i<m_dwNumMaterials; ++i)
		{
			// Setup textures
			V_RETURN(m_pEffect->SetTexture(m_hSkyBoxCubeTexture, m_pSkyBoxCubeTexture));

			// Commit the effect parameters changes
			V_RETURN(m_pEffect->CommitChanges());

			// Draw the mesh subset
			V_RETURN(m_pSkyBoxMesh->DrawSubset(i));
		}

		// End the effect pass
		V_RETURN(m_pEffect->EndPass());
	}
	
	// End the effect
	return (m_pEffect->End());
}


//--------------------------------------------------------------------------------------
// OnResetDevice
//--------------------------------------------------------------------------------------
HRESULT SkyBox::OnResetDevice()
{
	if(!m_pEffect) return D3DERR_INVALIDCALL;
	return (m_pEffect->OnResetDevice());
}


//--------------------------------------------------------------------------------------
// OnLostDevice
//--------------------------------------------------------------------------------------
HRESULT SkyBox::OnLostDevice()
{
	if(!m_pEffect) return D3DERR_INVALIDCALL;
	return (m_pEffect->OnLostDevice());
}