#include "stdafx.h"
#include "ShadowMapRenderer.h"
#include "stdafx.h"
#include "RenderTarget.h"
#include "ShadowMapMaterial.h"
#include "../../../Graphics/MeshFilter.h"
#include "OverlordGame.h"
#include "../../../Content/ContentManager.h"
#include "../../../Scenegraph/GameObject.h"
#include "../../../Scenegraph/GameScene.h"
#include "../../../Scenegraph/SceneManager.h"
#include "../../../Components/ModelComponent.h"
#include "ModelAnimator.h"
#include "../../../Components/TransformComponent.h"

ShadowMapRenderer::ShadowMapRenderer()
	:m_Size(850.0f)
{
}

ShadowMapRenderer::~ShadowMapRenderer()
{
	SafeDelete(m_pShadowRT);
	SafeDelete(m_pShadowMat);
}

void ShadowMapRenderer::Initialize(const GameContext & gameContext)
{
	//Create shadow Render Target
	m_pShadowRT = new RenderTarget(gameContext.pDevice);
	auto windowSettings = OverlordGame::GetGameSettings().Window;

	RENDERTARGET_DESC desc;
	//desc.pDepth = true;
	desc.EnableDepthBuffer = true;
	desc.EnableDepthSRV = true;
	desc.EnableColorBuffer = false;
	desc.Width = windowSettings.Width;
	desc.Height = windowSettings.Height;

	m_pShadowRT->Create(desc);

	//Create ShadowMaterial
	m_pShadowMat = new ShadowMapMaterial();
	m_pShadowMat->Initialize(gameContext);

	m_IsInitialized = true;
}

void ShadowMapRenderer::SetLight(XMFLOAT3 position, XMFLOAT3 direction)
{
	m_LightPosition = position;
	m_LightDirection = direction;

	auto windowSettings = OverlordGame::GetGameSettings().Window;
	float viewWidth = (m_Size>0) ? m_Size * windowSettings.AspectRatio : windowSettings.Width;
	float viewHeight = (m_Size>0) ? m_Size : windowSettings.Height;
	auto projection = XMMatrixOrthographicLH(viewWidth, viewHeight,10.0f, 2500.0f);

	XMVECTOR upVec = { 0,1,0 };
	XMVECTOR positionVec = XMLoadFloat3(&m_LightPosition);
	XMVECTOR directionVec = XMLoadFloat3(&m_LightDirection);

	auto view = XMMatrixLookAtLH(positionVec, positionVec + directionVec, upVec);

	XMMATRIX result = view * projection;
	XMStoreFloat4x4(&m_LightVP, result);
}

void ShadowMapRenderer::SetLightPosition(XMFLOAT3 position)
{
	SetLight(position, m_LightDirection);
}

void ShadowMapRenderer::SetLightDirection(XMFLOAT3 direction)
{
	SetLight(m_LightPosition, direction);
}

void ShadowMapRenderer::Begin(const GameContext & gameContext)
{
	m_pOldRT = SceneManager::GetInstance()->GetGame()->GetRenderTarget();

	// Unbind shader resources
	ID3D11ShaderResourceView* nullViews[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
	gameContext.pDeviceContext->PSSetShaderResources(0, 8, nullViews);

	// Unbind render targets
	auto depth = m_pShadowRT->GetDepthStencilView();
	gameContext.pDeviceContext->OMSetRenderTargets(0, NULL, depth);
	gameContext.pDeviceContext->ClearDepthStencilView(m_pShadowRT->GetDepthStencilView(), D3D11_CLEAR_DEPTH,1.0f, 0);
}

void ShadowMapRenderer::Draw(const GameContext & gameContext, ModelComponent * pModelComponent, XMFLOAT4X4 world)
{
	auto pMeshFilter = pModelComponent->GetMeshFilter();
	auto pAnimator = pModelComponent->GetAnimator();

	if (!pMeshFilter->m_ShadowEnabled)
		return;

	//If the model has an animator, set the corresponding booleans in the shadowMaterial
	if (pAnimator)
	{
		m_pShadowMat->SetHasAnimations(true);
		m_pShadowMat->SetBones(pAnimator->GetBoneTransforms());
	}
	else
		m_pShadowMat->SetHasAnimations(false);

	m_pShadowMat->SetLightVP(m_LightVP);
	m_pShadowMat->SetWorldVP(world);

	//Set Inputlayout
	gameContext.pDeviceContext->IASetInputLayout(m_pShadowMat->GetInputLayout());

	//Set Vertex Buffer
	UINT offset = 0;
	auto vertexBufferData = pMeshFilter->GetVertexBufferData(gameContext, m_pShadowMat);
	gameContext.pDeviceContext->IASetVertexBuffers(0, 1, &vertexBufferData.pVertexBuffer, &vertexBufferData.VertexStride, &offset);

	//Set Index Buffer
	gameContext.pDeviceContext->IASetIndexBuffer(pMeshFilter->m_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0);

	//Set Primitive Topology
	gameContext.pDeviceContext->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	//DRAW
	auto tech = m_pShadowMat->GetTechnique();
	D3DX11_TECHNIQUE_DESC techDesc;
	tech->GetDesc(&techDesc);
	for (UINT p = 0; p < techDesc.Passes; ++p)
	{
		tech->GetPassByIndex(p)->Apply(0, gameContext.pDeviceContext);
		gameContext.pDeviceContext->DrawIndexed(pMeshFilter->m_IndexCount, 0, 0);
	}
}

void ShadowMapRenderer::End(const GameContext & gameContext)
{
	// Unbind render targets
	ID3D11RenderTargetView* oldView = m_pOldRT->GetRenderTargetView();
	gameContext.pDeviceContext->OMSetRenderTargets(1, &oldView, m_pOldRT->GetDepthStencilView());
	gameContext.pDeviceContext->ClearRenderTargetView(m_pOldRT->GetRenderTargetView(), reinterpret_cast(&Colors::Black));
	gameContext.pDeviceContext->ClearDepthStencilView(m_pOldRT->GetDepthStencilView(), D3D10_CLEAR_DEPTH, 1.0f, 0);
}

XMFLOAT3 ShadowMapRenderer::GetLightDirection()
{
	return m_LightDirection;
}

XMFLOAT4X4 ShadowMapRenderer::GetLightVP()
{
	return m_LightVP;
}

ShadowMapMaterial * ShadowMapRenderer::GetMaterial()
{
	return m_pShadowMat;
}

ID3D11ShaderResourceView * ShadowMapRenderer::GetShadowMap()
{
	return m_pShadowRT->GetDepthShaderResourceView();
}