#include "RenderTarget.h"
#include "TextureManager.h"
#include "DeviceStateManager.h"
#include "DXViewPort.h"

RenderSurface::RenderSurface(TextureManager* const manager, const FORMAT fmt) : m_Manager(manager)
{
	m_Format = fmt;
//	m_InRenderSequence = false;
}

void RenderSurface::Release(void)
{
	m_Manager->Release(this);
}

DepthStencilBuffer::DepthStencilBuffer(TextureManager* const manager, const int xl, const int yl, const FORMAT fmt) : RenderSurface(manager, fmt)
{
	m_Surface = NULL;
	m_Restore = NULL;
	m_XL = xl;
	m_YL = yl;

	Restore();
}

void DepthStencilBuffer::StartRender(void)
{
	m_Manager->GetDevice()->GetDevice()->GetDepthStencilSurface(&m_Restore);

	m_Manager->GetDevice()->GetDevice()->SetDepthStencilSurface(m_Surface);
}

void DepthStencilBuffer::EndRender(void)
{
	m_Manager->GetDevice()->GetDevice()->SetDepthStencilSurface(m_Restore);

	SAFERELEASE(m_Restore);
}

void DepthStencilBuffer::DoRestore(void)
{
	HRESULT hr;
	if(FAILED(hr = m_Manager->GetDevice()->GetDevice()->CreateDepthStencilSurface(m_XL, m_YL, (D3DFORMAT)m_Format, D3DMULTISAMPLE_NONE, 0, false, &m_Surface, NULL)))
	{
		wlog("CreateDepthStencilSurface failed: %r",hr);
	}
}

DepthStencilTexture::DepthStencilTexture(TextureManager* const manager, const int xl, const int yl, const FORMAT fmt) : RenderSurface(manager, fmt)
{
	m_Surface = m_Manager->CreateTexture(xl, yl, 1, D3DUSAGE_DEPTHSTENCIL, fmt, POOL_DEFAULT);
}

void DepthStencilTexture::StartRender(void)
{
	m_Manager->GetDevice()->GetDevice()->GetDepthStencilSurface(&m_Restore);

	LPDIRECT3DSURFACE9 surf;
	m_Surface->GetTextureSurface()->GetSurfaceLevel(0, &surf);
	m_Manager->GetDevice()->GetDevice()->SetDepthStencilSurface(surf);
	surf->Release();
}

void DepthStencilTexture::EndRender(void)
{
	m_Manager->GetDevice()->GetDevice()->SetDepthStencilSurface(m_Restore);

	SAFERELEASE(m_Restore);
}

RenderTarget::RenderTarget(TextureManager* const manager, const FORMAT fmt) : RenderSurface(manager, fmt)
{
	m_DepthStencil = NULL;
	//m_iRTRestore = 0;
	//m_Restore = NULL;

	//m_Surface = m_Manager->CreateTexture(xl, yl, 1, D3DUSAGE_RENDERTARGET, fmt, POOL_DEFAULT);
}

void RenderTarget::Release(void)
{
	//SAFERELEASE(m_DepthStencil);
	m_Manager->Release(this);
}


RenderTexture::RenderTexture(TextureManager* const manager, const unsigned xl, const unsigned yl, const FORMAT fmt) : RenderTarget(manager, fmt)
{
	m_iRTRestore = 0;
	m_XL = xl;
	m_YL = yl;
	m_Restore = NULL;
	m_DSFormat = FMT_UNKNOWN;

	m_Surface = m_Manager->CreateTexture(xl, yl, 1, D3DUSAGE_RENDERTARGET, fmt, POOL_DEFAULT);
	m_bInvalidated = false;
	//Restore();
}

void RenderTexture::CreateDepthStencil(const FORMAT fmt)
{
	SAFERELEASE(m_DepthStencil);
	m_DepthStencil = m_Manager->CreateDepthStencilBuffer(m_XL, m_YL, fmt);
	m_DSFormat = fmt;
}

void RenderTexture::StartRender(const unsigned iRT)
{
	if(!m_Surface || !((DXTexture*)m_Surface)->GetTextureSurface())
		return;

	m_iRTRestore = iRT;

	m_Manager->GetDevice()->GetDevice()->GetRenderTarget(iRT, &m_Restore);

	LPDIRECT3DSURFACE9 surfRT;
	((DXTexture*)m_Surface)->GetTextureSurface()->GetSurfaceLevel(0, &surfRT);
    m_Manager->GetDevice()->GetDevice()->SetRenderTarget(iRT, surfRT);

	if(m_DepthStencil)
		m_DepthStencil->StartRender();

	surfRT->Release();
}

void RenderTexture::EndRender(void)
{
	m_Manager->GetDevice()->GetDevice()->SetRenderTarget(m_iRTRestore, m_Restore);

	SAFERELEASE(m_Restore);

	if(m_DepthStencil)
		m_DepthStencil->EndRender();
}

CubeRenderTexture::CubeRenderTexture(TextureManager* const manager, const unsigned edgelength, const FORMAT fmt) : RenderTarget(manager, fmt)
{
	m_DepthStencil = NULL;
	m_iRTRestore = 0;
	m_EdgeLength = edgelength;
	m_Restore = NULL;

	m_Surface = m_Manager->CreateCubeTexture(edgelength, 1, D3DUSAGE_RENDERTARGET, fmt, POOL_DEFAULT);
	Restore();
}

void CubeRenderTexture::StartRender(const unsigned iRT, const D3DCUBEMAP_FACES face)
{
	m_iRTRestore = iRT;

	m_Manager->GetDevice()->GetDevice()->GetRenderTarget(iRT, &m_Restore);

	LPDIRECT3DSURFACE9 surfRT;
	((DXCubeTexture*)m_Surface)->GetCubeTexture()->GetCubeMapSurface(face, 0, &surfRT);
    m_Manager->GetDevice()->GetDevice()->SetRenderTarget(iRT, surfRT);

	if(m_DepthStencil)
		m_DepthStencil->StartRender();

	surfRT->Release();
}

void CubeRenderTexture::EndRender(void)
{
	m_Manager->GetDevice()->GetDevice()->SetRenderTarget(m_iRTRestore, m_Restore);

	SAFERELEASE(m_Restore);

	if(m_DepthStencil)
		m_DepthStencil->EndRender();
}

void CubeRenderTexture::CreateDepthStencil(const FORMAT fmt)
{
	SAFERELEASE(m_DepthStencil);
	m_DepthStencil = m_Manager->CreateDepthStencilBuffer(m_EdgeLength, m_EdgeLength, fmt);
}

OffscreenRenderTexture::OffscreenRenderTexture(TextureManager* const manager, const unsigned xl, const unsigned yl, const FORMAT format, const bool autolock, const bool autorestore, const bool autoupdate) : RenderSurface(manager, format)
{
	m_AutoLock = autolock;
	m_AutoRestore = autorestore;
	m_AutoUpdate = autoupdate;

	m_XL = xl;
	m_YL = yl;

	m_iRTRestore = 0;
	m_RenderTargetRestore = NULL;

	m_GPUSurface = m_Manager->CreateRenderTexture(xl, yl, format);
	m_OffscreenSurface = m_Manager->CreateTexture(xl, yl, 1, 0, format, POOL_SYSTEMMEM);

	if(m_AutoLock)
		m_OffscreenSurface->Lock();
}

void OffscreenRenderTexture::UpdateGPUSurface(void)
{
	if(m_AutoLock || m_OffscreenSurface->GetLockedData())
		m_OffscreenSurface->Unlock();

	LPDIRECT3DSURFACE9 dest, src;
	((DXTexture*)(m_GPUSurface->GetSurface()))->GetTextureSurface()->GetSurfaceLevel(0, &dest);
	m_OffscreenSurface->GetTextureSurface()->GetSurfaceLevel(0, &src);

	m_Manager->GetDevice()->GetDevice()->UpdateSurface(src, NULL, dest, NULL);

	src->Release();
	dest->Release();
	
	if(m_AutoLock)
		m_OffscreenSurface->Lock();
}

void OffscreenRenderTexture::UpdateOffscreenSurface(void)
{
	if(m_AutoLock || m_OffscreenSurface->GetLockedData())
		m_OffscreenSurface->Unlock();

	LPDIRECT3DSURFACE9 dest, src;
	((DXTexture*)(m_GPUSurface->GetSurface()))->GetTextureSurface()->GetSurfaceLevel(0, &src);
	m_OffscreenSurface->GetTextureSurface()->GetSurfaceLevel(0, &dest);

	m_Manager->GetDevice()->GetDevice()->GetRenderTargetData(src, dest);

	src->Release();
	dest->Release(); 

	if(m_AutoLock)
		m_OffscreenSurface->Lock();
}

void OffscreenRenderTexture::StartRender(const unsigned iRT)
{
	m_GPUSurface->StartRender(iRT);
}

void OffscreenRenderTexture::EndRender(void)
{
	m_GPUSurface->EndRender();

	if (m_AutoUpdate)
		UpdateOffscreenSurface();
}

void OffscreenRenderTexture::Release(void)
{
	SAFERELEASE(m_GPUSurface); 
	m_Manager->Release(this);
}

ViewPortRenderTexture::ViewPortRenderTexture(TextureManager* const manager, DXViewPort* const vp, const FORMAT fmt, const float scale) : RenderTexture(manager, vp->GetWidth() * scale, vp->GetHeight() * scale, fmt)
{
	m_pVP = vp;
	m_Scale = scale;

//	m_iRTRestore = 0;
	//m_Restore = NULL;
	//m_DSFormat = FMT_UNKNOWN;

	//Restore();
	//m_Surface = m_Manager->CreateTexture(m_VP->Width * m_Scale, m_VP->Height * m_Scale, 1, D3DUSAGE_RENDERTARGET, fmt, POOL_DEFAULT);
}

ViewPortRenderTexture::~ViewPortRenderTexture(void)
{
	m_pVP->ReleaseRTRef(this);
}

void ViewPortRenderTexture::DoInvalidate(void)
{
	SAFERELEASE(m_Surface);
	SAFERELEASE(m_DepthStencil);
}

void ViewPortRenderTexture::DoRestore(void)
{
	m_XL = m_pVP->GetWidth() * m_Scale;
	m_YL = m_pVP->GetHeight() * m_Scale;
	
	m_Surface = m_Manager->CreateTexture(m_XL, m_YL, 1, D3DUSAGE_RENDERTARGET, m_Format, POOL_DEFAULT);

	if (m_DSFormat != FMT_UNKNOWN)
	{
		m_DepthStencil = m_Manager->CreateDepthStencilBuffer(m_XL, m_YL, m_DSFormat);
	}
}

/*void ViewPortRenderTexture::CreateDepthStencil(const FORMAT fmt)
{
	m_DSFormat = fmt;
	SAFERELEASE(m_DepthStencil);
	m_DepthStencil = m_Manager->CreateDepthStencilBuffer(m_pVP->GetWidth() * m_Scale, m_pVP->GetHeight() * m_Scale, fmt);
}*/
/*
void ViewPortRenderTexture::StartRender(const unsigned iRT)
{
	//ceck if size still matches viewport
	//if((int)(m_VP->Width * m_Scale) != ((DXTexture*)m_Surface)->GetXl() || (int)(m_VP->Height * m_Scale) != ((DXTexture*)m_Surface)->GetYl())
	//{
	//	DoInvalidate();
	//	DoRestore();
	//}

	m_iRTRestore = iRT;

	m_Manager->GetDevice()->GetDevice()->GetRenderTarget(iRT, &m_Restore);

	LPDIRECT3DSURFACE9 surfRT;
	((DXTexture*)m_Surface)->GetTextureSurface()->GetSurfaceLevel(0, &surfRT);
    m_Manager->GetDevice()->GetDevice()->SetRenderTarget(iRT, surfRT);

	if(m_DepthStencil)
		m_DepthStencil->StartRender();

	surfRT->Release();
}

void ViewPortRenderTexture::EndRender(void)
{
	m_Manager->GetDevice()->GetDevice()->SetRenderTarget(m_iRTRestore, m_Restore);

	SAFERELEASE(m_Restore);

	if(m_DepthStencil)
		m_DepthStencil->EndRender();
}*/