/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


#ifndef	__NBASERENDERER__
#define	__NBASERENDERER__


		#include "core/renderable.h"
		#include "core/material.h"
		#include "timing/benchmark.h"
		#include "data/registry.h"
		#include "geometry/frustum.h"
		#include "geometry/rect.h"
		#include "plugin/plugin_manager.h"
		#include "async/job_perf.h"


namespace GS {
	namespace Render {
		class RasterFont;
		struct IEnvironment;

/// Video mode settings.
enum	VideoMode
{
	VideoWindowed = 0,
	VideoFullscreen,
	VideoWindowedQuiet,				///< Open windowed output but do not automatically show the window.
    VideoRaw,

	VideoGenericPAL,				///< Used by console systems.
	VideoGenericNTSC
};

///
struct	Window
{
		/// Get handle.
virtual	void		*GetHandle() const = 0;
		/// Get render window size.
virtual	void		GetSize(uint &w, uint &h) = 0;

virtual	~Window() {}
};

/*!
	@short	Renderer base class.

	This the base that has to be derived to add support for a new renderer.
	For example implementations see NullRenderer or GPURenderer.
	nNull_Renderer can be used as a starting point in implementing a
	new renderer.

	This class also implements all functions required to maintain the minimum
	concept of viewport used by the engine.

	@see S3D::Scene::SetSceneInterface().

	@author Emmanuel Julien (ejulien@gsworks.fr)
*/
class	Renderer : public RegistryListener
{
public:

		NPLACEMENT_NEW(Renderer)

		/// Return the plugin class.
static	const char		*GetPluginClass() { return "Renderer"; }
		/*!
			@short Return the plugin version.

			@note This version number must be increased whenever a modification
			is made to the base interface in order to prevent incompatible
			plugin from being loaded.
		*/
static	uint			GetPluginVersion() { return 1; }

/*!
	@name	Resource factory.
	@{
*/
virtual	Geometry	*NewGeometry(const char *name = 0) = 0;
virtual	Material	*NewMaterial(const char *name = 0) = 0;
virtual	Texture		*NewTexture(const char *name = 0) = 0;
virtual	Shader		*NewShader(const char *name = 0) = 0;
/// @}

		/// Clear function
enum	ClearFunction
{
	ClearColor		=	(1 << 0),
	ClearDepth		=	(1 << 1),
	ClearAll		=	~0
};

		/*!
			@short	Geometry updater control word.
			@see	UpdateGeometry().
		*/
enum	GeoUpdate
{
	GEOUPDATE_NONE = 0,
	GEOUPDATE_IDX,
	GEOUPDATE_VTX,
	GEOUPDATE_VNRM,
	GEOUPDATE_PNRM,
	GEOUPDATE_SOFTVTX,
	GEOUPDATE_RGB
};

/// Renderer statistics.
struct	Statistics
{
		String			adapter,
						vendor;

		uint			queue_pass,				///< Number of queue pass rendered.
						light_processed,		///< Number of light processed.

						renderable_processed,	///< Number of renderable processed.
						renderable_drawn,		///< Number of renderable drawn.
						list_drawn,				///< Number of display list drawn.
						triangle_drawn,			///< Number of triangles drawn.

						texture_memory,			///< Texture memory in use.
						geometry_memory;		///< Geometry memory in use.

		ASync::JobPerf	bench_prepare_light;

		Benchmark		bench_post_process,
						bench_render;

		/// Reset statistics.
		void			Reset()
		{
			queue_pass = 0;
			light_processed = 0;

			renderable_processed = 0;
			renderable_drawn = 0;
			list_drawn = 0;
			triangle_drawn = 0;

			bench_post_process.Reset();
			bench_prepare_light.Reset();
			bench_render.Reset();
		}

		Statistics()
		{
			texture_memory = 0;
			geometry_memory = 0;

			Reset();
		}
};

		/// Reload managed resources on context change.
virtual	void			OnContextChanged(ResourceFactory *) {}
		/// Get current adapter ideal triangle batch size.
virtual	uint			GetTriangleIdealBatchSize() const { return 16384; }

/*!
	@name	Renderer registry.
	@{
*/
		Registry	registry;

virtual	RegistryRValue ProcessMessage(RegistryMessage, const Registry *, const void *parm) { return RegistryReturn_Ok; }
/*!
	@}
	@name	Renderer queue system.
	@{
*/
		List <Core::Renderable *> renderable_list;

		/// Delete the renderable list.
		void			DeleteRenderableList();
		/// Push a renderable to the renderable list.
		void			PushRenderable(Core::Renderable *);

		/*!
			@short	Build a renderable primitive list.

			A separate lod view can be provided to ensure selection of the
			correct geometry lod when rendering derived views such as shadow
			maps.
		*/
		uint			BuildRenderablePrimitiveList(const Core::Camera &view, const Core::Camera &lod_view, Stack <Primitive *> &, Core::Renderable::Context = Core::Renderable::Context_Default) const;
/// @}

protected:

		String			error;					///< Last error description.

		/// Request lights to the manager and setup lighting.
		void			RequestAndSetupLighting(Vector4 &pos);

		// Renderer state.
		bool			fullscreen;				///< Windowed or fullscreen?

		float			output_aspect_ratio;	///< Output aspect ratio override (default: -1 for no override)

		float			global_aspect_ratio;	///< Global aspect ratio.

		Core::Camera	*view_item;				///< Current view item.
		Registry		*view_registry;			///< Current view registry.
		Frustum			frustum;				///< Current frustum.

		sTexture		output_texture;			///< Frame buffer output texture.

		List <Window *>	windows;

		Window			*output_window;
		Window			*default_window;

public:
		tVector2 <uint>	dimensions;				///< Output dimensions.
		tVector2 <uint> ideal_render_system_vr_resolution;

		tVector2 <uint> saved_ideal_render_system_vr_resolution;

		float ipd;
		bool use_fix_camera;

		Window*		GetOutputWindow(){ return	output_window; }

		// Renderer statistics.
		Statistics		stats;

		/// Set output texture.
virtual	void			SetOutputTexture(Texture *texture) { output_texture = texture; }

		/// Get output dimensions.
		tVector2 <uint>	GetOutputDimensions() const;

		/// Return the output aspect ratio.
		float			GetOutputAspectRatio() const;
		/// Set the output aspect ratio override (0 to disable), returns the previous aspect ratio.
		float			SetOutputAspectRatio(float k = 0) { float v = output_aspect_ratio; output_aspect_ratio = k; return v; }

		/// Get renderer global aspect ratio.
		float			GetGlobalAspectRatio() const { return global_aspect_ratio; }
		/// Set renderer global aspect ratio, returns the previous aspect ratio.
		float			SetGlobalAspectRatio(float k = 1.f) { float v = global_aspect_ratio; global_aspect_ratio = k; return v; }

		/// Is the renderer fullscreen.
		bool			isFullscreen() const { return fullscreen; }

virtual	void			ResetStatistics() { stats.Reset(); }

		/// Display on-screen statistics.
virtual	void			DrawProfilerText(RasterFont *[2], float &x, float &y);

		/// Return the current camera.
		Core::Camera	*GetCamera() const;
		/// Set the current viewing item.
		void			SetCamera(Core::Camera *);
		/// Apply view.
virtual	void			ApplyCamera();

		/// Set view registry.
		void			SetViewRegistry(Registry *);

		/*!
			@short	Interface used by the renderer to query environment.
			@see	SetEnvironmentInterface().
		*/
		IEnvironment	*environment_interface;
		/// Set the scene interface.
		void			SetEnvironmentInterface(IEnvironment *itf) { environment_interface = itf; }

/*!
	@name Renderer core.
	These are the functions to be reimplemented by a capable renderer.
	@{
*/
		/// Start a new frame.
virtual	bool			BeginDrawList() = 0;
		/// End current frame, draw to back-buffer.
virtual	void			EndDrawList() = 0;

		/// Returns the renderer's name as a C string.
virtual	const char		*GetName() const = 0;
		/// Returns a C string describing the renderer.
virtual	const char		*GetDescription() const = 0;
		/// Returns a C string containing the renderer version number.
virtual	const char		*GetVersion() const = 0;
		/// Returns a C string containing the last error description.
virtual	const char		*GetError() const { return error; }

		/// Open video.
virtual	bool			Open(uint width, uint height, char bpp = 32, VideoMode = VideoWindowed, const void *sys_handle = 0) = 0;
		/// Free all renderer resources.
virtual	void			Free() {}
		/// Close video.
virtual	void			Close() {}

		/// Setup core resources.
virtual	bool			SetupCoreResources(bool support_3d = true) { return true; }

		/// Explicitly resize video output.
virtual	bool			ResizeVideo(uint width, uint height) = 0;
		/// Switch to/from fullscreen.
virtual	void			SetFullscreen(bool = true) = 0;
		/// Refresh display output.
virtual	void			Refresh() = 0;
		/// Return a pointer to the current subsystem window output.
virtual	void			*GetCurrentSystemWindowHandle() const = 0;

		/// Setup an extra window to use as renderer output.
virtual	Window			*NewOutputWindow(const void *) = 0;
		/// Set the output window.
virtual	void			SetOutputWindow(Window * = 0) = 0;
		/// Free a rendering window.
virtual	void			FreeOutputWindow(Window *) = 0;

		/// Render the display queue.
virtual	void			RenderList() = 0;
		/// Request that the frame be displayed to current output window.
virtual	void			ShowFrame() = 0;

		/// Clear the render target.
virtual	void			Clear(float r, float g, float b, float a = 1.f, float z = 1.f, ClearFunction = ClearAll) = 0;
		/// Grab the frame buffer to a texture.
virtual	void			GrabDisplay(Texture *) = 0;
		/// Grab the frame buffer to a picture.
virtual	void			GrabDisplay(Picture &) = 0;

virtual void RenderFrame() {};



		/// Set a texture as render target.
virtual	void			SetRenderTarget(Texture *) = 0;

		/// Draw line.
virtual	void			DrawLine(uint count, const Vector4 *, const Color * = 0, Core::Material::BlendOperator = Core::Material::Blend_None, Core::Material::RenderWord = Core::Material::Render_None, Shader * = 0) = 0;
		/// Draw triangle.
virtual	void			DrawTriangle(uint count, const Vector4 *, const ushort *idx = 0, const Color * = 0, const Vector2 * = 0, const Texture * = 0, Core::Material::BlendOperator = Core::Material::Blend_None, Core::Material::RenderWord = Core::Material::Render_None, Shader * = 0) = 0;
		/// Draw sprite.
virtual	void			DrawSprite(uint count, const Vector4 *, const Color * = 0, const float *size = 0, const Texture * = 0, float global_size = 1.f, Core::Material::BlendOperator = Core::Material::Blend_None, Core::Material::RenderWord = Core::Material::Render_None, Shader * = 0) = 0;

//
struct	WriterConfig
{
	bool		correct_ar;		///< Enforce aspect ratio.
	bool		normalized;		///< Normalized/absolute coordinates.

	WriterConfig(bool normd = true, bool ar = true) : normalized(normd), correct_ar(ar) {}
};

enum	WriterAlignment
{
	AlignLeft = 0,
	AlignMiddle,
	AlignRight
};

		/// Output string to screen.
virtual	void			Write(const RasterFont &, const char *, float &x, float &y, const WriterConfig &, float s = 1, const Color * = 0, WriterAlignment = AlignLeft, bool mirrored = false) = 0;

		/// Set the view matrix.
virtual	void			SetViewMatrix(const Matrix4 &, const Matrix4 *inverse = 0) = 0;
		/// Set the projection matrix.
virtual	void			SetProjectionMatrix(const Matrix4 &) = 0;
		/// Set the world matrix.
virtual	void			SetWorldMatrix(const Matrix4 &, const Matrix4 *inverse = 0) = 0;

		/// Clear the user clipping plane.
virtual	void			ClearClippingPlane() = 0;
		/// Set the user clipping plane.
virtual	void			SetClippingPlane(const Vector4 &p, const Vector4 &n) = 0;

		/// Set clipping rect.
virtual	void			SetClippingRect(const fRect *) = 0;
		/// Get clipping rect.
virtual	fRect			GetClippingRect() const = 0;

		/// Set viewport.
virtual	void			SetViewport(const fRect &) = 0;
		/// Get viewport.
virtual	fRect			GetViewport() const = 0;
/// @}

		bool			FromMetaTag(NML::Tag &);
		NML::Tag		*AsMetaTag();

		Renderer();
virtual	~Renderer();
};

typedef PluginManager <Renderer> RendererPluginManager;

	} // Render
} // GS


#endif	// __NBASERENDERER__
