///////////////////////////////////////////////////////////////////////////////
/// @file deRender.hpp
///
/// @brief High-level Rendering interface
///
/// @author Assassin
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Feb 2002
/// @author Assassin
/// @remarks Initial Creation
///
///////////////////////////////////////////////////////////////////////////////

#ifndef DERENDER_HPP
#define DERENDER_HPP

#include "deGlobalTypes.hpp"
#include "deDriver.hpp"
#include "deMath.hpp"

#if defined(DERENDER_DLL_EXPORTS) || defined(DESTINY3D_EXPORT_ALL)
#	define DERENDER_API extern "C" DEDLL_EXPORT
#elif defined(DESTINY3D_STATIC_LINK)
#	define DERENDER_API extern "C"
#else
#	define DERENDER_API extern "C" DEDLL_IMPORT
#endif

#ifdef USING_DESTINY3D
#ifdef _DEBUG
#	ifdef DESTINY3D_STATIC_LINK
#		pragma comment(lib, "deRender_sd")
#	else
#		pragma comment(lib, "deRenderd")
#	endif //DESTINY3D_STATIC_LINK
#else
#	ifdef DESTINY3D_STATIC_LINK
#		pragma comment(lib, "deRender_s")
#	else
#		pragma comment(lib, "deRender")
#	endif //DESTINY3D_STATIC_LINK
#endif //_DEBUG
#endif //USING_DESTINY3D


class IdePortal;
class IdeCamera;
class IdeWorld;
class Ide2DCollection;
class IdeRender;
class IdeSurface;
class IdeVertexBuffer;
class IdeRenderTexture;
class IdeRenderMaterial;
class IdeRenderLight;
class IdeSceneLight;
class IdeSurface;

// factory functions
/// initialize the Render system
DERENDER_API deBoolean IdeRender_Initialize();
/// get a pointer to an IdeRender singleton.
DERENDER_API IdeRender* IdeRender_GetPtr();
/// destroy the IdeRender singleton
DERENDER_API void IdeRender_Destroy();
/// release all drivers held by IdeDriver
DERENDER_API void IdeRender_DestroyDriverLinkages();

typedef IdeRender* (*fIdeRender_GetPtr)();
typedef void (*fIdeRender_Destroy)();
typedef void (*fIdeRender_DestroyDriverLinkages)();

#define DERENDER_MAX_LIGHTS (32)

/// Interface to a high-level rendering API.
/// IdeRender interfaces with the deScene, deDriver, and de2D modules to handle
/// rendering of 3D scenes with 2D composite overlays.
/// Related functions: IdeRender_GetPtr.
//class IdeRender
DE3D_INTERFACE_(IdeRender)
{
protected:
	virtual ~IdeRender(void) {}
public:

	/// flags for indicating rendering styles
	enum RenderFlag_t
	{
		Render_Dot = 0,		///< render dots (points)
		Render_Mesh,		///< render wireframe meshes
		Render_Fill,		///< render solid-filled triangles (default value)
		Render_FillAndMesh,	///< render solid-filled with wireframe on top
		Render_DepthMesh,	///< render only visible wireframe pieces
		Render_CullCW,		///< cull faces with clockwise vertex winding (default value)
		Render_CullCCW,		///< cull faces with counter-clockwise vertex winding
		Render_CullNone,	///< cull no faces according to vertex winding
		Render_NoWaterMark,
		Render_WaterMark,
		Render_WaterMarkFade,
		Render_MaxFlag,
		Render_Force32 = 0x7FFFFFFF
	};

	/// flag for setting render-states in a group
	// this is really a hack and should be superceded by the shader system
	enum StencilType_t
	{
		Stencil_Disable = 0,///< No stenciling comparisons or operations
		Stencil_DrawOn,		///< render only when stencil value is Ref. Overloads Comparison to EQUAL if ALWAYS is specified
		Stencil_SetTo,		///< set rendered pixels to stencil value Ref
		Stencil_Invert,		///< invert stencil value on rendered pixels
		Stencil_Increment,	///< increment rendered pixels' stencil values, and wrap if X is non-zero
		Stencil_Decrement,	///< decrement rendered pixels' stencil values, and wrap if X is non-zero
		Stencil_UseColor,	///< not a stencil state, call with boolean value in Ref for color buffer rendering
		Stencil_MaxFlag,
		Stencil_Force32 = 0x7FFFFFFF
	};

	// another hack, this will be superceded by IdeSurface
	struct deRenderSurface
	{
		deBoolean			Transparency;
		IdeRenderMaterial*	Material;
		IdeRenderTexture*	Texture;
		short				TexBaseIndex, TexDetailIndex;
		short				BumpBaseIndex, BumpDetailIndex;
		short				SpecMapIndex, EmissiveIndex;
		// insert shader entries here
		void*				VProgram;
		void*				DiffuseShader;
		void*				TextureShader;
		void*				SpecShader;
	};

	/// a structure used by the scenegraph to have geometry rendered
	struct deRenderObject
	{
		IdeVertexBuffer*	VB;
		IdeVertexBuffer*	IndexOverride;
		long				IndexOffset;
		IdeSurface*			pSurface;
		deBoolean			isSky;
		deTransformInfo		Transform;
		IdeSceneLight*		Lights[DERENDER_MAX_LIGHTS];
		// put in info about base texture, bumpmap, specmap, etc
		// update: forget that, it's all going into the shader/surface system
		deRenderSurface		SurfaceDetail; // deprecated by IdeSurface, remove soon
	};

	/// Structure keeping statistical information about a rendered frame.
	/// This information can be useful for optimizing your application and content.
	struct deRenderStats
	{
		/// information extracted from the driver
		IdeDriver::deDriverStats	DriverStats;
		/// count of how many things were rendered
		long VertexRenders, IndexRenders, PrimitiveRenders; 
		/// count of items regarding vertex buffers
		long VBufferRenders, VBufferUpdates, VBufferSubRenders;
		/// total number of vertex buffers used (and number that had indexed data)
		long VBuffersTotal, VBuffersIndexed;
		/// number of textures used
		long TexturesTotal;
		/// number of texture switches
		long TexturesChanged;
		/// number of material changes during the frame
		long MaterialsChanged;
		/// number of bitmaps total and in relation to a vertex buffer
		long BitmapsTotal, MinBitmapsPer, MaxBitmapsPer;
		/// number of lights per vertex buffer
		long MinLightsPer, MaxLightsPer;
		/// each index stores the number of vertices rendered with that number of lights.
		/// ie, LightsVertNum[3] is the number of vertices rendered with 3 lights affecting them.
		long LightsVertNum[9];
		/// the scene may be rendered more than one time to optimize certain things
		long RenderPasses, VertexPasses;
		/// the frustum far plane distance used for rendering
		deFloat FarPlaneDistUsed;
		// perhaps a bit of info about # of pixels Cleared
	};

	/// set a flag for the next call to Render to use.
	/// see IdeRender::RenderFlag_t for notes on usage
	virtual deBoolean SetRenderFlag(IdeRender::RenderFlag_t Flag) = 0;

	/// Set a pre-defined stencil method.
	/// notes on how stenciling works and what it does:
	/// <br>(StencilRef & StencilMask) RenderCmpFunc (StencilBufferValue & StencilMask)<br>
	/// That is the comparison performed for each pixel drawn when stenciling is enabled.
	/// The StencilRef value is compared with the StencilBufferValue, using a comparison function
	/// to evaluate success or failure of the test. If the stencil test fails, the operation for
	/// STENCILFAIL will be performed. If the stencil test passes and the depth-test fails, the
	/// operatation for STENCILZFAIL will be performed. If both the stencil and depth tests pass,
	/// the operation for STENCILPASS will be performed.
	/// <br>If modifying values in the stencil buffer, the write-mask is applied to the bits being
	/// altered. Using the StencilMaks and WriteMask, you can effectively store more than one useful
	/// "channel" of stencil information and modify it. However, there is usually a maximum of 8 bits
	/// available for use in stenciling, so not too much detail can be stored if the stencil channel
	/// is being "shared" in such a way. For Destiny3D, the aim is to perform stenciled lighting
	/// and stenciled portaling on the same geometry by sharing out the stencil buffer in this
	/// manner.
	/// @param pDriver valid render-capable driver
	/// @param Stencil any valid value of StencilType_t
	/// @param Ref the StencilRef value
	/// @param X a special-use flag argument, used with some stencil types to specify behavior
	/// @param Comparison a comparison function for comparing current stencil value to reference value.
	///                   see IdeDriver documentation for notes on how stenciling works
	/// @param StencilTest the test for which the operation will be performed. Default is STENCILPASS,
	///                    can also be STENCILFAIL or STENCILZFAIL
	/// @param StencilMask the stenciling read-mask
	/// @param WriteMask the stenciling write-mask
	virtual deBoolean SetStencilState(	IdeDriver * pDriver, StencilType_t Stencil, DWORD Ref, DWORD X,
										IdeDriver::RenderCmpFunc Comparison = IdeDriver::COMPARE_ALWAYS,
										IdeDriver::RenderState StencilTest = IdeDriver::RENDER_STENCILPASS,
										DWORD StencilMask = 0xff, DWORD WriteMask = 0xff) = 0;

	/// Render a scene from a given vantage point, possibly doing some cleanup work with optional params
	/// @param cam a camera containing details of how to render the scene
	/// @param DeltaTime time elapsed since the last call to Render
	/// @param UseSG set to true if the scenegraph is to be traversed
	/// @param ClearColorBuffer (optional, default false) if true, the color buffer will be cleared to the stored BGColor
	/// @param BeginFrame (optional, default false) will call BeginFrame(...) for you
	/// @param EndFrame (optional, default false) will call EndFrame(...) for you
	virtual deBoolean Render(IdeCamera * cam, deDouble DeltaTime, deBoolean UseSG = deTRUE, deBoolean ClearColorBuffer = DE_FALSE, deBoolean BeginFrame = DE_FALSE, deBoolean EndFrame = DE_FALSE) = 0;
	/// Just like Render() except instead of rendering to the screen, it renders to a texture.
	/// the texture to be used is handled through the Target parameter, and the texture on the video
	/// hardware after the render will not match the bitmap in system memory. BeginFrame should
	/// still be called before this method, so that statistics are properly handled. The camera's
	/// viewport will be used when rendering to the texture, so the camera viewport must be the 
	/// correct size for the texture being rendered into. However, the aspect ratio of the camera can
	/// be locked to something other than width/height so that the texture can be mapped in a
	/// way that doesn't require it to be viewed as square.
	/// @param Target The bitmap which acts as the handle to the texture to be rendered into.
	///               This bitmap must have been added to the driver after having been set to
	///               have render-target capability.
	virtual deBoolean RenderTexture(IdeBitmap * Target, IdeCamera * cam, deDouble DeltaTime) = 0;
	/// begin a frame before rendering geometry.
	/// @param WindowHandle the window with an attached driver to be used
	/// @param ClearColorBuffer (optional, default true) if true, the color buffer will be cleared to the stored BGColor
	virtual deBoolean BeginFrame(HWND WindowHandle, deBoolean ClearColorBuffer = DE_TRUE) = 0;
	/// end a frame and present the back-buffer to the screen
	/// @param WindowHandle the window with an attached driver to be used
	/// @param DeltaTime time elapsed since the last call to EndFrame
	virtual deBoolean EndFrame(HWND WindowHandle, deDouble DeltaTime) = 0;

	// for use by scenegraph only
	virtual IdeDriver * CurrentPassDriver() = 0;
	virtual void RenderPrepBuffer(deRenderObject & obj) = 0;
	virtual void RenderPrepBatch(long Level, IdePortal * Portal, deRect & Viewport, deTransformInfo * PortalPos, deTransformInfo * Concat) = 0;
	virtual deDouble GetTimeStep() = 0;

	/// set the relative path that contains Destiny3D driver files (defaults to "." which is current directory)
	virtual void SetDriverPath(const char * RelativePath) = 0;
	/// returns a pointer to internally-held IdeDriverLoad interface
	virtual IdeDriverLoad* GetDriverLoad() = 0;
	/// attach a driver to the specified window. The driver will not be ready for rendering
	/// @return a valid IdeDriver pointer if success, NULL if failure
	/// @param hWnd a valid window handle to attach a driver to
	/// @param DriverName (optional, defualt = "Direct3D8") string containing the name of driver to use
	virtual IdeDriver * AttachToWindow(HWND hWnd, char * DriverName = "Direct3D8") = 0;
	/// detach and destroy a driver attached to the specified window
	virtual deBoolean DetachFromWindow(HWND hWnd) = 0;
	/// update the driver's knowledge of the window's client rect, ie after a WM_SIZE message is received
	virtual void UpdateWindow(HWND hWnd) = 0;
	virtual void UpdateDisplay() = 0;

	/// load a driver up and prepare it for rendering
	/// @return true if successful, false if failure to use specified format
	/// @param hWnd a valid window handle to attach a driver to
	/// @param Display user-filled driver settings to be used in initializing the driver.
	/// @param DriverName (optional, defualt = "Direct3D") string containing the name of driver to use
	virtual deBoolean LoadDriver(HWND hWnd, IdeDriver::deDisplay * Display, char * DriverName = "Direct3D8") = 0;
	/// check a driver-enumerated resolution
	/// @return false if pDriver is NULL or if Adapter or EntryNum are outside valid ranges, true otherwise
	/// @param Width pointer to integer to store width of entry into
	/// @param Height pointer to integer to store height of entry into
	/// @param RefreshRate pointer to integer to store screen refresh rate (in hertz) of entry into
	virtual deBoolean GetResolution(IdeDriver * pDriver, long Adapter, long EntryNum, long * Width, long * Height, long * RefreshRate) = 0;
	/// a rather high-level method of finding a format close to what you want for z-buffer.
	/// @pre Adapter, Bpp, and Windowed variables in Display set to appropriate values.
	///      ZDepth and StencilDepth values in Display set to minimum acceptable bit-depth values.
	/// @post Display contains a valid combination of values to call LoadDriver() with.
	///       The user should check the values of ZDepth, StencilDepth, and Zpp upon failure,
	///       so that he can adjust game settings to what is possible.
	/// @return true if a format was found that satisfies criteria set, false if criteria could not be met.
	/// @param pDriver pointer to a valid IdeDriver interface
	/// @param Display pointer to a IdeDriver::deDisplay structure meeting the pre-requisites
	/// @param PreferStencil upon failure to find a good format, satisfy stencil buffer needs before depth-buffer needs (if true)
	virtual deBoolean QueryFormat(IdeDriver * pDriver, IdeDriver::deDisplay * Display, deBoolean PreferStencil = deFALSE) = 0;

	/// set the color that the color-buffer will be cleared to.
	/// ie, 0x00102030 will set the color to 16 red intensity, 32 green intensity, and 48 blue intensity
	/// out of a maximum intensity of 255
	virtual void SetBGColor(deARGB color) = 0;
	/// set the maximum depth of T-portal progression.
	/// Each T-Portal clears a portion of the screen as part of the rendering process,
	/// so using a high number can adversely affect your framerate. Default value = 8.
	virtual void SetMaxTPortalDepth(long MaxDepth = 8) = 0;
	virtual void SetMaxLights(long MaxHWLights = 32, long MaxTexLights = 4) = 0;
	/// clear the entire color-buffer to a specified color, possibly also clearing the Z-buffer
	virtual deBoolean ClearBackground(HWND hWnd, deARGB color = 0x00000000, deBoolean ClearZ = DE_TRUE) = 0;
	/// enable or disable texturing on all geometry rendered through this interface
	virtual void EnableTexturing(deBoolean Enabled) = 0;

	/// Specify a user-handled 2D collection for a driver attached to specified window to use.
	/// This method invokes Collection->Claim(), and then Release() on its currently held collection.
	virtual deBoolean Use2DCollection(HWND hWnd, Ide2DCollection * Collection) = 0;
	/// Get the 2D collection that is being used by the driver attached to specified window.
	/// This method does NOT invoke Claim() on the returned collection, but user code may do so.
	virtual Ide2DCollection * Get2DCollection(HWND hWnd) = 0;

	/// call before BeginFrame with a user-handled instance of the structure to be filled
	/// with information. the data will be valid after a call to Render() or EndFrame()
	virtual void SetStatStruct(deRenderStats * StatStruct) = 0;

	virtual deBoolean Screenshot(HWND hWnd, const char* filename, long ScreenSizes = 1) = 0;

	/// get the current frame ID number.
	/// clamped to [0 <-> 2^31-1]
	virtual long GetFrameNum() = 0;
};

#endif
