#include "Compositor.h"
#include "D3DApp.h"
#include "DeviceStateManager.h"
#include "RenderTarget.h"
#include "BufferManager.h"
#include "DXViewPort.h"

CompositorEffect::CompositorEffect(DeviceStateManager* const device, DXViewPort* const vp)
{
	m_pDevice = device;
	m_pViewPort = vp;
	//m_InputFormat = inputFormat;
	m_Enabled = false;
	m_ListElement = NULL;
	m_Compositor = NULL;
	//m_InputWidth = inputWidth;
	//m_InputHeight = inputHeight;
	m_Input = NULL;
}

CompositorEffect::~CompositorEffect(void)
{
	RemoveFromCompositorChain();
	SAFERELEASE(m_Input);
}

void CompositorEffect::CreateInput(const FORMAT fmt, const float vpscale)
{
	SAFERELEASE(m_Input);
	m_Input = m_pViewPort->CreateViewPortRenderTexture(vpscale, fmt);
}

void CompositorEffect::CreateInput(const FORMAT fmt, const unsigned width, const unsigned height)
{
	SAFERELEASE(m_Input);
	m_Input = m_pDevice->GetTextureManager()->CreateRenderTexture(width, height, fmt);
}

/*void CompositorEffect::DoRestore(void)
{
	if(!m_Input) //just for sure
	{
		m_Input = m_Device->GetTextureManager()->CreateRenderTexture(m_InputWidth ? m_InputWidth : m_ViewPort->Width, m_InputHeight ? m_InputHeight : m_ViewPort->Height, m_InputFormat);
	}
}

void CompositorEffect::DoInvalidate(void)
{
	SAFERELEASE(m_Input);
}*/


/*void CompositorEffect::OnViewportChanged(void)
{
	if (!m_InputWidth && m_Input->GetXL() != m_ViewPort->Width || !m_InputHeight && m_Input->GetYL() != m_ViewPort->Height)
	{
		DoInvalidate();
		DoRestore();
	}
}*/

void CompositorEffect::AddToCompositorChain(CompositorChain* cmp)
{
	RemoveFromCompositorChain();

	m_Compositor = cmp;
	if(cmp)
	{
		m_ListElement = new ListElement<CompositorEffect>(this, (DWORD)this);
		cmp->m_EffectChain.AddTail(m_ListElement);
	}
}

void CompositorEffect::RemoveFromCompositorChain(void)
{
	if(m_ListElement)
	{
		m_ListElement->Unlink();
		delete m_ListElement;
		m_ListElement = NULL;
	}
}

CompositorChain::CompositorChain(DeviceStateManager* const device)
{
	m_Device = device;
	m_First = NULL;
	m_RenderTime = 0;

	D3DVERTEXELEMENT9 declFullScreen[] =
    {
        { 0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITIONT, 0 },  
		{ 0, 16, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, 
        D3DDECL_END()
    };
	m_Device->GetDevice()->CreateVertexDeclaration(declFullScreen, &m_VDeclFSQuad);

	m_VB = m_Device->GetBufferManager()->CreateDynamicVB(128);
}

CompositorChain::~CompositorChain(void)
{
	FOREACH(el, m_EffectChain, CompositorEffect)
	{
		el->GetInfo()->RemoveFromCompositorChain();
	}
	//m_EffectChain.Release(); //there should not be any entries
	SAFERELEASE(m_VDeclFSQuad);
	SAFERELEASE(m_VB);
}

/*void CompositorChain::DoRestore(void)
{
	FOREACH(el, m_EffectChain, CompositorEffect)
	{
		el->GetInfo()->Restore();
	}
}

void CompositorChain::DoInvalidate(void)
{
	FOREACH(el, m_EffectChain, CompositorEffect)
	{
		el->GetInfo()->Invalidate();
	}
}*/

void CompositorChain::SetupRenderScene(void)
{
	m_First = NULL;

	//find fist effect and set its input as rendertarget for the scene
	FOREACH(el, m_EffectChain, CompositorEffect)
	{
		if (el->GetInfo()->IsEnabled() && el->GetInfo()->GetInput()) //must be enabled and has input rendertarget
		{
			m_First = el;
			break;
		}
	}

	//if no effect is enabled, there is nothing todo
	if (m_First)
	{
		CompositorEffect* comeff = m_First->GetInfo();

//		comeff->OnViewportChanged();

		if(!comeff->GetInput()->GetDepthStencil()) //if no depthstencil, create on
			comeff->GetInput()->CreateDepthStencil(FMT_D24S8);

		comeff->GetInput()->StartRender(0);
	}
}

void CompositorChain::RenderEffects(void)
{
	m_RenderTime = 0;

	if(m_First)
	{
		m_Device->GetApp()->GetClock().StartCapture(9991);

		ListElement<CompositorEffect>* current = m_First;

		//render all effect
		while(current)
		{
			current->GetInfo()->GetInput()->EndRender();

			current->GetInfo()->Process();

			//find next pass to setup target pass
			ListElement<CompositorEffect>* next = current->GetNext();
			while(next)
			{
				if(next->GetInfo()->IsEnabled() && next->GetInfo()->GetInput()) //must be enabled and has input rendertarget
				{
					break;
				}
				next = next->GetNext();
			}

			//setup next target
			if(next)
			{
				next->GetInfo()->GetInput()->StartRender(0); 
			}

			//if there is no further effect, the restored rendertarget will be the backbuffer

			//render output of current effect
			current->GetInfo()->RenderTargetPass();

			current = next;
		}

		m_First = NULL; //first effect will be defined in next SetupRenderScene

		m_Device->GetApp()->GetClock().EndCapture(9991);
		LONGLONG rt = m_Device->GetApp()->GetClock().GetCaptureTime(9991);

		m_RenderTime = rt / (double)m_Device->GetApp()->GetClock().GetTicksPerSec();
	}
}

void CompositorChain::RenderQuad(const unsigned xl, const unsigned yl, ShaderEffect* const fx)
{
	float* buffer = (float*)m_VB->LockVB(sizeof(float)*6 * 4);

	//x,y,z,w,tu,tv
	*buffer++ = 0-0.5f; *buffer++ = 0-0.5f; *buffer++ = 0; *buffer++ = 1; *buffer++ = 0; *buffer++ = 0; 
	*buffer++ = xl-0.5f; *buffer++ = 0-0.5f; *buffer++ = 0; *buffer++ = 1; *buffer++ = 1; *buffer++ = 0;
	*buffer++ = 0-0.5f; *buffer++ = yl-0.5f; *buffer++ = 0; *buffer++ = 1; *buffer++ = 0; *buffer++ = 1;
	*buffer++ = xl-0.5f; *buffer++ = yl-0.5f; *buffer++ = 0; *buffer++ = 1; *buffer++ = 1; *buffer++ = 1;

	m_VB->UnlockVB();
	m_VB->SetVBStream(sizeof(float)*6, 0);

	m_Device->SetVertexDeclaration(m_VDeclFSQuad);

	RENDERFX(fx, 
		m_Device->DrawPrimitive(PT_TRIANGLESTRIP, 0, 2);
		);

}