/*	Renegade Scripts.dll
	Base class and helper classes for scene (post-processing) shaders
	Copyright 2007 Mark Sararu, Jonathan Wilson

	This file is part of the Renegade scripts.dll
	The Renegade scripts.dll is free software; you can redistribute it and/or modify it under
	the terms of the GNU General Public License as published by the Free
	Software Foundation; either version 2, or (at your option) any later
	version. See the file COPYING for more details.
	In addition, an exemption is given to allow Run Time Dynamic Linking of this code with any closed source module that does not contain code covered by this licence.
	Only the source code to the module(s) containing the licenced code has to be released.
*/
#include "scripts.h"
#include "shadereng.h"
#include "resourcemanager.h"
#include "shaderstatemanager.h"
#include "effect.h"
#include "shader_scene.h"
SceneShaderControllerClass* SceneShaderController;
SceneShaderRegistrarClass* SceneShaderRegistrar;
#ifndef SDBEDIT
DebugOutputClass *SceneShaderClassDebug;
#endif

SceneShaderClass::SceneShaderClass()
{
	UID = 0;
	Name = 0;
	FXFilename = 0;
	Initialized = false;
	Enabled = false;
#ifndef SDBEDIT
	Effect = 0;
#endif
	PrivateData = 0;
}
SceneShaderClass::~SceneShaderClass()
{
	if (Name)
	{
		delete[] Name;
	}
	Name = 0;
	if (FXFilename) 
	{
		delete[] FXFilename;
	}
	FXFilename = 0;
#ifndef SDBEDIT
	if (Effect)
	{
		Effect->Release_Ref();
	}
	Effect = 0;
#endif
	if (PrivateData)
	{
		delete PrivateData;
	}
	PrivateData = 0;
}
void SceneShaderClass::Load(ChunkLoadClass& cload)
{
 	while (cload.Open_Micro_Chunk())
	{
		switch(cload.Cur_Micro_Chunk_ID())
		{
			case MC_SCENESHADER_UID:
				cload.Read(&UID,sizeof(unsigned int));
			case MC_SCENESHADER_NAME:
				Name = new char[cload.Cur_Micro_Chunk_Length()];
				cload.Read(Name,cload.Cur_Micro_Chunk_Length());
				break;
			case MC_SCENESHADER_FXFILENAME:
				FXFilename = new char[cload.Cur_Micro_Chunk_Length()];
				cload.Read(FXFilename,cload.Cur_Micro_Chunk_Length());
				#ifndef SDBEDIT
				Effect = new EffectClass(FXFilename);
				#endif
				break;
		}
		cload.Close_Micro_Chunk();
	}
}
void SceneShaderClass::Save(ChunkSaveClass& csave)
{
	csave.Begin_Chunk(CHUNK_SCENESHADER);
	csave.Begin_Micro_Chunk(MC_SCENESHADER_UID);
	csave.Write(&UID,sizeof(unsigned int));
	csave.End_Micro_Chunk();
	csave.Begin_Micro_Chunk(MC_SCENESHADER_NAME);
	csave.Write(Name,strlen(Name) + 1);
	csave.End_Micro_Chunk();
 	csave.Begin_Micro_Chunk(MC_SCENESHADER_FXFILENAME);
	csave.Write(FXFilename,strlen(FXFilename) + 1);
	csave.End_Micro_Chunk();
	csave.End_Chunk();
}
#ifdef SDBEDIT
SceneShaderEditorClass* SceneShaderClass::GetEditor()
{
	return 0;
}
#endif
#ifndef SDBEDIT
void SceneShaderClass::Initialize()
{
	Effect->Init();
	this->Initialized = true;
}
bool SceneShaderClass::Validate()
{
	return true;
}
void SceneShaderClass::OnDeviceLost()
{
	Effect->OnDeviceLost();
	this->Initialized = false;
}
void SceneShaderClass::OnDeviceReset()
{
	Effect->OnDeviceReset();
	this->Initialized = true;
}
void SceneShaderClass::OnCustom(unsigned int eventid, float parameter)
{

}
void SceneShaderClass::Render(SceneShaderRenderInfo *render)
{

}
SceneShaderRenderClass::SceneShaderRenderClass()
{
	BackBufferRT = 0;
	DepthBuffer = 0;
	BackBuffer = 0;
 	EffectBufferRT = 0;
	EffectBuffer = 0;
}
void SceneShaderRenderClass::OnDeviceLost()
{
	if (BackBuffer)
	{
		BackBuffer->Release();
	}
	BackBuffer = 0;
	SceneShaderClassDebug->Assert(BackBufferRT == 0,"[%s:%d] BackBufferRT still valid\n",__FILE__,__LINE__);
	if(BackBufferRT)
	{
		BackBufferRT->Release();
	}
	BackBufferRT = 0;
	SceneShaderClassDebug->Assert(DepthBuffer == 0,"[%s:%d] DepthBuffer still valid\n",__FILE__,__LINE__);
	if (DepthBuffer)
	{
		DepthBuffer->Release();
	}
	DepthBuffer = 0;
	if (EffectBufferRT)
	{
		EffectBufferRT->Release();
	}
	EffectBufferRT = 0;
	if (EffectBuffer)
	{
		EffectBuffer->Release();
	}
	EffectBuffer = 0;
}
void SceneShaderRenderClass::Render(SceneShaderClass *shader)
{
	if (shader->Validate() == false)
	{
		return;
	}
	Direct3DDevice->GetRenderTarget(0,&BackBufferRT);
	Direct3DDevice->GetDepthStencilSurface(&DepthBuffer);
	Direct3DDevice->SetDepthStencilSurface(0);
	if (!BackBuffer)
	{
		HRESULT res = Direct3DDevice->CreateTexture(parameters->BackBufferWidth,parameters->BackBufferHeight,1,D3DUSAGE_RENDERTARGET,parameters->BackBufferFormat,D3DPOOL_DEFAULT,&BackBuffer,NULL);
		if (FAILED(res))
		{
			SceneShaderClassDebug->Trace(DEBUGLEVEL_ERROR,"BackBuffer failed to create with error code '%X'\n",res);
		}
	}
	if (!EffectBufferRT)
	{
		HRESULT res =Direct3DDevice->CreateRenderTarget(parameters->BackBufferWidth,parameters->BackBufferHeight,parameters->BackBufferFormat,D3DMULTISAMPLE_NONE,0,false,&EffectBufferRT,NULL);
		if (FAILED(res))
		{
			SceneShaderClassDebug->Trace(DEBUGLEVEL_ERROR,"BackBufferRT failed to create with error code '%X'\n",res);
		}
	}
	IDirect3DSurface9* surBackBuffer;
	BackBuffer->GetSurfaceLevel(0, &surBackBuffer);
	Direct3DDevice->StretchRect(BackBufferRT,NULL,surBackBuffer,NULL,D3DTEXF_NONE);
	surBackBuffer->Release();
	surBackBuffer = 0;
	RenderInfo.OutputSurface = EffectBufferRT;
	RenderInfo.SceneBuffer = BackBuffer;
	RenderInfo.InputBuffer = BackBuffer;
	RenderInfo.Width = (float)parameters->BackBufferWidth;
	RenderInfo.Height = (float)parameters->BackBufferHeight;
	RenderInfo.UScale = 1.0;
	RenderInfo.VScale = 1.0;
	Direct3DDevice->EndScene();
	StateManager->SetTextureStageState(0,D3DTSS_TEXCOORDINDEX,0);
	StateManager->SetTextureStageState(1,D3DTSS_TEXCOORDINDEX,1);
	StateManager->SetTextureStageState(0,D3DTSS_TEXTURETRANSFORMFLAGS,0);
	StateManager->SetTextureStageState(1,D3DTSS_TEXTURETRANSFORMFLAGS,0);
	shader->Render(&RenderInfo);
	Direct3DDevice->StretchRect(RenderInfo.OutputSurface,NULL,BackBufferRT,NULL,D3DTEXF_NONE);
	StateManager->SetRenderTarget(0,BackBufferRT);
	Direct3DDevice->SetDepthStencilSurface(DepthBuffer);
	BackBufferRT->Release();
	BackBufferRT = 0;
	DepthBuffer->Release();
	DepthBuffer = 0;
	Direct3DDevice->BeginScene();
}
void SceneShaderRenderClass::RequestNewBuffer(SceneShaderRenderInfo **renderinfo)
{
	if (!EffectBuffer)
	{
		HRESULT res = Direct3DDevice->CreateTexture(parameters->BackBufferWidth,parameters->BackBufferHeight,1,D3DUSAGE_RENDERTARGET,parameters->BackBufferFormat,D3DPOOL_DEFAULT,&EffectBuffer,NULL);
		if (FAILED(res))
		{
			SceneShaderClassDebug->Trace(DEBUGLEVEL_ERROR,"BackBufferRT failed to create with error code %X\n",res);
		}
	}
	IDirect3DSurface9* surEffectBuffer;
	EffectBuffer->GetSurfaceLevel(0, &surEffectBuffer);
	Direct3DDevice->StretchRect(EffectBufferRT,NULL,surEffectBuffer,NULL,D3DTEXF_NONE);
	surEffectBuffer->Release();
	RenderInfo.InputBuffer = EffectBuffer;
	*renderinfo = &RenderInfo;
}
#endif
SceneShaderControllerClass::SceneShaderControllerClass()
{
	Shaders = 0;
#ifndef SDBEDIT
	SceneShaderClassDebug = new DebugOutputClass("_shader.txt");
	ActiveShader = 0;
	ScopeShaderActive = false;
	ScopeShader = 0;
	RenderShader = new SceneShaderRenderClass();
#endif
}
SceneShaderControllerClass::~SceneShaderControllerClass()
{
#ifndef SDBEDIT
	delete RenderShader;
	delete SceneShaderClassDebug;
#endif
	UnloadDatabase();
}
void SceneShaderControllerClass::LoadDatabase(FileClass *file)
{
	if (SceneShaderRegistrar->FactoryCount == 0) 
	{
		return;
	}
	if (!Shaders) 
	{
		Shaders = new SimpleDynVecClass<SceneShaderClass *>(0);
	}
	ChunkLoadClass cload(file);
	while (cload.Open_Chunk())
	{
		SceneShaderClass *shader = LoadSceneShader(cload);
		if (shader)
		{
			Shaders->Add(shader);
		}
		cload.Close_Chunk();
	}
	if (Shaders->Count() >= 1) DatabaseLoaded = true;
}
void SceneShaderControllerClass::SaveDatabase(FileClass *file)
{
	if (!Shaders)
	{
		return;
	}
	ChunkSaveClass csave(file);
	if (Shaders)
	{
		for (int i = 0;i < Shaders->Count();i++)
		{
			(*Shaders)[i]->Save(csave);
		}	
	}
}
void SceneShaderControllerClass::UnloadDatabase()
{
	if (Shaders)
	{
		for (int i = 0; i < Shaders->Count(); i++)
		{
			if((*Shaders)[i])
			{
				delete (*Shaders)[i]; 
			}
			(*Shaders)[i] = 0;
		}
		delete Shaders;
	}
	Shaders = 0;
}
unsigned int SceneShaderControllerClass::GetShaderCount()
{
	if (!Shaders)
	{
		return 0;
	}
	return Shaders->Count();
}
SceneShaderClass* SceneShaderControllerClass::GetShaderByIndex(unsigned int index)
{
	if (!Shaders)
	{
		return 0;
	}	
	return (*Shaders)[index];
}
void SceneShaderControllerClass::AddShader(SceneShaderClass* shader)
{
	if (!Shaders)
	{
		Shaders = new SimpleDynVecClass<SceneShaderClass *>(0);
	}
	Shaders->Add(shader);
}
void SceneShaderControllerClass::DeleteShader(SceneShaderClass* shader)
{
	if (!Shaders)
	{
		return;
	}
   	for (int i = 0; i < Shaders->Count(); i++)
	{
		if((*Shaders)[i] == shader)
		{
			#ifndef SDBEDIT
			(*Shaders)[i]->OnDeviceLost();
			#endif
			delete (*Shaders)[i];
			Shaders->Delete(i);
		}

	}
}
void SceneShaderControllerClass::DeleteShaderByIndex(unsigned int index)
{
 	if (!Shaders)
	{
		return;
	}
	if((*Shaders)[index])
	{
		#ifndef SDBEDIT
		(*Shaders)[index]->OnDeviceLost();
		#endif
		delete (*Shaders)[index];
		Shaders->Delete(index);
	}
}
SceneShaderClass *SceneShaderControllerClass::LoadSceneShader(ChunkLoadClass& cload)
{
	SceneShaderFactory *factory = SceneShaderRegistrar->GetFactory(cload.Cur_Chunk_ID());
	if (factory)
	{
		return factory->Load(cload);	
	}
	char *c = new char[cload.Cur_Chunk_Length()];
	cload.Read(c,cload.Cur_Chunk_Length());
	delete[] c;
	return 0;
}
#ifndef SDBEDIT
void SceneShaderControllerClass::IntializeShaders()
{
	if (!Shaders)
	{
		return;
	}
	for (int i = 0; i < Shaders->Count(); i++)
	{
		SceneShaderInitTask *ShaderInitTask = new SceneShaderInitTask((*Shaders)[i]);
		ResourceFactory->EnqueueTask(ShaderInitTask,RESOURCELOADPRIORITY_NORMAL); 
	}
}
void SceneShaderControllerClass::OnDeviceLost()
{
	if (!Shaders)
	{
		return;
	}
	for (int i = 0; i < Shaders->Count(); i++)
	{
		(*Shaders)[i]->OnDeviceLost();
	}
	RenderShader->OnDeviceLost();
}
void SceneShaderControllerClass::OnDeviceReset()
{
	if (!Shaders)
	{
		return;
	}
	for (int i = 0; i < Shaders->Count(); i++)
	{
		(*Shaders)[i]->OnDeviceReset();
	}
}
void SceneShaderControllerClass::OnCustom(unsigned int eventid, float parameter)
{
	if (!Shaders)
	{
		return;
	}
	switch(eventid)
	{
	case EVENT_ENABLESHADER:
		if (ActiveShader)
		{
			if (parameter == 0)
			{
				ActiveShader->Enabled = false;
			}
			else 
			{
				ActiveShader->Enabled = true;
			}
		}
		break;
	case EVENT_SETSHADER:
		if (parameter == 0) 
		{
			if (ActiveShader)
			{
				ActiveShader->Enabled = false;
			}
			ActiveShader = 0;
		}
		else 
		{
			for (int i = 0; i < Shaders->Count(); i++)
			{
				if ((*Shaders)[i]->UID == (unsigned int) parameter)
				{
					if (ActiveShader)
					{
						ActiveShader->Enabled = false;
					}
					ActiveShader = (*Shaders)[i];
					ActiveShader->Enabled = true;
				}
			}
		}
		break;
	case EVENT_ENABLESCOPESHADER:
		if (ScopeShader)
		{
			if (parameter == 0)
			{
				ScopeShader->Enabled = false;
				ScopeShaderActive = false;
			}
			else 
			{
				ScopeShader->Enabled = true;
				ScopeShaderActive = true;
			}
		}
		break;
	case EVENT_SETSCOPESHADER:
		if (parameter == 0) 
		{
			ScopeShader = 0;
		}
		else 
		{
			for (int i = 0; i < Shaders->Count(); i++)
			{
				if ((*Shaders)[i]->UID == (unsigned int) parameter)
				{
					ScopeShader = (*Shaders)[i];
				}
			}
		}
		break;
	default:
		if (ActiveShader)
		{
			ActiveShader->OnCustom(eventid,parameter);
		}
		break;
	}
}
void SceneShaderControllerClass::OnFrameStart()
{

}
void SceneShaderControllerClass::OnFrameEnd()
{
	if (ScopeShaderActive && ScopeShader)
	{
		RenderShader->Render(ScopeShader);
	}
	else if (ActiveShader) 
	{
		RenderShader->Render(ActiveShader);
	}
}
void SceneShaderControllerClass::RequestNewBuffer(SceneShaderRenderInfo **renderinfo)
{
	RenderShader->RequestNewBuffer(renderinfo);
}

SceneShaderInitTask::SceneShaderInitTask(SceneShaderClass *sceneshader)
{
	Data = sceneshader;
}
void SceneShaderInitTask::LoadResource()
{
	SceneShaderClass *sceneshader = (SceneShaderClass*) Data;
	sceneshader->Initialize();
}
#endif

SceneShaderRegistrarClass::SceneShaderRegistrarClass()
{
	Factories = new SimpleDynVecClass<SceneShaderFactory *>(0);
	FactoryCount = 0;
}
SceneShaderRegistrarClass::~SceneShaderRegistrarClass()
{
	for (int i = 0; i < Factories->Count(); i++)
	{
		(*Factories)[i] = 0;
	}
	delete Factories;
}
void SceneShaderRegistrarClass::RegisterFactory(SceneShaderFactory *factory)
{
	Factories->Add(factory);
	FactoryCount++;
}
SceneShaderFactory* SceneShaderRegistrarClass::GetFactory(unsigned int chunkid)
{
	for (int i = 0;i < FactoryCount;i++)
	{
		if ((*Factories)[i]->Chunk_ID == chunkid)
		{
			return (*Factories)[i];
		}
	}	
	return 0;
}
SceneShaderFactory* SceneShaderRegistrarClass::GetFactoryByIndex(int index)
{
	if (index > FactoryCount) return 0;
	if ((*Factories)[index])
	{
		return (*Factories)[index];
	}	
	return 0;
}
