#pragma once
#include "Widget.h"
#include "Graphics.h"
#include "Common.h"
BEGIN_NAMESPACE


class Pixmap : public PaintTargetInterface
{
public:
	Pixmap(unsigned char* a_memoryBuffer, int a_width, int a_height, int a_depth);
	Pixmap(const char* a_fileName);
	~Pixmap();

	const uint32_t* bits() const;
	uint32_t width() const;
	uint32_t height() const;
  Size size() const { return (Size){ (int)width(), (int)height() }; }

	PixelBuffer& targetBuffer() override;
	float targetScale() override { return 1.0f; }

private:
	struct PixmapData* m_data;
};


enum FontStyle
{
	FS_Normal     = 0,
	FS_Italic     = 1<<0,
	FS_Oblique    = 1<<1,
	FS_StrikeOut  = 1<<2,
	FS_UnderLine  = 1<<3,
	FS_OverLine   = 1<<4,
	FS_FixedPitch =	1<<5
};


/*
class FontMetrics
{
public:
	Property<int> ascent;
	Property<int> descent;
	Property<int> xHeight;
	Property<int> capHeight;
	Property<int> lineGap;
};

*/

class Font
{
public:
	Font()                                                  { Init();                                                    }
	Font(const Font& a_other)                               { Init(a_other.family.value(), a_other.pointSize.value(),
                                                                 a_other.weight.value(), a_other.style.value(),
                                                                 a_other.stretch.value());                             }
	Font& operator=(const Font& a_other)                    { Init(a_other.family.value(), a_other.pointSize.value(),
                                                                 a_other.weight.value(), a_other.style.value(),
                                                                 a_other.stretch.value());
                                                            return *this;                                              }
	Font(const char* a_family)                              { Init(a_family);                                            }
	Font(int a_pointSize)                                   { Init(Font::Helvetica, a_pointSize);                        }
	Font(FontStyle a_style)                                 { Init(Font::Helvetica, 12, 50, a_style);                    }
	Font(const char* a_family,
       int         a_pointSize,
       int         a_weight = 50,
       FontStyle   a_style = FS_Normal,
       int         a_stretch = 100)                       { Init(a_family, a_pointSize, a_weight, a_style, a_stretch); }

	Property<String> family;
	Property<int> pointSize;
	Property<int> weight;       // 25 light  50 normal  75 bold
	Property<FontStyle> style;
	Property<int> stretch;			// 50 very condensed  75 condensed  100 normal  125 expanded  200 very expanded

  //FontMetrics fontMetrics();
	Property<FontMetrics> metrics;
	//FontMetrics metrics;

/*
  // Idea for a more cross-platform / generic specification:
	static const char* DefaultFamily;
	static const char* SansFamily;
	static const char* SerifFamily;
	static const char* MonoFamily;
*/
	static const char* Helvetica;
	static const char* Times;
	static const char* Courier;
	static const char* OldEnglish;
	static const char* Monospace;
	static const char* Fantasy;
	static const char* Cursive;
	static const char* UIFont;

private:
	void Init(const char* a_family = Font::Helvetica, int a_pointSize = 12,
			      int a_weight = 50, FontStyle a_style = FS_Normal, int a_stretch = 100)
	{
		family = a_family;
		pointSize = a_pointSize;
		weight = a_weight;
		style = a_style;
		stretch = a_stretch;
	}
};


enum BrushStyle
{
	BS_None,
	BS_SolidColor,
	BS_Pixmap,
	BS_Gradient
};


class Brush
{
public:
	Brush()                                 { Init();                                                             }
	Brush(const Brush& a_other)             { Init(a_other.style.value(), a_other.color.value(),
                                                 a_other.pixmap.value(), a_other.origin.value());               }
	Brush& operator=(const Brush& a_other)  { Init(a_other.style.value(), a_other.color.value(),
                                                 a_other.pixmap.value(), a_other.origin.value());               
                                            return *this;                                                       }
	Brush(uint32_t a_color)                 { Init(BS_SolidColor, a_color);                                       }
	Brush(Pixmap* a_pixmap)                 { Init(BS_Pixmap, 0, a_pixmap);                                       }
	Brush(Pixmap* a_pixmap, Point a_origin) { Init(BS_Pixmap, 0, a_pixmap, a_origin);                             }
	Brush(Gradient a_gradient)              { }  // TODO: gradient brush not fully implemented - some gradient support

	Property<BrushStyle> style;
	Property<uint32_t> color;
	Property<Pixmap*> pixmap;
	Property<Point> origin;

private:
	void Init(BrushStyle a_style = BS_None, uint32_t a_color = 0, Pixmap* a_pixmap = 0, Point a_origin = {})
	{
		style = a_style;
		color = a_color;
		pixmap = a_pixmap;
		origin = a_origin;
	}
};


enum class PenStyle
{
	None,
	Solid,
	Dotted,
	Dashed,
	DotDashed
};


class Pen
{
public:
  Pen()                               { Init();                             }
  Pen(const Pen& a_other)             { Init(a_other.style.value(),
                                             a_other.color.value(),
                                             a_other.width.value());        }
  Pen& operator=(const Pen& a_other)  { Init(a_other.style.value(),
                                             a_other.color.value(),
                                             a_other.width.value());
                                        return *this;                       }
  Pen(PenStyle a_style)               { Init(a_style);                      }
  Pen(uint32_t a_color)               { Init(PenStyle::Solid, a_color);     }

  Property<PenStyle> style;
	Property<uint32_t> color;
	Property<uint32_t> width;

private:
	void Init(PenStyle a_style = PenStyle::None, uint32_t a_color = 0, uint32_t a_width = 1)
  {
    style = a_style;
    color = a_color;
    width = a_width;
  }
};


class Painter
{
public:
	Painter();
	Painter(PaintTargetInterface* a_window);
	~Painter();

	// setPen with a Pen
	// setBrush with a Brush
  // setFont with a Font
	
  void setPen(Pen a_pen);
	void setBrush(Brush a_brush);
	void setFont(Font a_font);

	void setPen(uint32_t a_color);
	void setBrush(uint32_t a_color); // brush can be color or pattern / pixmap with some origin

  void setFontFamily(const char* a_fontName);
	void setFontSize(uint32_t a_size);

	// drawArc drawChord drawPie drawPolygon drawPolyline 
	void drawEllipse(int a_x, int a_y, int a_width, int a_height);
	void drawGradient(int a_x, int a_y, Gradient a_gradient, int a_width, int a_height);
	void drawLine(int a_x, int a_y, int a_x2, int a_y2);
	// drawLines
	// drawPoint
	// drawPoints
	// drawPath
	// drawImage
	void drawPixmap(int a_x, int a_y, const Pixmap& pixmap, int a_x1 = 0, int a_y1 = 0, int a_x2 = -1, int a_y2 = -1, bool a_alphaMask = false);
	void drawPixmapAlphaBlended(int a_x, int a_y, const Pixmap& pixmap, int a_x1 = 0, int a_y1 = 0, int a_x2 = -1, int a_y2 = -1, int a_alpha = 0);

	void drawPixelBuffer(int a_x, int a_y, const uint8_t* a_pixels, int a_pixelsWidth, int a_pixelsHeight, int a_pixelsDepth);

	// drawRect
	// drawRects
	// drawRoundedRect
	// drawStaticText // optimization which caches things
	// drawTiledPixmap
	// fillRect // like drawRect, but passing in the brush and no-pen

	void drawRectangle(int a_x, int a_y, int a_width, int a_height, bool a_alphaBlending = false);
	void drawFocusRectangle(int a_x, int a_y, int a_width, int a_height);
	void drawText(int a_x, int a_y, const String& a_formattedUtf8String, int a_flags = 0);
  Size textExtents(const char* a_formattedUtf8String);

  // Helpers to wrap the above
	void drawRectangle(Rectangle a_rectangle, bool a_alphaBlending = false)
  {
	  drawRectangle(a_rectangle.m_x, a_rectangle.m_y, a_rectangle.m_width, a_rectangle.m_height, a_alphaBlending);
  }

	void drawFocusRectangle(Rectangle a_rectangle)
  {
	  drawFocusRectangle(a_rectangle.m_x, a_rectangle.m_y, a_rectangle.m_width, a_rectangle.m_height);
  }

  template <typename ...Ts>
	void drawText(int a_x, int a_y, const char* a_utf8String, Ts ...args)
  {
	  drawText(a_x, a_y, String(a_utf8String, args...));
  }

  template <typename ...Ts>
  Size textExtents(const char* a_utf8String, Ts ...args)
  {
	  return textExtents(String(a_utf8String, args...));
  }

	FontMetrics fontMetrics();

  GlyphMetrics glyphMetrics(uint32_t a_unicodeCharacter); // technically unicode code-points do not map one-to-one with glyphs,
                                                          // API would need to expose a lot of complexity for this

  // Clear the target buffer ready for drawing to
  void reset();
private:
  void scaleArguments(int& a_x, int& a_y, int& a_width, int& a_height);
	struct PainterData* m_data;
};


END_NAMESPACE

