/*
**  ClanLib SDK
**  Copyright (c) 1997-2015 The ClanLib Team
**
**  This software is provided 'as-is', without any express or implied
**  warranty.  In no event will the authors be held liable for any damages
**  arising from the use of this software.
**
**  Permission is granted to anyone to use this software for any purpose,
**  including commercial applications, and to alter it and redistribute it
**  freely, subject to the following restrictions:
**
**  1. The origin of this software must not be misrepresented; you must not
**     claim that you wrote the original software. If you use this software
**     in a product, an acknowledgment in the product documentation would be
**     appreciated but is not required.
**  2. Altered source versions must be plainly marked as such, and must not be
**     misrepresented as being the original software.
**  3. This notice may not be removed or altered from any source distribution.
**
**  Note: Some of the libraries ClanLib may link to may have additional
**  requirements or restrictions.
**
**  File Author(s):
**
**    Magnus Norddahl
**    Harry Storbacka
**    Kenneth Gangstoe
**    Mark Page
*/


#pragma once

#include "../Render/graphic_context.h"

namespace clan
{
/// \addtogroup clanDisplay_Display clanDisplay Display
/// \{

class Canvas_Impl;
class GraphicContext;
class PrimitivesArray;
class Sprite_Impl;
class Image;
class GlyphCache;
class Draw;
class RenderBatcher;
class Colorf;
class Pointf;
class Gradient;
class LineSegment2f;
class LineSegment2;
class Trianglef;
class Triangle;
class DisplayWindow;
class DisplayWindowDescription;
class Path;
class Pen;
class Brush;

/// \brief Mapping modes.
enum MapMode
{
	map_2d_upper_left,
	map_2d_lower_left,
	map_user_projection
};

/// \brief 2D Graphics Canvas
class Canvas
{
	/// \name Construction
	/// \{

public:
	/// \brief Constructs a null instance.
	Canvas();

	/// \brief Constructs a Canvas based on a framebuffer.  (based on the copy of the canvas)
	Canvas(Canvas &canvas, FrameBuffer &framebuffer);

	/// \brief Constructs a Canvas
	explicit Canvas(DisplayWindow &window);

	~Canvas();

	/// \}
	/// \name Attributes
	/// \{
public:
	/// \brief Create a copy of a canvas
	Canvas create();

	/// \brief Returns true if this object is invalid.
	bool is_null() const { return !impl; }

	/// \brief Throw an exception if this object is invalid.
	void throw_if_null() const;

	/// \brief Get gc
	///
	/// \return Graphic Context
	GraphicContext& get_gc() const;

	/// \brief Returns the current effective transform matrix.
	const Mat4f &get_transform() const;

	/// \brief Returns the inverse of the current effective transform matrix
	///
	/// This is cached
	Mat4f &get_inverse_transform();

	/// \brief Returns the current effective projection matrix.
	const Mat4f &get_projection() const;

	operator GraphicContext&() const { return get_gc(); }

	/// \brief Returns the current width of the context.
	inline float get_width() const { return get_gc().get_dip_width(); }

	/// \brief Returns the current height of the context.
	inline float get_height() const { return get_gc().get_dip_height(); }

	/// \brief Returns the current size of the context.
	inline Sizef get_size() const { return get_gc().get_dip_size(); }

	/// \brief Returns the current clipping rectangle
	Rectf get_cliprect() const;

	/// \brief Return the content of the read buffer into a pixel buffer.
	PixelBuffer get_pixeldata(const Rect& rect, TextureFormat texture_format = tf_rgba8, bool clamp = true);

	/// \brief Return the content of the read buffer into a pixel buffer.
	PixelBuffer get_pixeldata(TextureFormat texture_format = tf_rgba8, bool clamp = true);

	/// Retrieves the display pixel ratio of the context.
	/// \seealso Resolution Independence
	float get_pixel_ratio() const { return get_gc().get_pixel_ratio(); }

/// \}
/// \name Operations
/// \{
public:
	/// \brief Set active rasterizer state
	void set_rasterizer_state(const RasterizerState &state);

	/// \brief Set active blend state
	void set_blend_state(const BlendState &state, const Colorf &blend_color = Colorf::white, unsigned int sample_mask = 0xffffffff);

	/// \brief Set active depth stencil state
	void set_depth_stencil_state(const DepthStencilState &state, int stencil_ref = 0);

	/// \brief Set active rasterizer state
	void reset_rasterizer_state();

	/// \brief Set active blend state
	void reset_blend_state();

	/// \brief Set active depth stencil state
	void reset_depth_stencil_state();

	/// \brief Set the current clipping rectangle.
	void set_cliprect(const Rectf &rect);

	/// \brief Push current clipping rectangle to stack.
	/** <p>If a rectangle is passed, it afterwards sets clipping
	    rectangle to the union of the current rectangle and the passed
	    rectangle.</p>*/
	void push_cliprect(const Rectf &rect);

	/// \brief Push cliprect
	void push_cliprect();

	/// \brief Pop current clipping rectangle from the stack.
	void pop_cliprect();

	/// \brief Removes the set clipping rectangle and empties the cliprect stack.
	void reset_cliprect();

	/// \brief Clears the whole context using the specified color.
	void clear(const Colorf &color = Colorf::black);

	/// \brief Clear the stencil buffer
	///
	/// \param value value to clear to.
	void clear_stencil(int value = 0);

	/// \brief Clear the depth buffer
	///
	/// \param value: value to clear to. Range 0.0 - 1.0.
	void clear_depth(float value = 0);

	/// \brief Set active program object to the standard program specified.
	void set_program_object(StandardProgram standard_program);

	/// \brief Set the projection mapping mode.
	void set_map_mode(MapMode mode);

	/// \brief Set the projection matrix to be used in user projection map mode.
	void set_projection(const Mat4f &matrix);

	/// \brief Specifies which render batcher is to be currently active
	///
	/// If the render batcher is already active, nothing happens. If a different render batcher
	/// is currently active, it is flushed before the new batcher is made active.
	void set_batcher(RenderBatcher *batcher);

	/// \brief Sets the transform matrix to a new matrix.
	void set_transform(const Mat4f &matrix);

	/// \brief Multiplies the passed matrix onto the transform matrix.
	void mult_transform(const Mat4f &matrix);

	/// \brief Flushes the render batcher currently active.
	void flush();

	/// \brief Draw a point.
	void draw_point(float x1, float y1, const Colorf &color);

	/// \brief Point
	///
	/// \param gc = Graphic Context
	/// \param point = Pointf
	/// \param color = Colorf
	void draw_point(const Pointf &point, const Colorf &color);

	/// \brief Draw a line.
	void draw_line(float x1, float y1, float x2, float y2, const Colorf &color = Colorf::white);

	/// \brief Line
	///
	/// \param gc = Graphic Context
	/// \param start = Pointf
	/// \param end = Pointf
	/// \param color = Colorf
	void draw_line(const Pointf &start, const Pointf &end, const Colorf &color = Colorf::white);

	/// \brief Line
	///
	/// \param gc = Graphic Context
	/// \param line_segment = The line
	/// \param color = Colorf
	void draw_line(const LineSegment2f &line_segment, const Colorf &color = Colorf::white);

	/// \brief Lines
	void draw_lines(const Vec2f *positions, int num_vertices, const Colorf &color = Colorf::white);

	/// \brief Lines
	void draw_lines(const Vec2f *line_positions, const Vec2f *texture_positions, int num_vertices, const Texture2D &texture, const Colorf &line_color = Colorf::white);

	/// \brief Line Strip
	void draw_line_strip(const Vec2f *positions, int num_vertices, const Colorf &color = Colorf::white);

	/// \brief Draw a box / rectangle.
	void draw_box(float x1, float y1, float x2, float y2, const Colorf &color = Colorf::white);

	/// \brief Box
	///
	/// \param gc = Graphic Context
	/// \param start = Pointf
	/// \param end = Pointf
	/// \param color = Colorf
	void draw_box(const Pointf &start, const Pointf &end, const Colorf &color = Colorf::white);

	/// \brief Box
	///
	/// \param gc = Graphic Context
	/// \param rect = Rectf
	/// \param color = Colorf
	void draw_box(const Rectf &rect, const Colorf &color);

	/// \brief Draw a filled box / rectangle.
	void fill_rect(float x1, float y1, float x2, float y2, const Colorf &color = Colorf::white);

	/// \brief Fill
	///
	/// \param gc = Graphic Context
	/// \param start = Pointf
	/// \param end = Pointf
	/// \param color = Colorf
	void fill_rect(const Pointf &start, const Pointf &end, const Colorf &color = Colorf::white);

	/// \brief Fill
	///
	/// \param gc = Graphic Context
	/// \param rect = Rectf
	/// \param color = Colorf
	void fill_rect(const Rectf &rect, const Colorf &color);

	/// \brief Gradient fill
	///
	/// \param gc = Graphic Context
	/// \param x1 = value
	/// \param y1 = value
	/// \param x2 = value
	/// \param y2 = value
	/// \param color = Gradient
	void fill_rect(float x1, float y1, float x2, float y2, const Gradient &color);

	/// \brief Gradient fill
	///
	/// \param gc = Graphic Context
	/// \param start = Pointf
	/// \param end = Pointf
	/// \param gradient = Gradient
	void fill_rect(const Pointf &start, const Pointf &end, const Gradient &gradient);

	/// \brief Gradient fill
	///
	/// \param gc = Graphic Context
	/// \param rect = Rectf
	/// \param gradient = Gradient
	void fill_rect(const Rectf &rect, const Gradient &gradient);

	/// \brief Draw a circle.
	void fill_circle(float center_x, float center_y, float radius, const Colorf &color = Colorf::white);

	/// \brief Circle
	///
	/// \param gc = Graphic Context
	/// \param center = Pointf
	/// \param radius = value
	/// \param color = Colorf
	void fill_circle(const Pointf &center, float radius, const Colorf &color = Colorf::white);

	/// \brief Gradient circle
	///
	/// \param gc = Graphic Context
	/// \param center = Pointf
	/// \param radius = value
	/// \param gradient = Gradient
	void fill_circle(const Pointf &center, float radius, const Gradient &gradient);

	/// \brief Gradient circle
	///
	/// \param gc = Graphic Context
	/// \param center = Pointf
	/// \param centergradient = Pointf
	/// \param radius = value
	/// \param gradient = Gradient
	void fill_circle(const Pointf &center, const Pointf &centergradient, float radius, const Gradient &gradient);

	/// \brief Draw a triangle.
	void fill_triangle(const Pointf &a, const Pointf &b, const Pointf &c, const Colorf &color = Colorf::white);

	/// \brief Draw a triangle.
	void fill_triangle(const Trianglef &dest_triangle, const Colorf &color = Colorf::white);

	/// \brief Draw triangles
	void fill_triangles(const Vec2f *triangle_positions, int num_vertices, const Colorf &color = Colorf::white);

	/// \brief Draw triangles
	void fill_triangles(const Vec2f *triangle_positions, int num_vertices, const Gradient &gradient);

	/// \brief Draw triangles
	void fill_triangles(const Vec2f *triangle_positions, const Colorf *colors, int num_vertices);

	/// \brief Draw triangles
	void fill_triangles(const std::vector<Vec2f> &triangles, const Colorf *colors);

	/// \brief Draw triangles
	void fill_triangles(const std::vector<Vec2f> &triangles, const Colorf &color = Colorf::white);

	/// \brief Draw triangles
	void fill_triangles(const std::vector<Vec2f> &triangles, const Gradient &gradient);

	/// \brief Draw triangles
	void fill_triangles(const Vec2f *positions, const Vec2f *texture_positions, int num_vertices, const Texture2D &texture, const Colorf &color = Colorf::white);

	/// \brief Draw triangles
	void fill_triangles(const Vec2f *positions, const Vec2f *texture_positions, int num_vertices, const Texture2D &texture, const Colorf *colors);

	/// \brief Draw triangles
	void fill_triangles(const std::vector<Vec2f> &positions, const std::vector<Vec2f> &texture_positions, const Texture2D &texture, const Colorf &color = Colorf::white);

	/// \brief Draw triangles
	void fill_triangles(const std::vector<Vec2f> &positions, const std::vector<Vec2f> &texture_positions, const Texture2D &texture, const std::vector<Colorf> &colors);

	/// \brief Draw triangles
	void fill_triangles(const std::vector<Vec2f> &positions, const Texture2D &texture, const Colorf &color = Colorf::white);

	/// \brief Draw triangles
	void fill_triangles(const std::vector<Vec2f> &positions, const Texture2D &texture, const Rect &texture_rect, const Colorf &color = Colorf::white);

	/// \brief Draw triangles
	void fill_triangles(const Vec2f *triangle_positions, int num_vertices, const Texture2D &texture, const Colorf &color = Colorf::white);

	/// \brief Draw triangles
	void fill_triangles(const Vec2f *triangle_positions, int num_vertices, const Texture2D &texture, const Rect &texture_rect, const Colorf &color = Colorf::white);

	/// \brief Draw triangles
	void fill_triangles(const std::vector<Vec2f> &positions, const Texture2D &texture, const Gradient &gradient);

	/// \brief Draw triangles
	void fill_triangles(const std::vector<Vec2f> &positions, const Texture2D &texture, const Rect &texture_rect, const Gradient &gradient);

	/// \brief Draw triangles
	void fill_triangles(const Vec2f *triangle_positions, int num_vertices, const Texture2D &texture, const Gradient &gradient);

	/// \brief Draw triangles
	void fill_triangles(const Vec2f *triangle_positions, int num_vertices, const Texture2D &texture, const Rect &texture_rect, const Gradient &gradient);

	/// \brief Draw a filled ellipse.
	void fill_ellipse(const Pointf &center, float radius_x, float radius_y, const Colorf &color = Colorf::white);

	/// \brief Draw a gradient filled ellipse.
	void fill_ellipse(const Pointf &center, float radius_x, float radius_y, const Gradient &gradient);

	/// \brief Snaps the point to the nearest pixel corner
	Pointf grid_fit(const Pointf &pos);

/// \}
/// \name Events
/// \{
public:

/// \}
/// \name Implementation
/// \{
private:
	std::shared_ptr<Canvas_Impl> impl;

	friend class Sprite_Impl;
	friend class Image;
	friend class Font_Impl;
	friend class Font_DrawSubPixel;
	friend class Font_DrawFlat;
	friend class Font_DrawScaled;
	friend class Path;
/// \}
};

}

/// \}
