#pragma once

#include "..\munga\vidrend.h"
#include "..\munga\rotation.h"
#include "..\munga\reticle.h"
#include "..\munga\graph2d.h"
#include "..\munga\simulate.h"
#include "l4vidrnd.h"
#include "..\munga\tree.h"
#include "..\munga\table.h"
#include "l4d3d.h"
#include <string>
#include <hash_map>
#include <vector>

using namespace std;
using namespace stdext;

//STUBBED: DPL RB 1/14/07
//once the stubs are removed, the following can be removed
#define char8	unsigned char

// RB 1/14/07
//#include <dpl\dpl.h>
//#include <dpl\dpl_2d.h>
#include <D3DX9.h>
#include <D3DX9math.h>

class NotationFile;

#define	INTERSECT_ALL	(0xFFFFFFFF)

//##########################################################################
//	Declaration of dpl callback function that performs in-place text
// substitution on all material names before they are looked up.
// There must be at least MATERIAL_NAME_LENGTH bytes available at source!
//
#define MATERIAL_NAME_BUFFER_LENGTH 256

char*
	substituteMaterial(char *source);

void loadTables();
const char* opMaterialName(const char *fileName, int opId);

struct ReplacementMaterialData
{
	float r, g, b;
	string texName;
};

hash_map<string, hash_map<int, string>> *gOpNames;
hash_map<string, ReplacementMaterialData> *gReplacementData;

//##########################################################################
//	This structure is attached to DPL nodes so we can put information in them
// on what entity the node is part of and what damage zone it belongs to.
//
#define MAX_DZ_NAME_LENGTH 25

struct dpl_tracker
{
	Entity
		*This_Entity;						// The entity this is part of
	char
		dz_name[MAX_DZ_NAME_LENGTH];	// this is temporary, to make testing easier
	int
		Damage_Zone_Number;				// number of this damage zone
};

//-----------------------------------------------------------------------------
//--------------------------DPL video resource object--------------------------
//-----------------------------------------------------------------------------
//##########################################################################
//##########################    L4VideoObject    ###########################
//##########################################################################

#define MAX_OBJECT_FILENAME_LENGTH 15

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// This class gets stored in a resource so it is derived from nothing.
//
class L4VideoObject
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction, Destruction and testing
//
public:
	enum ResourceType
	{
		Unknown,
		Skeleton,
		Object,
     Rubble,
		VidFile
	};
	enum RendererModes	// bit flags
	{
		Normal			 = 0x000,
		BillboardXAxis	 = 0x001,
		BillboardYAxis  = 0x002,
		BillboardZAxis  = 0x004,
		BillboardObject = (BillboardXAxis | BillboardYAxis | BillboardZAxis),
		BlinkObject 	 = 0x008,
		IntersectImmune = 0x010
	};

//		L4VideoObject();
	L4VideoObject(
		const char *filename,
		ResourceType resource_type,
		Enumeration renderer_modes,	// RendererModes
		float blink_period = 0.0f,
		float percent_time_on = 0.0f
	);
	~L4VideoObject();

	const char*
		GetObjectFilename() const
			{ return objectFilename; }
	char*
		GetObjectFilename()
			{ return objectFilename; }
	ResourceType
		GetResourceType() const
			{ return resourceType; }
	Enumeration	// RendererModes
		GetRendererModes() const
			{ return rendererModes; }

	Logical
		TestInstance() const;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Implementations
//
//	private:

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private data
//
private:
	char
		objectFilename[MAX_OBJECT_FILENAME_LENGTH];
	ResourceType
		resourceType;
	Enumeration			// RendererModes
		rendererModes;	// bit flags (see RendererModes)
	float
		blinkPeriod,	// cycle length in seconds
		percentTimeOn;	// percent of cycle on
};

//##########################################################################
//#######################    L4VideoObjectWrapper    #######################
//##########################################################################

class L4VideoObjectWrapper:
	public Plug
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction, Destruction and testing
//
public:
//		L4VideoObjectWrapper();
	L4VideoObjectWrapper(
		L4VideoObject *video_object,
		Logical delete_object = False
	);
	~L4VideoObjectWrapper();

	L4VideoObject*
		GetVideoObject() const
			{ Check(this); return videoObject; }

	Logical
		TestInstance() const;

	static int
		BuildVideoObjectChainFromResource(
			ChainOf<L4VideoObjectWrapper*> *video_chain,
			ResourceDescription *video_resource
		);
	static void
		DeleteVideoObjectChain(
			ChainOf<L4VideoObjectWrapper*> *video_chain
		);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Implementations
//
//	public:

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private data
//
private:
	L4VideoObject
		*videoObject;
	Logical
		deleteObject;
};

//
//-----------------------------------------------------------------------------
//--------------Actual DPL video renderer class starts below-------------------
//-----------------------------------------------------------------------------
//
class DPLObjectCacheLine:
	public Plug
{
	public:
		DPLObjectCacheLine(
			const CString 		&object_name,
			dpl_OBJECT			*object_ptr)
			{
				objectName		=	object_name;
				objectPointer	=	object_ptr;
			};

		~DPLObjectCacheLine(){};

		Logical
			TestInstance() const
			{
				Check(&objectName);
				Check_Pointer(objectPointer);

				return True;
			};

		CString
			objectName;
		dpl_OBJECT
			*objectPointer;
};

class DPLJointToDCSTranslator:
	public Plug
{
	public:
		DPLJointToDCSTranslator(
			Entity	*entity,						// The entity to translate
			dpl_DCS	*dcs_array[]);				// Array of DCS's to translate

		~DPLJointToDCSTranslator();

		Logical
			TestInstance() const;

		int
			DCSCount;
		dpl_DCS
			**translation_array;					// array to translate from joint # to DCS*
};

//##########################################################################
//###########################    DPLRenderer    ############################
//##########################################################################
#define	DELAY_DCS_FLUSH_ARRAY_SIZE	100
#define	MAX_PSFX_COUNT		40
#define MAX_INDIE_EMITTERS	32
class DPLRenderer :
	public VideoRenderer
{
public:
	//
	// Construction, Destruction and test instance declarations
	//
	DPLRenderer(HWND hWnd, unsigned int screenWidth, unsigned int screenHeight, bool fullscreen, InterestType interest_type, InterestDepth depth_calibration);
	~DPLRenderer();

	virtual vector<MONITORINFO> MonitorsCreateAll(int &monitorCount);

	Logical TestInstance() const;
	//
	// These routines are the renderer side of the culling system, they are
	// used to setup the stored data on the eyepoint's transformation matrices
	//
	inline Scalar GetCurrentFrameTime() { return(currentFrameTime); }		// Returns the time at the start of the frame

	inline LinearMatrix* GetWorldToEyeMatrix() { return(&worldToEyeMatrix); }		// Returns the world to eye matrix (for culling)
	inline Scalar GetViewRatio() { return(viewRatio); }					// Returns tan(viewAngle/2.0)
	void SetupCull();								// Sets up the WorldToEyeMatrix each frame
	//
	// These routines manage the renderer's list of renderables that need to be
	// executed each frame.  For the moment these routines just blindly add and
	// remove the items from the dplRenderableSocket
	//
	void AddDynamicRenderable(Component *my_renderable);
	void RemoveDynamicRenderable(Component *my_renderable);
	//
	// These routines are used for tracking target frame time
	//
	void ResetStatistics();
	void ReportStatistics();
	//
	// Other renderer stuff
	//
	dpl_ZONE* MakeNewZone();

	void MarkDCSHiearchy(dpl_DCS *root_DCS, Entity *entity);
	void FlushBitSliceTexture(unsigned int *local_storage);
	void LoadBitSliceTexture(BitMap *bitmap_to_load, LPDIRECT3DTEXTURE9 local_storage);

	void SetViewAngle(Degree new_angle);

	unsigned int* MakeBitSliceStorage();

	void SortAndReloadNameBitmaps();

	void LoadNameBitmaps();

	void LoadOrdinalBitmaps();

	void DPLIndependantEffect(Point3D location,			// Location of the effect in world space
							int effect_number,			// DPL effect number to trigger at this location
							dpl_DCS *my_dcs = NULL,
							int subid = 0);				// inserted into third byte of effect id.

	void DPLIndependantPFX(Point3D location,								// Location in space to trigger the effect
						dpl_PARTICLESTART_EFFECT_INFO *psfx_definition,		// Description of the pfx
						dpl_DCS	*my_dcs = NULL,								// optional DCS to issue from
						int subid = 0);										// inserted into third byte of effect id.

	void DPLDelayDCSFlush(dpl_DCS *my_dcs);							// The DCS we want to remember for later

	void DPLDoDCSBatchFlush();							// Flush the dcs's remembered by DPLDelayDCSFlush

	void DPLReportFreeMemory(std::ostream &output);		// Report current free memory in card

	void DPLReportPerfStats(std::ostream &output);		// Report performance statistics

	void DPLToggleWireframe();							// Toggle state of dpl wireframe

	void DPLTogglePVision();							// Toggle state of dpl pvision

	void DPLFrameDump(Logical antialias);				// Write framedump to targa file

	InnerProjectileRenderable* GetProjectile(
			d3d_OBJECT	*graphical_object,	// object to hang on the DCS, may be a list later <NULL>
			bool isDeathZone);	// DPL Zone this stuff will live in (for culling)

	void ReleaseProjectile(InnerProjectileRenderable *inner_projectile);

	int GetUniqueID();

	dpl_PARTICLESTART_EFFECT_INFO* ReadPSFX(const char *file_name);		// Name of the file containing the PSFX description

	dpl_OBJECT* GetCachedObject(const CString &object_name);			// Name of the object we want to get

	void PutCachedObject(
			const CString 	&object_name,					// Name of the object we will cache
			dpl_OBJECT		*object_pointer);				// pointer to the object being cached

	static ResourceDescription::ResourceID CreateModelVideoStreamResource(ResourceFile *resource_file, const char *model_name, NotationFile *model_file, const ResourceDirectories *directories);
	
	void CacheExplosionScripts(int script_select);			// The script to be cached

	enum FogStyle
	{
		updateFogSetting,
		noUpdateFogSetting,
		searchLightOnFogStyle,
		searchLightOffFogStyle,
		winnersCircleFogStyle
	};

	void SetFogStyle(FogStyle my_fog);

	void GetCurrentFogSettings(float *fogRed, float *fogGreen, float *fogBlue, float *fogNear, float *fogFar);
	void SetCurrentFogLimits(float fogNear, float fogFar);

	LPDIRECT3DTEXTURE9 GetNameTexture(int playerIndex)	{ return mNameTextures[playerIndex]; }

	inline LPDIRECT3DDEVICE9 GetDevice()		{ return mDevice; }
	inline LPD3DXMATRIXSTACK GetMatrixStack()	{ return m_MatrixStack; }
	inline void AddRenderable(HierarchicalDrawComponent *component) { mRenderables.Add(component); }
	void AddToPassList(d3d_OBJECT *object, int pass);
	void AddStaticObject(d3d_OBJECT *object);
	void RecurseStaticObject(HierarchicalDrawComponent *obj);
	virtual void ConsolidateStaticObjects();

	inline unsigned int GetWidth()	{ return x_size; }
	inline unsigned int GetHeight()	{ return y_size; }

	inline int *GetSecondaryIndex() { return mSecondaryIndex; }
	inline int *GetAux1Index() { return mAux1Index; }
	inline int *GetAux2Index() { return mAux2Index; }

private:
	d3d_OBJECT *ConsolidateSingleObject(LPD3DXMESH *meshes, D3DXMATRIX *transforms, UINT startMesh, UINT meshCount, hash_map<DWORD, hyper> subsetHash, hash_map<hyper, DWORD> hashToOp, vector<L4DRAWOP*> finalOps);
	void ExplosionScripts(Entity *entity,						// The entity we are dealing with
						ResourceDescription *model_resource,	// Pointer to the video resource
						ViewFrom view_type, 					// Type of reference (inside/outside...etc.)
						int script_select);
	void DPLRenderer::DPLReadINIPage(NotationFile *master_notation_file, const char *starting_page_name, Mission *mission, Logical debug_print);

	void DPLRenderer::DPLReadEnvironment(Mission *mission);
	void ShutdownImplementation();
	void SuspendImplementation();
	void ResumeImplementation();
	void ExecuteImplementation(RendererComplexity complexity_update, RendererOrigin::InterestingEntityIterator *iterator);
	void ExecuteIdle();

	hyper HashAdd(hyper input, char data);
	hyper HashDrawOp(L4DRAWOP *op);

	//Damn eye renderable is our camera and I hate it
public:
	DPLEyeRenderable *mCamera;
	float GetCloudRed() { return mCloudRed;}
	float GetCloudGreen() { return mCloudGreen;}
	float GetCloudBlue() { return mCloudBlue;}
	float GetCloudEmitRed() { return mCloudEmitRed; }
	float GetCloudEmitGreen() { return mCloudEmitGreen; }
	float GetCloudEmitBlue() { return mCloudEmitBlue; }

	void SetCoreRenderStates();

private:
	//
	// Direct3D Required Variables
	//
	LPDIRECT3DDEVICE9 mDevice;
	D3DPRESENT_PARAMETERS mPresentParams;
	unsigned int x_size;
	unsigned int y_size;
	SChainOf<HierarchicalDrawComponent *> mRenderables;
	d3d_OBJECT *mRenderLists[PASS_TOTAL_COUNT];
	LPD3DXMATRIXSTACK m_MatrixStack;
	D3DXMATRIX mProjectionMatrix;
	D3DXMATRIX mDecalProjectionMatrix;
	float mDecalEpsilon;

	void FindBestAdapterIndices(bool isWindowed);

	float mCloudRed, mCloudGreen, mCloudBlue;
	float mCloudEmitRed, mCloudEmitGreen, mCloudEmitBlue;

	int *mPrimaryIndex, *mSecondaryIndex, *mAux1Index, *mAux2Index;

	// these are used for the map rendering
	d3d_OBJECT *mStaticObjectsHead;
	int mStaticObjectsCount;
	std::list<d3d_OBJECT *> mConsolidatedStaticObjects;

	// name and ordinal texture maps
	LPDIRECT3DTEXTURE9 mNameTextures[MAX_PLAYER_NAMES];
	LPDIRECT3DTEXTURE9 mOrdinalTextures[MAX_PLAYER_NAMES];
	
protected:
	ReticleRenderable *mReticle;
	CameraShipHUDRenderable *mCamShipHUD;
	
	void LoadMissionImplementation(Mission *mission);

	//
	// These variables hold some culling related information
	//

	LinearMatrix worldToEyeMatrix;		// the current world to eye transform for our linked entity
	Scalar currentFrameTime;			// the time at the start of renderable execution

	//
	// These variables are used for tracking target frame time
	//
	unsigned long	total_cull,
					total_draw,
					total_pixelplanes,
					total_frame_time,
					target_frame_time,
					frame_count,
					target_frame_count;
	Logical statistics_started;
	Scalar report_time;
	//
	// The rest of the renderer's variables.
	//

	Time StartSample;				// For generating a framerate printout

	Logical fogUpdating,
			eyeRelative,			// True if the eye will be relative to the linked entity origin
			completeCycleNeeded;	// True when we are waiting for DPL to be ready for the next frame

	int myUniqueID,					// Generates a unique 16 bit id value for PSFX use
		lastAppState;				// For keeping track of what the application is doing

	float aspectRatio,				// aspect ratio of the screen
		FrameCount,					// For keeping track of framerate
		// Fog is setup as standard fog used for vehicles without simulated lights
		// and used when a vehicle with lights has them switched on.  This setting
		// is replicated into the "noSearchLight" setting so vehicles with lights
		// will always get this setting if the environment doesn't specifically say
		// that headlight simulation will work.
		fogRed,						// Fog setting to use when vehicle lights are on
		fogBlue,                    // This is the default for vehicles with no lights
		fogGreen,
		fogNear,
		fogFar,
		currentFogNear,
		currentFogFar,
		searchLightFogRed,			// Fog setting to use when vehicle lights are on
		searchLightFogBlue,
		searchLightFogGreen,
		searchLightFogNear,
		searchLightFogFar,
		noSearchLightFogRed,		// Fog setting to use when vehicle lights are off
		noSearchLightFogBlue,
		noSearchLightFogGreen,
		noSearchLightFogNear,
		noSearchLightFogFar,
		clipNear,					// Near clipping plane (from INI file)
		clipFar,					// Far clipping plane (from INI file)
		backgroundRed,				// Background color (from INI file)
		backgroundGreen,
		backgroundBlue,
		viewAngle,					// Viewing angle (from INI file)
		viewRatio;					// This is tan(viewAngle/2.0f) (handy for culling)

	dpl_DCS *dplTestEyeDCS;			// A DCS we temporarily hook our eye on (for testing)

	void MakeEntityRenderables(Entity *this_entity,	// The entity we are dealing with
			ResourceDescription	*model_resource,	// Pointer to the video resource
			ViewFrom type);							// Type of reference (inside/outside...etc.)

	dpl_VIEW *dplMainView;			// The DPL view we create at the beginning and use for our eye

	dpl_ZONE *dplDeathZone,			// The DPL zone that holds our vehicle (for death effect)
			*dplMainZone;			// The DPL zone that holds the rest of the world

	Reticle *vehicleReticle;		// Pointer to our vehicle's reticle if one exists.

	dpl_DCS *delayDCSFlushArray[DELAY_DCS_FLUSH_ARRAY_SIZE+1];
	int delayedDCSCount;
	INDIE_EFFECT myPSFXDescriptons[MAX_PSFX_COUNT];
	ParticleEmitter myPSFXEmitters[MAX_INDIE_EMITTERS];
	//
	//	This information is captured from the pickpoint when it is enabled
	//
	dpl_INSTANCE *dplHitInstance;
	dpl_DCS *dplHitDCS;
	dpl_GEOGROUP *dplHitGeoGroup;
	dpl_GEOMETRY *dplHitGeometry;
	//
	// This information holds the list of projectile renderables
	//
	SChainOf<InnerProjectileRenderable*> projectile_list;
	//
	// This holds the list of cached objects
	//
	TreeOf<DPLObjectCacheLine*, CString> dplObjectCacheSocket;
	//
	// This holds the list of jointed mover DCS tables
	//
	TableOf<DPLJointToDCSTranslator*, RegisteredClassMemoryAddress> dplJointToDCSTranslatorSocket;
	//
	// This chain contains the list of active renderables
	//
	SChainOf<Component*> dplRenderableSocket;

public:
	//
	// Store Lights for Later Revision
	//
//	dpl_LIGHT *ambientLight;
	D3DLIGHT9 *sceneLight;		// Array of all the lights

	dpl_DCS **sceneLightDCS;	// Array of DCS's for Lights

	int sceneLightCount;

	//static UserHeap *DPLHeap;
};

extern LPDIRECT3D9 gD3D;
