//	Drawer.h - Base class for any subclasses used to draw to screen.
//  ----------------------------------------------------------------------------
//	This file is part of 'NiallsAVLib', base code for any kind of audiovisual
//	apps.
//	Copyright (C) 2012  Niall Moody
//	
//	This program 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 3 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.
//	----------------------------------------------------------------------------

#ifndef DRAWER_H_
#define DRAWER_H_

#include "TwoFloats.h"
#include "FontAtlas.h"
#include "TextLayout.h"
#include "ThreeFloats.h"
#include "AppSettings.h"
#include "TextureAtlas.h"

#include <vector>
#include <stdint.h>

///	Base class for any subclasses used to draw to screen.
class Drawer
{
  public:
	///	Constructor.
	Drawer() {};
	///	Destructor.
	virtual ~Drawer() {};

	///	Any initialisation goes here.
	/*!
		\param screenWidth The width of the window we have to draw into.
		\param screenHeight The height of the window we have to draw into.
		\param appSettings This can be used to determine the dimensions of the
		area we're actually drawing to (which won't necessarily be the same as
		the screen dimensions above, e.g. you might have drawing dimensions of
		1280x720 and a window of 800x450).
	 */
	virtual void initialise(int screenWidth,
							int screenHeight,
							const AppSettings& appSettings) = 0;

	///	Used to replace the font renderer for this drawer.
	virtual void setFontRenderer(FontAtlas::Creator& renderer) = 0;

	///	Loads a single image from its raw(decoded) image data.
	/*!
		\param data The image data to be loaded.
		\param width The width of the image.
		\param height The height of the image.
		\param name This image's name. This name will be used to draw this image
		via drawImage() etc.

		Images passed in should be in 32-bit RGBA format.  The image data will
		be deleted by the Drawer.
	 */
	virtual void loadSingleImage(uint8_t *data,
								 int width,
								 int height,
								 const std::wstring& name) = 0;
	///	Loads a texture atlas from its raw(decoded) image data.
	/*!
		\param data The image data to be loaded.
		\param width The width of the image.
		\param height The height of the image.
		\param atlas The atlas data (how many images it contains, etc.).

		Images passed in should be in 32-bit RGBA format.  The image data will
		be deleted by the Drawer.

		Each image in the TextureAtlas will have its own name (generated from its
		filename by AVLibResourceGenerator), which can be used to draw it via
		drawImage() etc.
	 */
	virtual void loadTextureAtlas(uint8_t *data,
								  int width,
								  int height,
								  const TextureAtlas& atlas) = 0;
	///	Releases the image associated with the passed-in name.
	/*!
		Note: If you pass in the name of an image within a texture atlas, the
		entire atlas will be released from memory, meaning any other images
		which were part of that atlas will no longer be available.
	 */
	virtual void releaseImage(const std::wstring& name) = 0;

	///	Loads a shader.
	/*!
		\param vertex Text of the vertex shader.
		\param fragment Text of the frament shader.
		\param name The identifier to associate with this shader.

		\return True if the shader was loaded successfully.
	 */
	virtual bool loadShader(const std::wstring& vertex,
							const std::wstring& fragment,
							const std::wstring& name) = 0;
	///	Associates a shader parameter (uniform) with the current shader.
	/*!
		\param name The name by which you can access the uniform both within
		the shader and via setShaderParam().

		Note that this requires you to first call useShader() to load the
		shader you're interested in.
	 */
	virtual void registerShaderParam(const std::wstring& name) = 0;
	///	Releases the named shader.
	virtual void releaseShader(const std::wstring& name) = 0;

	///	Loads a truetype font.
	/*!
		\param name The filename of the font (minus its extension).
		\param size The desired size of the font.
		\param identifier String used to identify the font in future
		drawText() calls.

		This will render the font into a texture atlas which can then
		be used to render text.
	 */
	virtual void loadFont(const std::wstring& name,
						  float size,
						  const std::wstring& identifier) = 0;
	///	Releases the named font.
	virtual void releaseFont(const std::wstring& identifier) = 0;

	///	To be called at the start of a drawing frame.
	virtual void beginDrawing() = 0;
	///	To be called at the end of a drawing frame.
	virtual void endDrawing() = 0;

	///	Returns the contents of the screen as a buffer of pixels.
	/*!
		\param pixelBuffer The buffer to write the pixel data into. It's
		dimensions will be set to:
		<width of the screen> x <height of the screen> x 3.

		(i.e. each pixel is 3 bytes, RGB)

		Make sure you call this after endDrawing().
	 */
	virtual void getScreenPixels(std::vector<uint8_t>& pixelBuffer) = 0;

	///	Activates a shader to be used for the following operations.
	/*!
		\param shader The identifier of the shader loaded via loadShader().
		Passing in an empty string will revert to the fixed function
		pipeline.
	 */
	virtual void useShader(const std::wstring& shader) = 0;
	///	Sets a shader parameter (uniform) for the current shader.
	virtual void setShaderParam(const std::wstring& param, float val) = 0;

	///	The possible blend modes.
	enum BlendMode
	{
		AlphaBlend = 0,
		Additive
	};
	///	Class used to implement custom BlendModes.
	class CustomBlendMode
	{
	  public:
		///	Destructor.
		virtual ~CustomBlendMode() {};

		///	Implement this to create your own blend mode.
		virtual void setBlendMode() = 0;
	};
	///	Sets the blend mode to use.
	/*!
		\param val Which blending mode to switch to. This can be one of the
		values in BlendMode, or (if you've registered your own blend mode),
		an arbitrary integer matching a custom blend mode.
	 */
	virtual void setBlendMode(int val) = 0;
	///	Registers an additional blend mode.
	/*!
		\param identifier The identifier used to switch to this blend mode
		in setBlendMode().
		\param mode The custom blend mode.
	 */
	virtual void registerCustomBlendMode(int identifier,
										 CustomBlendMode *mode) = 0;

	///	Draws an image at the desired position.
	/*!
		\param name The identifier of the image to draw.
		\param position The position to draw the image at.
		\param size The amount to scale the image by.
		\param colour How to tint the image.
		\param alpha How to fade the image in/out.
	 */
	virtual void drawImage(const std::wstring& name,
						   const TwoFloats& position,
						   const TwoFloats& size = TwoFloats(1.0f, 1.0f),
						   const ThreeFloats& colour = ThreeFloats(1.0f, 1.0f, 1.0f),
						   float alpha = 1.0f) = 0;
	///	Draws a rotated image at the desired position.
	/*!
		\param name The identifier of the image to draw.
		\param position The position to draw the image at.
		\param angle The angle to rotate the image by (degrees).
		\param pivot The pivot point of the rotation (0->1 w/in the image).
		\param size The amount to scale the image by.
		\param colour How to tint the image.
		\param alpha How to fade the image in/out.
	 */
	virtual void drawRotImage(const std::wstring& name,
							  const TwoFloats& position,
							  float angle,
							  const TwoFloats& pivot,
							  const TwoFloats& size = TwoFloats(1.0f, 1.0f),
							  const ThreeFloats& colour = ThreeFloats(1.0f, 1.0f, 1.0f),
							  float alpha = 1.0f) = 0;
	///	Draws part of an image at the desired position.
	/*!
		\param name The identifier of the image to draw.
		\param position The position to draw the image at.
		\param subPos The position of the sub-image within the image (0->1).
		\param subSize The size of the sub-image within the image (0->1).
		\param size The amount to scale the image by.
		\param colour How to tint the image.
		\param alpha How to fade the image in/out.
	 */
	virtual void drawSubImage(const std::wstring& name,
							  const TwoFloats& position,
							  const TwoFloats& subPos,
							  const TwoFloats& subSize,
							  const TwoFloats& size = TwoFloats(1.0f, 1.0f),
							  const ThreeFloats& colour = ThreeFloats(1.0f, 1.0f, 1.0f),
							  float alpha = 1.0f) = 0;
	///	Draws a single image tiled multiple times within the desired rect.
	/*!
		\param name The identifier of the image to tile with.
		\param position The position to draw the rect at.
		\param size The size of the rect to fill with the tile image (in
		pixels; note the difference wrt drawImage() ).
		\param tilePos The position within the texture that the image should
		start at.
		\param tileNum The number of times the image should be tiled.
		\param colour How to tint the image.
		\param alpha How to fade the image in/out.

		Note: this method will only work correctly if it is used with a
		non-atlas-ed image with power-of-two dimensions.
	 */
	virtual void drawTiledImage(const std::wstring& name,
							    const TwoFloats& position,
							    const TwoFloats& size,
								const TwoFloats& tilePos,
							    const TwoFloats& tileNum,
							    const ThreeFloats& colour = ThreeFloats(1.0f, 1.0f, 1.0f),
							    float alpha = 1.0f) = 0;
	///	Draws a string of text with the selected font.
	/*!
		\param text The text to draw.
		\param font The identifier of the font to draw the text with.
		\param position The position to start drawing at.
		\param boxWidth The width of the box to draw the text within.
		\param justification How the text should be justified.
		\param size How to scale the font.
		\param colour The colour to tint the text (black by default).
		\param alpha How to fade the text in/out.
	 */
	virtual void drawText(const std::wstring& text,
						  const std::wstring& font,
						  const TwoFloats& position,
						  float boxWidth,
						  TextLayout::Justification justification = TextLayout::Left,
						  float size = 1.0f,
						  const ThreeFloats& colour = ThreeFloats(0.0f, 0.0f, 0.0f),
						  float alpha = 1.0f) = 0;
	///	Draws a string of text with the selected font, caching the TextLayout.
	/*!
		\param text The text to draw.
		\param font The identifier of the font to draw the text with.
		\param layout The TextLayout to lay the text out with. If this is empty,
		it will be filled with the passed-in text; otherwise the text in the
		existing layout will be drawn to the screen verbatim.
		\param position The position to start drawing at.
		\param boxWidth The width of the box to draw the text within.
		\param justification How the text should be justified.
		\param size How to scale the font.
		\param colour The colour to tint the text (black by default).
		\param alpha How to fade the text in/out.

		This version of drawText() is for text strings which don't change
		frequently. Because laying out text is a relatively costly operation,
		you probably don't want to be doing it for every frame if you can
		avoid it.

		As such, this method allows you to cache the TextLayout for a particular
		string. The first time the method is called with a particular string,
		the passed-in TextLayout will be filled with the string's layout. From
		then on though, the TextLayout's layout will be drawn straight to
		screen without being updated.
	 */
	virtual void drawText(const std::wstring& text,
						  const std::wstring& font,
						  TextLayout& layout,
						  const TwoFloats& position,
						  float boxWidth,
						  TextLayout::Justification justification = TextLayout::Left,
						  float size = 1.0f,
						  const ThreeFloats& colour = ThreeFloats(0.0f, 0.0f, 0.0f),
						  float alpha = 1.0f) = 0;
	///	Draws a single line.
	/*!
		\param image The image used to texture the line. If this is an empty
		string, a default image will be used.
		\param start The start of the line.
		\param end The end of the line.
		\param colour The colour to tint the line (black by default).
		\param alpha How to fade the line in/out.
	 */
	virtual void drawLine(const std::wstring& image,
						  const TwoFloats& start,
						  const TwoFloats& end,
						  const ThreeFloats& colour = ThreeFloats(0.0f, 0.0f, 0.0f),
						  float alpha = 1.0f) = 0;
	///	Draws a connected line segment, with an attempt at mitering.
	/*!
		\param image The image used to texture the line. If this is an empty
		string, a default image will be used.
		\param points The points of the line to draw.
		\param pointsScale How to scale the width of the line at the individual
		points.
		\param pointsColour The colour of the individual points.
		\param pointsAlpha The alpha value of the individual points.
	 */
	virtual void drawConnectedLine(const std::wstring& image,
								   const std::vector<TwoFloats>& points,
								   const std::vector<float>& pointsScale,
								   const std::vector<ThreeFloats>& pointsColour,
								   const std::vector<float>& pointsAlpha) = 0;
	///	Fills a rectangle with the desired colour.
	/*!
		\param position The top-left position of the rectangle.
		\param size The size of the rectangle.
		\param colour The colour of the rectangle.
		\param alpha How to fade the rectangle in/out.
	 */
	virtual void fillRect(const TwoFloats& position,
						  const TwoFloats& size,
						  const ThreeFloats& colour,
						  float alpha = 1.0f) = 0;
	///	Fills a rectangle with a horizontal gradient.
	/*!
		\param position The top-left position of the rectangle.
		\param size The size of the rectangle.
		\param colourTop The colour of the top of the rectangle.
		\param colourBottom The colour of the bottom of the rectangle.
		\param alphaTop The alpha value at the top of the rectangle.
		\param alphaBottom The alpha value at the bottom of the rectangle.
	 */
	virtual void fillGradient(const TwoFloats& position,
							  const TwoFloats& size,
							  const ThreeFloats& colourTop,
							  const ThreeFloats& colourBottom,
							  float alphaTop = 1.0f,
							  float alphaBottom = 1.0f) = 0;

	///	Translates all subsequent drawing commands by the specified offset.
	virtual void translate(const TwoFloats& offset) = 0;
	///	Scales all subsequent drawing commands by the specified amounts.
	virtual void scale(const TwoFloats& size) = 0;
	///	Rotates all subsequent drawing commands by the specified angle.
	virtual void rotate(float angle, const TwoFloats& pivot) = 0;

	///	Specifies a viewport to restrict drawing to.
	/*!
		\param position The position of the viewport (relative to the drawing
		area, not the window).
		\param size The size of the viewport (ditto).
	 */
	virtual void setViewport(const TwoFloats& position,
							 const TwoFloats& size) = 0;
	///	Resets the viewport.
	virtual void resetViewport() = 0;

	///	Used to draw to a frame buffer instead of the screen.
	/*!
		Once the user has drawn to a frame buffer (and called
		endFrameBufferDrawing(), they may then call getFrameBufferImage() to get
		the identifier of the frame buffer image that they drew to.

		The size of the frame buffer will be the same as the size of the window.
	 */
	virtual void startFrameBufferDrawing() = 0;
	///	Used to draw to a frame buffer instead of the screen.
	virtual void endFrameBufferDrawing() = 0;
	///	Used to e.g. apply post-processing etc. to the contents of a frame buffer image.
	virtual const std::wstring getFrameBufferImage() const = 0;
	
	///	Used to find out about the capabilities of the user's graphics card.
	enum GraphicsCapabilities
	{
		Shaders,
		FrameBuffer,
		VertexArrayObjects
	};
	///	Used to find out about the capabilities of the user's graphics card.
	virtual bool canSupport(GraphicsCapabilities feature) const = 0;

	///	Returns the size of the drawing area.
	/*!
		This is the size of the area we do our drawing into, which won't
		necessarily be the same as the size of the window we've got.
	 */
	virtual const TwoFloats& getDrawingAreaSize() const = 0;
	///	Returns the size of the window.
	/*!
		This is the size of the window we've got to draw to. It won't
		necessarily have the same dimensions as the area we're actually drawing
		into.
	 */
	virtual const TwoFloats& getWindowSize() const = 0;

	///	Struct representing the basic information a Drawer holds on an image.
	/*!
		It's expected that subclasses of Drawer will subclass this struct and
		add their own members to it.
	 */
	struct Image
	{
		///	The size of the image in pixels.
		TwoFloats size;
		///	The position of the image within the texture (0->1).
		TwoFloats posWithin;
		///	The size of the image within the texture (0->1).
		TwoFloats sizeWithin;
	};
	///	Returns the internal image data for the named image.
	/*!
		This can be used to implement custom drawing routines with textures
		which have already been loaded. Drawer subclasses should store texture
		ids in Image subclasses.

		Note that different Drawer subclasses will probably have their own
		unique Image subclasses, so if you are implementing custom drawing
		routines, they will probably be tied to a particular Drawer type.
	 */
	virtual const Image * const getInternalImageData(const std::wstring& imageId) const = 0;
};

typedef shared_ptr<Drawer> DrawerPtr;

#endif
