#include "MeshObject.h"
#include "ShadowApp.h"

#include "..\DXFunctions\ShaderManager.h"
#include "..\DXFunctions\DeviceStateManager.h"
#include "ShadowScene.h"

MeshObject::MeshObject(ShadowScene* const pScene, CMesh* const mesh, const DXMatrix& pos) : SceneObject(pScene)
{
	//m_App = app;
	//m_matWorld = pos;

	m_SubTransformation = pos;
	m_CombinedTransformation = pos; //always global object

	m_Mesh = mesh;
//	m_bVisible = false;
	m_FX = m_pScene->GetShadowApp()->GetDevice()->GetShaderManager()->CreateFX("Shaders\\Materials.fx");

	m_Bound = m_Mesh->GetBound();
	D3DXVec3TransformCoord((D3DXVECTOR3*)&m_Bound.Center, (D3DXVECTOR3*)&m_Bound.Center, (D3DXMATRIX*)&m_CombinedTransformation);
	m_Bound.Radius *= _max(_max(m_CombinedTransformation.GetXAxis().Length(), m_CombinedTransformation.GetYAxis().Length()), m_CombinedTransformation.GetZAxis().Length());

	BuildRenderJobs();	
}

MeshObject::~MeshObject(void)
{
	if(m_FX)
		m_FX->Release();

	m_RenderJobs.Clear();
	m_ShadowRenderJobs.Clear();
	m_RenderJobsDS.Clear();

	m_SP.Clear();
	m_TP.Clear();
}

void MeshObject::AddToRenderList(RenderList& rl)
{
	if(m_pScene->GetShadowApp()->UseDeferred())
	{
		FOREACH(job, m_RenderJobsDS, RenderJob)
			rl.AddRJ(job->GetInfo());
	}
	else
	{
		FOREACH(job, m_RenderJobs, RenderJob)
			rl.AddRJ(job->GetInfo());
	}
}

void MeshObject::AddToShadowRenderList(RenderList& rl)
{
	FOREACH(job, m_ShadowRenderJobs, RenderJob)
	{
		rl.AddRJ(job->GetInfo());
	}
}

/*void Object::Render(const bool depthmap)
{
	if(m_Mesh && m_Mesh->GetMesh() && m_FX && m_FX->IsCompiled())
	{
		if(depthmap)
		{
			m_FX->SetMatrix("SM_matWorld", m_matWorld);
			m_FX->SetMatrix("SM_matSM", m_App->GetShadowMapping()->GetSMMatrix());

			m_FX->SetTechnique("RenderShadowDepth");

			RENDERFX(m_FX,
				for( DWORD i=0; i<m_Mesh->GetMaterialCount(); i++ )
				{
					m_Mesh->GetMesh()->DrawSubset(i);
				}
			);
		}
		else
		{
			m_FX->GetShader()->SetValue("matViewProj", (float*)&m_App->GetActiveCam()->GetViewProjMatrix(), sizeof(DXMatrix));
			//m_FX->SetMatrix("matViewProj", m_App->GetActiveCam()->GetViewProjMatrix());

			if(!m_App->UseDeferred())
			{
				m_FX->SetVector("LightColor", static_cast<DXVector4>(m_App->GetSunColor()));
				m_FX->SetVector("AmbientColor", static_cast<DXVector4>(m_App->GetAmbientColor()));
				m_FX->SetFloatArray("LightDir", (float*)&m_App->GetSunDir(), 3);

				//m_FX->SetMatrix("SM_matSMTex", m_App->GetShadowMapping()->GetSMTexMatrix());  
				m_FX->SetMatrix("SM_matSM", m_App->GetShadowMapping()->GetSMMatrix()); 
				m_FX->SetTexture("SM_ShadowMap",m_App->GetShadowMapping()->GetShadowMap()->GetSurface());  
				m_FX->SetFloat("SM_ShadowMapSize", m_App->GetShadowMapping()->GetShadowMapSize());  
				m_FX->SetFloat("SM_rcpShadowMapSize", 1.f/m_App->GetShadowMapping()->GetShadowMapSize()); 
			}

			m_FX->SetMatrix("matWorld", m_matWorld);

			m_FX->SetInt("TexFilter", D3DTEXF_LINEAR);
			m_FX->SetInt("TexAnisotropy", 1);
		
			for( DWORD i=0; i<m_Mesh->GetMaterialCount(); i++ )
			{
				if(m_Mesh->GetMaterials()[i].GetTexture() && m_Mesh->GetMaterials()[i].GetTexture()->GetSurface())
				{
					if(m_App->UseDeferred())
						m_FX->SetTechnique("RenderTexturedDS");
					else
						m_FX->SetTechnique("RenderTextured");
					
					m_FX->SetTexture("Texture", m_Mesh->GetMaterials()[i].GetTexture());
				}
				else
				{
					if(m_App->UseDeferred())
						m_FX->SetTechnique("RenderDS");
					else
						m_FX->SetTechnique("Render");
				}
				
				//m_FX->SetFloatArray("Ambient", (float*)&m_Mesh->GetMaterials()[i].GetAmbient(), 4);
				m_FX->SetFloatArray("Diffuse", (float*)&m_Mesh->GetMaterials()[i].GetDiffuse(), 4);
			
				RENDERFX(m_FX,

					//m_Mesh->GetMesh()->DrawSubset(i);
					m_Mesh->DrawSubset(m_App->GetDevice(), i);
				);
			}
		}
	}
}*/
/*
void Object::Update(const float dt)
{
	m_bVisible = false;

	int bx = m_matWorld._41 / (m_App->GetTerrain()->GetPatchStride()-1) / m_App->GetTerrain()->GetTileScale();
	int by = m_matWorld._42 / (m_App->GetTerrain()->GetPatchStride()-1) / m_App->GetTerrain()->GetTileScale();

	//AABB aabb = m_Mesh->GetBound().BuildAABB();
	AABB aabb = m_Bound.BuildAABB();
	//aabb.AddOffset(DXVector3(m_matWorld._41, m_matWorld._42, m_matWorld._43));
	m_App->ExpandSceneBound(aabb);
		

	//for(unsigned i=0; i < m_App->GetNumAntiLightPlanes(); i++)
	//{
	//	if(m_Bound.IsCulled(m_App->GetAntiLightPlanes()[i]))
	//		return;
	//}

	QuadtreeNode* n = m_App->GetTerrain()->GetQT()->GetLeaf(m_App->GetTerrain()->GetQT()->GetLevels()-1, bx, by);
	if(n && n->visible != QTVT_NONE)
	//if(Intr_AABB_OBS(m_App->GetVisibleBound(), m_Bound))
	//if(Intr_OBB_OBS(m_App->GetShadowFrustrum(), m_Bound))
	{
		m_App->ExpandVisibleBound(aabb);

		//update renderjobs
		//only needed when object has moved
		//m_MatWorld.UpdateValue(&m_matWorld);
		//m_SMMatWorld.UpdateValue(&m_matWorld);

		//add to renderlists
		if(m_App->UseDeferred())
		{
			FOREACH(job, m_RenderJobsDS, RenderJob)
				m_App->GetRenderList()->AddRJ(job->GetInfo());
		}
		else
		{
			FOREACH(job, m_RenderJobs, RenderJob)
				m_App->GetRenderList()->AddRJ(job->GetInfo());
		}

		m_bVisible = true;
	}
	else
	{
		if (m_App->GetUseDebugCam())
		{
			for(unsigned i=0; i < m_App->GetNumAntiLightPlanes(); i++)
			{
				if(m_Bound.IsCulled(m_App->GetAntiLightPlanes()[i]))
					return;
			}

			if(m_App->UseDeferred())
			{
				FOREACH(job, m_RenderJobsDS, RenderJob)
					m_App->GetRenderList()->AddRJ(job->GetInfo());
			}
			else
			{
				FOREACH(job, m_RenderJobs, RenderJob)
					m_App->GetRenderList()->AddRJ(job->GetInfo());
			}
		}
	}
}

void Object::UpdateShadow(void)
{
	//visible objects and objects in shadow frustrum can cast shadows, so add the to the shadow renderlist
	if(m_bVisible)
	{
		FOREACH(job, m_ShadowRenderJobs, RenderJob)
		{
			m_App->GetShadowRenderList()->AddRJ(job->GetInfo());
		}
	}
	else
	{
		for(unsigned i=0; i < m_App->GetNumAntiLightPlanes(); i++)
		{
			if(m_Bound.IsCulled(m_App->GetAntiLightPlanes()[i]))
				return;
		}

		//if(Intr_OBB_OBS(m_App->GetShadowFrustum(), m_Bound))
		{
			FOREACH(job, m_ShadowRenderJobs, RenderJob)
			{
				m_App->GetShadowRenderList()->AddRJ(job->GetInfo());
			}
		}
	}
}*/

void MeshObject::BuildRenderJobs(void)
{
	m_MatWorld.SetValuePtr("matWorld", &m_CombinedTransformation, sizeof(DXMatrix));
	m_SMMatWorld.SetValuePtr("SM_matWorld", &m_CombinedTransformation, sizeof(DXMatrix));
	float id = frand();
	m_SMID.SetValue("SM_ID", &id, sizeof(float));

	for( DWORD i=0; i<m_Mesh->GetMaterialCount(); i++ )
	{
		//render final
		RenderJob* job = new RenderJob();

		job->Effect = m_FX;

		job->AddParameter(&m_MatWorld);
		job->AddParameter(m_pScene->GetShadowApp()->GetShadowMapping()->GetParamSMMatrix());
		job->AddParameter(m_pScene->GetShadowApp()->GetShadowMapping()->GetParamSMSize());
		job->AddTexture(m_pScene->GetShadowApp()->GetShadowMapping()->GetTexParamSM());
		job->AddTexture(m_pScene->GetShadowApp()->GetShadowMapping()->GetTexParamID());
		job->AddParameter(&m_SMID);

		TEXTUREPARAMETER* tp = NULL; 
		if(m_Mesh->GetMaterials()[i].GetTexture() || m_Mesh->GetMaterials()[i].GetTextureFilename().getlen() > 0 && m_Mesh->GetMaterials()[i].LoadTexture(m_pScene->GetShadowApp()->GetDevice()))
		{
			tp = new TEXTUREPARAMETER("Texture", m_Mesh->GetMaterials()[i].GetTexture());
			job->AddTexture(tp);
			m_TP.AddTail(tp, 0);

		//	job->Technique = "RenderTextured";
		}
		else
		{
		//	job->Technique = "Render";
		}

		SHADERPARAMETER* shpDiff = new SHADERPARAMETER("Diffuse", &m_Mesh->GetMaterials()[i].GetDiffuse(), sizeof(DXColor), false);
		job->AddParameter(shpDiff);
		
		job->DrawCall = m_Mesh->GetDrawCalls() + i;

		job->UserData = NULL;

		m_RenderJobs.AddTail(job, 0);

		//render shadow map
		job = new RenderJob();

		job->Effect = m_FX;

		job->AddParameter(m_pScene->GetShadowApp()->GetShadowMapping()->GetParamSMMatrix());
		job->AddParameter(&m_SMMatWorld);
		job->AddParameter(&m_SMID);

		//job->Technique = "RenderShadowDepth";

		job->DrawCall = m_Mesh->GetDrawCalls() + i;

		job->UserData = &m_Bound;

		m_ShadowRenderJobs.AddTail(job, 0);

		//render ds
		job = new RenderJob();

		job->Effect = m_FX;

		job->AddParameter(&m_MatWorld);
		job->AddParameter(&m_SMID);

		if(m_Mesh->GetMaterials()[i].GetTexture() && m_Mesh->GetMaterials()[i].GetTexture()->GetSurface())
		{
			job->AddTexture(tp);
		//	job->Technique = "RenderTexturedDS";

		}
		else
		{
		//	job->Technique = "RenderDS";
		}
		
		job->AddParameter(shpDiff);

		job->DrawCall = m_Mesh->GetDrawCalls() + i;

		m_SP.AddTail(shpDiff, 0);

		job->UserData = NULL;

		m_RenderJobsDS.AddTail(job, 0);
	}

	UpdateTechnique();
}

void MeshObject::UpdateTechnique(void)
{
	FOREACH(rj, m_RenderJobs, RenderJob)
	{
		if (rj->GetInfo()->GetNumTextures() > 2) // obj is textured (3 = shadowmap + idbuffer + texture)
		{
			if (m_pScene->GetShadowApp()->GetSMFiltering())
				rj->GetInfo()->Technique = "RenderTextured_Filter";
			else
				rj->GetInfo()->Technique = m_pScene->GetShadowApp()->GetUseIDBuffer() ? "RenderTextured_ID" : "RenderTextured";
		}
		else
		{
			if (m_pScene->GetShadowApp()->GetSMFiltering())
				rj->GetInfo()->Technique = "Render_Filter";
			else
				rj->GetInfo()->Technique = m_pScene->GetShadowApp()->GetUseIDBuffer() ? "Render_ID" : "Render";
		}
	}

	FOREACH(rj, m_RenderJobsDS, RenderJob)
	{
		if (m_pScene->GetShadowApp()->GetWorldPosType() == WPT_DEPTH)
		{
			if (rj->GetInfo()->GetNumTextures() > 0) // obj is textured
			{
				rj->GetInfo()->Technique = m_pScene->GetShadowApp()->GetUseIDBuffer() ? "RenderDS_Textured_Depth_ID" : "RenderDS_Textured_Depth";
			}
			else
			{
				rj->GetInfo()->Technique = m_pScene->GetShadowApp()->GetUseIDBuffer() ? "RenderDS_Depth_ID" : "RenderDS_Depth_ID";
			}
		}
		else if (m_pScene->GetShadowApp()->GetWorldPosType() == WPT_LINEARDEPTH)
		{
			if (rj->GetInfo()->GetNumTextures() > 0) // obj is textured
			{
				rj->GetInfo()->Technique = m_pScene->GetShadowApp()->GetUseIDBuffer() ? "RenderDS_Textured_LinearDepth_ID" : "RenderDS_Textured_LinearDepth";
			}
			else
			{
				rj->GetInfo()->Technique = m_pScene->GetShadowApp()->GetUseIDBuffer() ? "RenderDS_LinearDepth_ID" : "RenderDS_LinearDepth_ID";
			}
		}
		else if (m_pScene->GetShadowApp()->GetWorldPosType() == WPT_WORLDPOS)
		{
			if (rj->GetInfo()->GetNumTextures() > 0) // obj is textured
			{
				rj->GetInfo()->Technique = m_pScene->GetShadowApp()->GetUseIDBuffer() ? "RenderDS_Textured_World_ID" : "RenderDS_Textured_World";
			}
			else
			{
				rj->GetInfo()->Technique = m_pScene->GetShadowApp()->GetUseIDBuffer() ? "RenderDS_World_ID" : "RenderDS_World_ID";
			}
		}
	}

	FOREACH(rj, m_ShadowRenderJobs, RenderJob)
	{
		if (m_pScene->GetShadowApp()->GetUseIDBuffer())
		{
			if (m_pScene->GetShadowApp()->GetUsePrimID() && m_pScene->GetShadowApp()->GetShadowMethod() != SM_COMMON)
			{
				rj->GetInfo()->Technique = "RenderShadowDepth_PrimID";
			}
			else
			{
				rj->GetInfo()->Technique = "RenderShadowDepth_ID";
			}
		}
		else
		{
			rj->GetInfo()->Technique = "RenderShadowDepth";
		}
	}
}