#include "Plugin.h"
#include <fstream>
#include <d3dx9.h>
#include <atlbase.h>


k_Plugin::k_Plugin(IServiceLocator& ar_ServiceLocator)
	: k_Direct3D9BasePlugin(ar_ServiceLocator, L"DepthOfField", L"Blurs scene elements who are out of focus.")
	, mr_ShaderSystem_(NULL)
	, mr_ShowTextureShader_(NULL)
	, mr_DefaultStateBlock_(NULL)
	, mr_ScreenMask_(NULL)
	, mr_QuarterMask_(NULL)
	, mr_ZQuarterMask_(NULL)
	, mr_BlurMask_(NULL)
	, mr_ZBlurMask_(NULL)
	, mk_ZScreenMaskBatch_(NULL)
	, mr_BlurToScreenMask_(NULL)
	, mr_ClearMask_(NULL)
	, mb_NeedDepthUpdate(false)
	, mb_NeedToReset(false)
{
	IEnvironment& lr_Environment = object_cast<IEnvironment>(ar_ServiceLocator);
	ms_LogFile = lr_Environment.get_ModulePath()+L"Helan.log";

	::D3DXMatrixLookAtLH(
		&mr_ViewMatrix,
		&D3DXVECTOR3(0.0f, 0.0f, 0.0f),
		&D3DXVECTOR3(0.0f, 0.0f, 1.0f),
		&D3DXVECTOR3(0.0f, 1.0f, 0.0f)
		);
	::D3DXMatrixPerspectiveFovLH(
		&mr_ProjMatrix,
		D3DXToRadian(90),
		1.0,
		1.0f,
		2.0f
		);

	mk_Parameters.push_back(new k_SliderParameter(*this, r_DepthOfField::Depth, L"DepthStart", L"Depth start", L"The depth at which the depth of field effect will start to work.", 0, 1000, 200));
	mk_Parameters.push_back(new k_SliderParameter(*this, r_DepthOfField::DepthStep, L"DepthStep", L"Depth step", L"The size of the transition from focus to out of focus.", 1, 100, 10));
	mk_Parameters.push_back(new k_SliderParameter(*this, r_DepthOfField::SizeFactor, L"SizeFactor", L"Size factor", L"The size of the depth of field texture.", 10, 50, 25));
	mk_Parameters.push_back(new k_SliderParameter(*this, r_DepthOfField::BlurSteps, L"BlurPasses", L"Blur passes", L"The steps the scene is blured.", 0, 20, 5));
	mk_Parameters.push_back(new k_SliderParameter(*this, r_DepthOfField::DofStrength, L"DepthOfField strength", L"Depth of field strength", L"This value specifics the transparency of the blured scene.", 0, 100, 90));
	
	mk_Parameters.push_back(new k_SliderParameter(*this, r_DepthOfField::ZSizeFactor, L"ZSizeFactor", L"ZSize factor", L"The size of the ZBuffer.", 10, 50, 50));
	mk_Parameters.push_back(new k_SliderParameter(*this, r_DepthOfField::ZBlurSteps, L"ZBlurPasses", L"ZBlur passes", L"The steps the ZBuffer is blured.", 0, 20, 3));
	mk_Parameters.push_back(new k_SliderParameter(*this, r_DepthOfField::ZScreenMaskCount, L"ZScreenmaskCount", L"Z screenmask count", L"The density of the ZBuffer tests.", 2, 100, 10));

	mk_Parameters.push_back(new k_CheckBoxParameter(*this, r_DepthOfField::ShowDepthOnly, L"ShowDepthOnly", L"Show depth only", L"Renders only the unblured depth.", false));
}


k_Plugin::~k_Plugin()
{
}


void k_Plugin::Initialize(IDirect3DDevice9* ar_Device_)
{
	PrintLog(ms_LogFile, L"DepthOfField: Initialising...\n");	

	ar_Device_->GetDeviceCaps(&mr_Caps);

	::ZeroMemory((void*)&mr_Viewport, sizeof(mr_Viewport));

	mf_DepthStep = 0.05f;
	mf_Depth = 0.25f;
	mf_SizeFactor = 0.25f;
	mi_BlurSteps = 5;
	mf_DofStrength = 0.8f;
	mb_ShowDepthOnly = false;

	mf_ZSizeFactor = 0.1f;
	mi_ZBlurSteps = 5;

	mi_ZScreenMaskCount = 12;

	PrintLog(ms_LogFile, L"DepthOfField: Initialising finished.\n");

	k_Direct3D9BasePlugin::Initialize(ar_Device_);
}


void k_Plugin::Deinitialize()
{
	PrintLog(ms_LogFile, L"DepthOfField: Deinitialising...\n");
	this->set_Active(false);
	PrintLog(ms_LogFile, L"DepthOfField: Deinitialising finished.\n");

	k_Direct3D9BasePlugin::Deinitialize();
}


void k_Plugin::Render()
{
	if (!this->CanRender())
		return;

	this->UpdateParameters();

	// set basic states
	{
		mr_CurrentStateBlock_->Capture();
		mr_DefaultStateBlock_->Apply();
	}

	// our render stuff
	{
		// reset our textures in case something on our backbuffer has changed
		this->RebuildTextures();
		// update our texture
		this->UpdateTextures();
		// render our stuff
		this->UpdateRendering();
	}

	// set states back to normal
	{
		mr_CurrentStateBlock_->Apply();
	}

	mdw_LastTime = ::GetTickCount();
}


void k_Plugin::OnBeforeReset()
{
	mb_NeedToReset = this->get_Active();
	this->set_Active(false);
}


void k_Plugin::OnAfterReset()
{
	if (mb_NeedToReset)
	{
		this->set_Active(true);
		mb_NeedToReset = false;
	}
}


void k_Plugin::set_Active(bool ab_State)
{
	if (ab_State && !this->get_Active())
	{
		PrintLog(ms_LogFile, L"DepthOfField: Activating...\n");

		//SE: update our textures in any case if we reactivate our plugin
		mb_NeedDepthUpdate = true;
		this->UpdateParameters();

		PrintLog(ms_LogFile, L"DepthOfField: Initialising shader subsystem.\n");
		mr_ShaderSystem_ = new ShaderSystem(mr_Device_);
		mr_ShaderSystem_->AddSemanticHandler(new HelTextureSemanticHandler("BackBuffer", &mr_BackBufferTexture));
		mr_ShaderSystem_->AddSemanticHandler(new HelTextureSemanticHandler("BlurTexture", &mr_BlurTexture));

		mr_ShaderSystem_->AddSemanticHandler(new HelTextureSemanticHandler("ZBackBuffer", &mr_ZBackBufferTexture));
		mr_ShaderSystem_->AddSemanticHandler(new HelTextureSemanticHandler("ZBlurTexture", &mr_ZBlurTexture));

		PrintLog(ms_LogFile, L"DepthOfField: Loading shaders...\n");
		mr_ShowTextureShader_ = this->LoadShader("ShowTexture.hlsl", "Plugins\\DepthOfField");
		mr_ShowTranslucentTextureShader_ = this->LoadShader("ShowTranslucentTexture.hlsl", "Plugins\\DepthOfField");
		mr_ShowBlurTextureShader_ = this->LoadShader("ShowBlurTexture.hlsl", "Plugins\\DepthOfField");
		mr_BlurShader_ = this->LoadShader("Blur.hlsl", "Plugins\\DepthOfField");
		mr_ZBlurShader_ = this->LoadShader("ZBlur.hlsl", "Plugins\\DepthOfField");
		mr_DepthRenderShader_ = this->LoadShader("RenderDepth.hlsl", "Plugins\\DepthOfField");
		mr_BlackRenderShader_ = this->LoadShader("RenderBlack.hlsl", "Plugins\\DepthOfField");
		mr_DepthOfFieldShader_ = this->LoadShader("DepthOfField.hlsl", "Plugins\\DepthOfField");

		mr_ShowZTextureShader_ = this->LoadShader("ShowZTexture.hlsl", "Plugins\\DepthOfField");
		mr_ShowZBlurTextureShader_ = this->LoadShader("ShowZBlurTexture.hlsl", "Plugins\\DepthOfField");

		PrintLog(ms_LogFile, L"DepthOfField: Gather basic states...\n");
		mr_Device_->CreateStateBlock( D3DSBT_ALL, &mr_DefaultStateBlock_ );
		mr_Device_->CreateStateBlock( D3DSBT_ALL, &mr_CurrentStateBlock_ );
		
		mdw_LastTime = ::GetTickCount();

		PrintLog(ms_LogFile, L"DepthOfField: Activating finished.\n");
	}
	else if (!ab_State && this->get_Active())
	{
		PrintLog(ms_LogFile, L"DepthOfField: Deactivating...\n");

		PrintLog(ms_LogFile, L"DepthOfField: Deinitialising mr_ShaderSystem_...\n");
		SAFEDELETE (mr_ShaderSystem_);
		
		PrintLog(ms_LogFile, L"DepthOfField: Deinitialising mr_ScreenMask_...\n");
		SAFEDELETE (mr_ScreenMask_);

		PrintLog(ms_LogFile, L"DepthOfField: Deinitialising mr_ZScreenMask_...\n");
		for(unsigned int i = 0; i < mk_ZScreenMaskList.size(); ++i)
		{
			SAFEDELETE (mk_ZScreenMaskList[i]);
		}
		mk_ZScreenMaskList.clear();
		SAFEDELETE(mk_ZScreenMaskBatch_);

		PrintLog(ms_LogFile, L"DepthOfField: Deinitialising mr_BlurMask_...\n");
		SAFEDELETE (mr_BlurMask_);

		PrintLog(ms_LogFile, L"DepthOfField: Deinitialising mr_ZBlurMask_...\n");
		SAFEDELETE (mr_ZBlurMask_);

		PrintLog(ms_LogFile, L"DepthOfField: Deinitialising mr_QuarterMask_...\n");
		SAFEDELETE (mr_QuarterMask_);

		PrintLog(ms_LogFile, L"DepthOfField: Deinitialising mr_BlurToScreenMask_...\n");
		SAFEDELETE (mr_BlurToScreenMask_);

		PrintLog(ms_LogFile, L"DepthOfField: Deinitialising mr_ZQuarterMask_...\n");
		SAFEDELETE (mr_ZQuarterMask_);	

		PrintLog(ms_LogFile, L"DepthOfField: Deinitialising mr_ClearMask_...\n");
		SAFEDELETE (mr_ClearMask_);

		PrintLog(ms_LogFile, L"DepthOfField: Deinitialising mr_BackBufferTexture...\n");
		if (mr_BackBufferTexture.mr_Texture_)
			mr_BackBufferTexture.mr_Texture_->Release();
		mr_BackBufferTexture.mr_Texture_ = NULL;
		mr_BackBufferTexture = r_Texture();

		PrintLog(ms_LogFile, L"DepthOfField: Deinitialising mr_BlurTexture...\n");
		if (mr_BlurTexture.mr_Texture_)
			mr_BlurTexture.mr_Texture_->Release();
		mr_BlurTexture.mr_Texture_ = NULL;
		mr_BlurTexture = r_Texture();

		PrintLog(ms_LogFile, L"DepthOfField: Deinitialising mr_ZBackBufferTexture...\n");
		if (mr_ZBackBufferTexture.mr_Texture_)
			mr_ZBackBufferTexture.mr_Texture_->Release();
		mr_ZBackBufferTexture.mr_Texture_ = NULL;
		mr_ZBackBufferTexture = r_Texture();

		PrintLog(ms_LogFile, L"DepthOfField: Deinitialising mr_ZBlurTexture...\n");
		if (mr_ZBlurTexture.mr_Texture_)
			mr_ZBlurTexture.mr_Texture_->Release();
		mr_ZBlurTexture.mr_Texture_ = NULL;
		mr_ZBlurTexture = r_Texture();

		PrintLog(ms_LogFile, L"DepthOfField: Deinitialising mr_DefaultStateBlock_...\n");
		if (mr_DefaultStateBlock_)
			mr_DefaultStateBlock_->Release();
		mr_DefaultStateBlock_ = NULL;	

		PrintLog(ms_LogFile, L"DepthOfField: Deinitialising mr_CurrentStateBlock_...\n");
		if (mr_CurrentStateBlock_)
			mr_CurrentStateBlock_->Release();
		mr_CurrentStateBlock_ = NULL;	

		PrintLog(ms_LogFile, L"DepthOfField: Deactivating finished.\n");
	}
	k_Direct3D9BasePlugin::set_Active(ab_State);
}


bool k_Plugin::UpdateParameters()
{
	float lf_Depth = object_cast<ISliderParameter>(this->GetParameter(r_DepthOfField::Depth)).get_CurrentValue()/1000.0f;
	float lf_DepthStep = object_cast<ISliderParameter>(this->GetParameter(r_DepthOfField::DepthStep)).get_CurrentValue()/100.0f;
	float lf_SizeFactor = object_cast<ISliderParameter>(this->GetParameter(r_DepthOfField::SizeFactor)).get_CurrentValue()/100.0f;
	mi_BlurSteps = object_cast<ISliderParameter>(this->GetParameter(r_DepthOfField::BlurSteps)).get_CurrentValue();
	mf_DofStrength = object_cast<ISliderParameter>(this->GetParameter(r_DepthOfField::DofStrength)).get_CurrentValue()/100.0f;
	mb_ShowDepthOnly = object_cast<ICheckBoxParameter>(this->GetParameter(r_DepthOfField::ShowDepthOnly)).get_CurrentValue();
	float lf_ZSizeFactor = object_cast<ISliderParameter>(this->GetParameter(r_DepthOfField::ZSizeFactor)).get_CurrentValue()/100.0f;
	mi_ZBlurSteps = object_cast<ISliderParameter>(this->GetParameter(r_DepthOfField::ZBlurSteps)).get_CurrentValue();
	int li_ZScreenMaskCount = object_cast<ISliderParameter>(this->GetParameter(r_DepthOfField::ZScreenMaskCount)).get_CurrentValue();
	//SE: add 2 'planes front and back! we always use
	li_ZScreenMaskCount += 2;

	if (mf_Depth != lf_Depth || mf_DepthStep != lf_DepthStep)
	{
		mf_Depth = lf_Depth;
		mf_DepthStep = lf_DepthStep;
		mb_NeedDepthUpdate = true;
	}

	if (mf_SizeFactor != lf_SizeFactor || mf_ZSizeFactor != lf_ZSizeFactor || mi_ZScreenMaskCount != li_ZScreenMaskCount)
	{
		mb_NeedDepthUpdate = true;
		mi_ZScreenMaskCount = li_ZScreenMaskCount;
		mf_SizeFactor = lf_SizeFactor;
		mf_ZSizeFactor = lf_ZSizeFactor;

		if (!this->IsInitialized())
			return false;

		if (mr_BackBufferTexture.mr_Texture_)
			mr_BackBufferTexture.mr_Texture_->Release();
		mr_BackBufferTexture.mr_Texture_ = NULL;
		mr_BackBufferTexture = r_Texture();

		if (mr_BlurTexture.mr_Texture_)
			mr_BlurTexture.mr_Texture_->Release();
		mr_BlurTexture.mr_Texture_ = NULL;
		mr_BlurTexture = r_Texture();

		if (mr_ZBackBufferTexture.mr_Texture_)
			mr_ZBackBufferTexture.mr_Texture_->Release();
		mr_ZBackBufferTexture.mr_Texture_ = NULL;
		mr_ZBackBufferTexture = r_Texture();

		if (mr_ZBlurTexture.mr_Texture_)
			mr_ZBlurTexture.mr_Texture_->Release();
		mr_ZBlurTexture.mr_Texture_ = NULL;
		mr_ZBlurTexture = r_Texture();

		this->RebuildTextures();
	}
	return false;
}


LPDIRECT3DTEXTURE9 k_Plugin::CreateTexture(int ai_Width, int ai_Height, D3DFORMAT ae_Format)
{
	PrintLog(ms_LogFile, L"DephOfField: CreateTexture: ");
	switch(ae_Format)
	{
		case D3DFMT_R8G8B8: PrintLog(ms_LogFile, L"D3DFMT_R8G8B8"); break;
		case D3DFMT_A8R8G8B8: PrintLog(ms_LogFile, L"D3DFMT_A8R8G8B8"); break;
		case D3DFMT_X8R8G8B8: PrintLog(ms_LogFile, L"D3DFMT_X8R8G8B8"); break;
		case D3DFMT_R5G6B5: PrintLog(ms_LogFile, L"D3DFMT_R5G6B5"); break;
		case D3DFMT_X1R5G5B5: PrintLog(ms_LogFile, L"D3DFMT_X1R5G5B5"); break;
		case D3DFMT_A1R5G5B5: PrintLog(ms_LogFile, L"D3DFMT_A1R5G5B5"); break;
		case D3DFMT_A4R4G4B4: PrintLog(ms_LogFile, L"D3DFMT_A4R4G4B4"); break;
		case D3DFMT_R3G3B2: PrintLog(ms_LogFile, L"D3DFMT_R3G3B2"); break;
		case D3DFMT_A8: PrintLog(ms_LogFile, L"D3DFMT_A8"); break;
		case D3DFMT_A8R3G3B2: PrintLog(ms_LogFile, L"D3DFMT_A8R3G3B2"); break;
		case D3DFMT_X4R4G4B4: PrintLog(ms_LogFile, L"D3DFMT_X4R4G4B4"); break;
		case D3DFMT_A2B10G10R10: PrintLog(ms_LogFile, L"D3DFMT_A2B10G10R10"); break;
		case D3DFMT_A8B8G8R8: PrintLog(ms_LogFile, L"D3DFMT_A8B8G8R8"); break;
		case D3DFMT_X8B8G8R8: PrintLog(ms_LogFile, L"D3DFMT_X8B8G8R8"); break;
		case D3DFMT_G16R16: PrintLog(ms_LogFile, L"D3DFMT_G16R16"); break;
		case D3DFMT_A2R10G10B10: PrintLog(ms_LogFile, L"D3DFMT_A2R10G10B10"); break;
		case D3DFMT_A16B16G16R16: PrintLog(ms_LogFile, L"D3DFMT_A16B16G16R16"); break;
		case D3DFMT_A8P8: PrintLog(ms_LogFile, L"D3DFMT_A8P8"); break;
		case D3DFMT_P8: PrintLog(ms_LogFile, L"D3DFMT_P8"); break;
		case D3DFMT_L8: PrintLog(ms_LogFile, L"D3DFMT_L8"); break;
		case D3DFMT_L16: PrintLog(ms_LogFile, L"D3DFMT_L16"); break;
		case D3DFMT_A8L8: PrintLog(ms_LogFile, L"D3DFMT_A8L8"); break;
		case D3DFMT_A4L4: PrintLog(ms_LogFile, L"D3DFMT_A4L4"); break;
		case D3DFMT_V8U8: PrintLog(ms_LogFile, L"D3DFMT_V8U8"); break;
		case D3DFMT_Q8W8V8U8: PrintLog(ms_LogFile, L"D3DFMT_Q8W8V8U8"); break;
		case D3DFMT_V16U16: PrintLog(ms_LogFile, L"D3DFMT_V16U16"); break;
		case D3DFMT_Q16W16V16U16: PrintLog(ms_LogFile, L"D3DFMT_Q16W16V16U16"); break;
		case D3DFMT_L6V5U5: PrintLog(ms_LogFile, L"D3DFMT_L6V5U5"); break;
		case D3DFMT_X8L8V8U8: PrintLog(ms_LogFile, L"D3DFMT_X8L8V8U8"); break;
		case D3DFMT_A2W10V10U10: PrintLog(ms_LogFile, L"D3DFMT_A2W10V10U10"); break;
		case D3DFMT_R16F: PrintLog(ms_LogFile, L"D3DFMT_R16F"); break;
		case D3DFMT_G16R16F: PrintLog(ms_LogFile, L"D3DFMT_G16R16F"); break;
		case D3DFMT_A16B16G16R16F: PrintLog(ms_LogFile, L"D3DFMT_A16B16G16R16F"); break;
		case D3DFMT_R32F: PrintLog(ms_LogFile, L"D3DFMT_R32F"); break;
		case D3DFMT_G32R32F: PrintLog(ms_LogFile, L"D3DFMT_G32R32F"); break;
		case D3DFMT_A32B32G32R32F: PrintLog(ms_LogFile, L"D3DFMT_A32B32G32R32F"); break;
		default: PrintLog(ms_LogFile, L"Unknown: " << ae_Format); break;
	}

	PrintLog(ms_LogFile, L" " << ai_Width << "x" << ai_Height << "\n");

	LPDIRECT3DTEXTURE9 lr_Texture_ = NULL;
	if(D3D_OK!=mr_Device_->CreateTexture(ai_Width, ai_Height, 1, D3DUSAGE_RENDERTARGET, ae_Format, D3DPOOL_DEFAULT,	&lr_Texture_,NULL))
	{
		PrintLog(ms_LogFile, L"DephOfField: Unable to create texture.\n");
		return NULL;
	}

	return lr_Texture_;
}


IShader* k_Plugin::LoadShader(const std::string& as_FileName, const std::string& as_Path)
{
	USES_CONVERSION;

	IEnvironment& lr_Environment = object_cast<IEnvironment>(mr_ServiceLocator);

	std::string ls_HelanPath = W2A(lr_Environment.get_HelanPath().c_str());
	std::string ls_File = ls_HelanPath+as_Path+"\\"+as_FileName;
	IShader* lr_Shader_ = mr_ShaderSystem_->LoadShader(as_FileName, ls_File);
	if (!lr_Shader_)
	{
		PrintLog(ms_LogFile, L"DephOfField: ");
		PrintLog(ms_LogFile, A2W(ls_File.c_str()));
		PrintLog(ms_LogFile, L" shader not loaded. Critical error!\n");
	}
	else if (lr_Shader_ && !lr_Shader_->isValid())
	{
		PrintLog(ms_LogFile, L"DephOfField: ");
		PrintLog(ms_LogFile, A2W(ls_File.c_str()));
		PrintLog(ms_LogFile, L" shader not loaded:\n");
		PrintLog(ms_LogFile, A2W(lr_Shader_->get_ShaderErrors().c_str()));
		PrintLog(ms_LogFile, L"\n");
	}
	else
	{
		PrintLog(ms_LogFile, L"DephOfField: ");
		PrintLog(ms_LogFile, A2W(ls_File.c_str()));
		PrintLog(ms_LogFile, L" shader loaded.\n");
	}

	return lr_Shader_;
}


void k_Plugin::CopyRenderTarget(IDirect3DTexture9* ar_Texture_, RECT* ar_Rect_)
{
	if (!ar_Texture_)
	{
		PrintLog(ms_LogFile, L"DephOfField: Destination texture is NULL.\n");
		return;
	}
	
	// get surfaces
	IDirect3DSurface9* lr_Dest_ = NULL;
	ar_Texture_->GetSurfaceLevel(0, &lr_Dest_);
	if (!lr_Dest_)
	{
		PrintLog(ms_LogFile, L"DephOfField: Unable to get surface from destination texture.\n");
	}

	IDirect3DSurface9* lr_Source_ = NULL;
	mr_Device_->GetRenderTarget(0, &lr_Source_);	
	if (!lr_Source_)
	{
		PrintLog(ms_LogFile, L"DephOfField: Unable to get surface from backbuffer.\n");
	}

	HRESULT lh_Result = mr_Device_->StretchRect(lr_Source_, ar_Rect_, lr_Dest_, ar_Rect_, D3DTEXF_NONE);
	if (FAILED(lh_Result))
	{
		PrintLog(ms_LogFile, L"DephOfField: Failed to update our textures: " << lh_Result << "\n");
	}

	lr_Dest_->Release();
	lr_Source_->Release();
}


void k_Plugin::Render(IRenderable* ar_ScreenMask_, IShader* ar_Shader_)
{
	// update and prepare screenmask
	ar_ScreenMask_->BeginRender(mr_Device_);

	unsigned int Passes = ar_Shader_->get_PassCount();
	ar_Shader_->Begin();
	for(unsigned int CurrentPass = 0; CurrentPass<Passes; ++CurrentPass)
	{
		RenderPassReturnValue::Enumeration ReturnValue;
		do
		{
			ReturnValue = ar_Shader_->BeginPass(CurrentPass);
			if (ReturnValue != RenderPassReturnValue::RenderAndCallNot)
			{
				ar_ScreenMask_->Render(mr_Device_);
			}
		} while(ReturnValue == RenderPassReturnValue::RenderAndCallAgain);
		ar_Shader_->EndPass(); 
	}
	ar_Shader_->End(); 
}


void k_Plugin::Blur(int ai_Steps)
{
	RECT lr_Rect;
	lr_Rect.left = lr_Rect.top = 0;
	lr_Rect.right = static_cast<LONG>(mr_QuarterMask_->width);
	lr_Rect.bottom = static_cast<LONG>(mr_QuarterMask_->height);

	// first size down, yay	
	this->Render(mr_QuarterMask_, mr_ShowTextureShader_);	
	this->CopyRenderTarget(mr_BlurTexture.mr_Texture_, &lr_Rect);

	float ddx = 1.0f/mr_BlurTexture.mi_Width;
	float ddy = 1.0f/mr_BlurTexture.mi_Height;
	mr_BlurShader_->get_ParameterByName("sddx")->set_Float4(D3DXVECTOR4(ddx, ddy, 0, 0));
	for (int i=0; i<ai_Steps; ++i)
	{
		this->Render(mr_BlurMask_, mr_BlurShader_);
		this->CopyRenderTarget(mr_BlurTexture.mr_Texture_, &lr_Rect);
	}	
}


void k_Plugin::RebuildTextures()
{
	float lf_BlurTextureScale = mf_SizeFactor;
	float lf_ZBlurTextureScale = mf_ZSizeFactor;
	LPDIRECT3DSURFACE9 lr_RendertargetSurface_;
	D3DSURFACE_DESC lr_RenderTargetDescription;

	mr_Device_->GetRenderTarget(0, &lr_RendertargetSurface_);
	lr_RendertargetSurface_->GetDesc(&lr_RenderTargetDescription);
	lr_RendertargetSurface_->Release();

	r_Texture lr_NewBackBufferTexture;
	lr_NewBackBufferTexture.me_Format = lr_RenderTargetDescription.Format;
	lr_NewBackBufferTexture.mi_Width = lr_RenderTargetDescription.Width;
	lr_NewBackBufferTexture.mi_Height = lr_RenderTargetDescription.Height;

	if (lr_NewBackBufferTexture != mr_BackBufferTexture)
	{
		lr_NewBackBufferTexture.mr_Texture_ = this->CreateTexture(lr_NewBackBufferTexture.mi_Width, lr_NewBackBufferTexture.mi_Height, lr_NewBackBufferTexture.me_Format);
		if (mr_BackBufferTexture.mr_Texture_)
			mr_BackBufferTexture.mr_Texture_->Release();
		mr_BackBufferTexture = lr_NewBackBufferTexture;

		r_Texture lr_NewZBackBufferTexture = lr_NewBackBufferTexture;
		lr_NewZBackBufferTexture.mr_Texture_ = this->CreateTexture(lr_NewZBackBufferTexture.mi_Width, lr_NewZBackBufferTexture.mi_Height, lr_NewZBackBufferTexture.me_Format);
		if (mr_ZBackBufferTexture.mr_Texture_)
			mr_ZBackBufferTexture.mr_Texture_->Release();
		mr_ZBackBufferTexture = lr_NewZBackBufferTexture;

		// rebuild screenmask
		SAFEDELETE (mr_ScreenMask_)
		mr_ScreenMask_ = new ScreenMask();
		mr_ScreenMask_->Create(mr_Device_, 0, 0, 1, 1);
		mr_ScreenMask_->SetTextureCoords(0,0,static_cast<float>(lr_NewBackBufferTexture.mi_Width)/lr_NewBackBufferTexture.mi_Width,static_cast<float>(lr_NewBackBufferTexture.mi_Height)/lr_NewBackBufferTexture.mi_Height);

		// rebuild Zscreenmask
		for(unsigned int i = 0; i < mk_ZScreenMaskList.size(); ++i)
		{
			SAFEDELETE (mk_ZScreenMaskList[i]);
		}
		mk_ZScreenMaskList.clear();
		SAFEDELETE(mk_ZScreenMaskBatch_);
		mk_ZScreenMaskBatch_ = new BatchScreenMask(mr_Device_, mi_ZScreenMaskCount*4);
		for(int i=0; i < mi_ZScreenMaskCount; ++i)
		{
			ScreenMask* lr_ScreenMask_ = NULL;
			lr_ScreenMask_ = new ScreenMask(true);
			lr_ScreenMask_->Create(mr_Device_, 0, 0, 1, 1);
			lr_ScreenMask_->SetTextureCoords(0,0,static_cast<float>(lr_NewBackBufferTexture.mi_Width)/lr_NewBackBufferTexture.mi_Width,static_cast<float>(lr_NewBackBufferTexture.mi_Height)/lr_NewBackBufferTexture.mi_Height);
			mk_ZScreenMaskList.push_back(lr_ScreenMask_);
		}

		SAFEDELETE(mr_ClearMask_);
		mr_ClearMask_ = new ScreenMask();
		mr_ClearMask_->Create(mr_Device_, 0, 0, 1, 1);
		mr_ClearMask_->SetTextureCoords(0,0,static_cast<float>(lr_NewBackBufferTexture.mi_Width)/lr_NewBackBufferTexture.mi_Width,static_cast<float>(lr_NewBackBufferTexture.mi_Height)/lr_NewBackBufferTexture.mi_Height);		
		
		// rebuild blur texture
		r_Texture lr_BlurBackBufferTexture;
		lr_BlurBackBufferTexture.me_Format = lr_RenderTargetDescription.Format;
		lr_BlurBackBufferTexture.mi_Width = static_cast<int>(mr_BackBufferTexture.mi_Width*lf_BlurTextureScale);
		lr_BlurBackBufferTexture.mi_Height = static_cast<int>(mr_BackBufferTexture.mi_Height*lf_BlurTextureScale);

		if (mr_BlurTexture.mr_Texture_)
			mr_BlurTexture.mr_Texture_->Release();
		mr_BlurTexture = lr_BlurBackBufferTexture;
		mr_BlurTexture.mr_Texture_ = this->CreateTexture(lr_BlurBackBufferTexture.mi_Width, lr_BlurBackBufferTexture.mi_Height, lr_BlurBackBufferTexture.me_Format);

		r_Texture lr_ZBlurBackBufferTexture;
		lr_ZBlurBackBufferTexture.me_Format = lr_RenderTargetDescription.Format;
		lr_ZBlurBackBufferTexture.mi_Width = static_cast<int>(mr_BackBufferTexture.mi_Width*lf_ZBlurTextureScale);
		lr_ZBlurBackBufferTexture.mi_Height = static_cast<int>(mr_BackBufferTexture.mi_Height*lf_ZBlurTextureScale);
		if (mr_ZBlurTexture.mr_Texture_)
			mr_ZBlurTexture.mr_Texture_->Release();
		mr_ZBlurTexture = lr_ZBlurBackBufferTexture;
		mr_ZBlurTexture.mr_Texture_ = this->CreateTexture(lr_ZBlurBackBufferTexture.mi_Width, lr_ZBlurBackBufferTexture.mi_Height, lr_ZBlurBackBufferTexture.me_Format);

		SAFEDELETE (mr_QuarterMask_);
		SAFEDELETE (mr_ZQuarterMask_);
		SAFEDELETE (mr_BlurMask_);
		SAFEDELETE (mr_ZBlurMask_);
		SAFEDELETE (mr_BlurToScreenMask_);

		mr_QuarterMask_ = new ScreenMask();
		mr_QuarterMask_->Create(mr_Device_, 0, 0, static_cast<float>(lr_BlurBackBufferTexture.mi_Width), static_cast<float>(lr_BlurBackBufferTexture.mi_Height), true);
		mr_QuarterMask_->SetTextureCoords(0,0,static_cast<float>(lr_NewBackBufferTexture.mi_Width)/lr_NewBackBufferTexture.mi_Width,static_cast<float>(lr_NewBackBufferTexture.mi_Height)/lr_NewBackBufferTexture.mi_Height);

		mr_ZQuarterMask_ = new ScreenMask();
		mr_ZQuarterMask_->Create(mr_Device_, 0, 0, static_cast<float>(lr_ZBlurBackBufferTexture.mi_Width), static_cast<float>(lr_ZBlurBackBufferTexture.mi_Height), true);
		mr_ZQuarterMask_->SetTextureCoords(0,0,static_cast<float>(lr_NewZBackBufferTexture.mi_Width)/lr_NewZBackBufferTexture.mi_Width,static_cast<float>(lr_NewZBackBufferTexture.mi_Height)/lr_NewZBackBufferTexture.mi_Height);
		
		mr_BlurToScreenMask_ = new ScreenMask();
		mr_BlurToScreenMask_->Create(mr_Device_, 0, 0, 1, 1);
		mr_BlurToScreenMask_->SetTextureCoords(0.0f, 0.0f, 1.0f, 1.0f);

		mr_BlurMask_ = new ScreenMask();
		mr_BlurMask_->Create(mr_Device_, 0, 0, static_cast<float>(lr_BlurBackBufferTexture.mi_Width), static_cast<float>(lr_BlurBackBufferTexture.mi_Height), true);
		mr_BlurMask_->SetTextureCoords(0.0f, 0.0f, 1.0f, 1.0f);

		mr_ZBlurMask_ = new ScreenMask();
		mr_ZBlurMask_->Create(mr_Device_, 0, 0, static_cast<float>(lr_ZBlurBackBufferTexture.mi_Width), static_cast<float>(lr_ZBlurBackBufferTexture.mi_Height), true);
		mr_ZBlurMask_->SetTextureCoords(0.0f, 0.0f, 1.0f, 1.0f);
	}
}


void k_Plugin::UpdateTextures()
{
	RECT lr_Rect;
	lr_Rect.left = lr_Rect.top = 0;
	lr_Rect.right = mr_BackBufferTexture.mi_Width;
	lr_Rect.bottom = mr_BackBufferTexture.mi_Height;

	this->CopyRenderTarget(mr_BackBufferTexture.mr_Texture_, &lr_Rect);

	if (this->IsViewportNew() || mb_NeedDepthUpdate)
	{
		for(unsigned int i=0; i<mk_ZScreenMaskList.size(); ++i)
		{
			float lf_Depth = mf_Depth - (i * (mf_DepthStep / mi_ZScreenMaskCount));
			lf_Depth = std::pow(lf_Depth, 0.01f);

			float lf_RealDepth = static_cast<float>(this->CalcZDepth(lf_Depth));
			mk_ZScreenMaskList[i]->SetZ(lf_RealDepth); // real z depth

			lf_Depth = 1.0f - (i * (1.0f / mi_ZScreenMaskCount));
			mk_ZScreenMaskList[i]->SetTextureCoords(lf_Depth, lf_Depth, lf_Depth, lf_Depth, 1); // color
			mk_ZScreenMaskList[i]->ReCreate(mr_Device_);
			mk_ZScreenMaskBatch_->AddScreenMaskData(*mk_ZScreenMaskList[i], mr_Device_, i==0, i+1==mk_ZScreenMaskList.size());
		}

		mb_NeedDepthUpdate = false;
	}

	this->Render(mr_ClearMask_, mr_BlackRenderShader_);	

	this->Render(mk_ZScreenMaskBatch_, mr_DepthRenderShader_);
	
	this->CopyRenderTarget(mr_ZBackBufferTexture.mr_Texture_, &lr_Rect);
}


void k_Plugin::UpdateRendering()
{
	if (mb_ShowDepthOnly)
	{
		this->Render(mr_ScreenMask_, mr_ShowTranslucentTextureShader_);
		return;
	}
	this->Blur(mi_BlurSteps);
	this->ZBlur(mi_ZBlurSteps);
	this->Render(mr_ScreenMask_, mr_ShowTextureShader_);

	mr_DepthOfFieldShader_->get_ParameterByName("Strength")->set_Float4(D3DXVECTOR4(mf_DofStrength,0,0,0));
	this->Render(mr_BlurToScreenMask_, mr_DepthOfFieldShader_);
}


bool k_Plugin::CanRender()
{
	IDirect3DSurface9* lr_Surface_ = NULL;
	mr_Device_->GetRenderTarget(0, &lr_Surface_);

	IDirect3DSwapChain9* lr_SwapChain_ = NULL;
	for (unsigned int i=0; i<mr_Caps.NumberOfAdaptersInGroup; ++i)
	{
		mr_Device_->GetSwapChain(i, &lr_SwapChain_);
		if (lr_SwapChain_)
		{
			D3DPRESENT_PARAMETERS lr_Param;
			lr_SwapChain_->GetPresentParameters(&lr_Param);
			for (unsigned int j=0; j<lr_Param.BackBufferCount; ++j)
			{
				IDirect3DSurface9* lr_ScSurface_ = NULL;
				lr_SwapChain_->GetBackBuffer(j, D3DBACKBUFFER_TYPE_MONO, &lr_ScSurface_);
				if (lr_ScSurface_)
				{
					if (lr_ScSurface_==lr_Surface_)
					{
						lr_Surface_->Release();
						lr_ScSurface_->Release();
						lr_SwapChain_->Release();
						return true;
					}
					lr_ScSurface_->Release();
				}

			}
			
			lr_SwapChain_->Release();
		}
	}
	
	lr_Surface_->Release();
	return false;
}


double k_Plugin::CalcZDepth(double ad_PercentageDepth)
{
	D3DXVECTOR3 lr_Final;
	D3DXVECTOR3 lr_Depth(0, 0, static_cast<float>(1.0+ad_PercentageDepth));

	D3DXMATRIX  lr_World;
	::D3DXMatrixIdentity(&lr_World);
	::D3DXVec3Project(
		&lr_Final,
		&lr_Depth,
		&mr_Viewport,
		&mr_ProjMatrix,
		&mr_ViewMatrix,
		&lr_World
		);

	return lr_Final.z;
}


bool k_Plugin::IsViewportNew()
{
	D3DVIEWPORT9 lr_Viewport;
	mr_Device_->GetViewport(&lr_Viewport);
	if (mr_Viewport.MaxZ == lr_Viewport.MaxZ && mr_Viewport.MinZ == lr_Viewport.MinZ)
		return false;

	mr_Viewport = lr_Viewport;

	return true;
}


void k_Plugin::ZBlur(int ai_Steps)
{
	RECT lr_Rect;
	lr_Rect.left = lr_Rect.top = 0;
	lr_Rect.right = static_cast<LONG>(mr_ZQuarterMask_->width);
	lr_Rect.bottom = static_cast<LONG>(mr_ZQuarterMask_->height);

	// first size down, yay	
	this->Render(mr_ZQuarterMask_, mr_ShowZTextureShader_);	
	this->CopyRenderTarget(mr_ZBlurTexture.mr_Texture_, &lr_Rect);

	float ddx = 1.0f/mr_ZBlurTexture.mi_Width;
	float ddy = 1.0f/mr_ZBlurTexture.mi_Height;
	mr_ZBlurShader_->get_ParameterByName("sddx")->set_Float4(D3DXVECTOR4(ddx,ddy,0,0));
	for (int i=0; i<ai_Steps; ++i)
	{
		this->Render(mr_ZBlurMask_, mr_ZBlurShader_);
		this->CopyRenderTarget(mr_ZBlurTexture.mr_Texture_, &lr_Rect);
	}
}
