// Copyright (c) 2021 - 2021 kio@little-bat.de
// BSD-2-Clause license
// https://opensource.org/licenses/BSD-2-Clause

#pragma once
#include "kio/kio.h"
#include <QPainter>
#include <QRect>
#include <QPoint>
#include <QPen>
#include <QBrush>
#include <QFont>
#include <QTransform>
#include <mutex>

#define maybe_faster_new  false


namespace gterm
{

enum FontStyle
{
	NORMAL = 0,
	BOLD = 1,
	ITALIC = 2,
	UNDERLINE = 4,
};
inline FontStyle operator+(FontStyle a, FontStyle b) { return FontStyle(a|b); }


////////////////////////////////////////////////////////////


class DrawingCommand
{
public:
	DrawingCommand* next = nullptr;

	DrawingCommand() = default;
	virtual ~DrawingCommand() = default;

#if maybe_faster_new
	void* operator new (std::size_t size);
	void operator delete (void* ptr, std::size_t size) noexcept;
#endif

	virtual void operator() (QPainter&) = 0;
};


////////////////////////////////////////////////////////////


class DrawingPipeline
{
	Q_DISABLE_COPY(DrawingPipeline)

	DrawingCommand* start = nullptr;
	DrawingCommand* last = nullptr;
	mutable std::mutex mutex;

public:
	DrawingPipeline() = default;
	~DrawingPipeline() { purge(); }

	void append (DrawingCommand* cmd)
	{
		if (last) { last->next = cmd; last = cmd; }
		else      { start = last = cmd; }
	}

	DrawingPipeline& operator<< (DrawingCommand* cmd)
	{
		append(cmd);
		return *this;
	}

	void purge()
	{
		std::lock_guard<std::mutex> m(mutex);

		auto* p = start;
		start = last = nullptr;
		while (auto* q = p) { p = q->next; delete q; }
	}

	void operator() (QPainter& p) const
	{
		std::lock_guard<std::mutex> m(mutex);

		for (auto* c = start; c; c = c->next)
		{
			c->operator() (p);
		}
	}
};


////////////////////////////////////////////////////////////


class CmdSetPen final : public DrawingCommand
{
public:
	QPen pen;

	CmdSetPen(QPen&& p) noexcept : pen(std::move(p)) {}
	CmdSetPen(const QPen& p) noexcept : pen(p) {}

	void operator() (QPainter& p) override { p.setPen(pen); }
};

class CmdSetBrush : public DrawingCommand
{
public:
	QBrush brush;

	CmdSetBrush(QBrush&& b) noexcept : brush(std::move(b)) {}
	CmdSetBrush(const QBrush& b) noexcept : brush(b) {}

	void operator() (QPainter& p) override { p.setBrush(brush); }
};

class CmdSetFont : public DrawingCommand
{
public:
	QFont font;

	CmdSetFont(QFont&& f) noexcept : font(std::move(f)) {}
	CmdSetFont(const QFont& f) : font(f) {}
	CmdSetFont(const QString& name, int size, FontStyle style = NORMAL)
		: font(name, size, style&BOLD ? QFont::Bold : QFont::Normal, style&ITALIC) {}

	void operator() (QPainter& p) override { p.setFont(font); }
};

class CmdDrawLine : public DrawingCommand
{
public:
	QPoint p1,p2;

	CmdDrawLine(int x, int y, int x2, int y2) noexcept : p1(x,y), p2(x2,y2) {}
	CmdDrawLine(const QPoint& p1, const QPoint& p2) noexcept : p1(p1), p2(p2) {}

	void operator() (QPainter& p) override { p.drawLine(p1,p2); }
};

class CmdDrawRect : public DrawingCommand
{
public:
	QRect rect;

	CmdDrawRect(int x, int y, int w, int h) noexcept : rect(x,y,w,h) {}
	CmdDrawRect(const QPoint& p1, const QSize& sz) noexcept : rect(p1,sz) {}
	CmdDrawRect(const QRect& r) noexcept : rect(r) {}

	void operator() (QPainter& p) override { p.drawRect(rect); }
};

class CmdDrawEllipse : public DrawingCommand
{
public:
	QRect rect;

	CmdDrawEllipse(int x, int y, int w, int h) noexcept : rect(x,y,w,h) {}
	CmdDrawEllipse(const QPoint& p1, const QSize& sz) noexcept : rect(p1,sz) {}
	CmdDrawEllipse(const QRect& r) noexcept : rect(r) {}

	void operator() (QPainter& p) override { p.drawEllipse(rect); }
};

class CmdDrawText : public DrawingCommand
{
public:
	QPoint p1;
	QString text;

	CmdDrawText(int x, int y, QString text) noexcept : p1(x,y), text(std::move(text)) {}
	CmdDrawText(const QPoint& p1, QString text) noexcept : p1(p1), text(std::move(text)) {}

	void operator() (QPainter& p) override { p.drawText(p1,text); }
};

class CmdDrawImage : public DrawingCommand
{
public:
	std::shared_ptr<class Image> image;
	QRect z; // destination
	QRect q; // source

	CmdDrawImage(std::shared_ptr<class Image> image, int x, int y) noexcept;
	CmdDrawImage(std::shared_ptr<class Image> image, const QPoint& z) noexcept;
	CmdDrawImage(std::shared_ptr<class Image> image, const QRect& z) noexcept; // scaling

	CmdDrawImage(std::shared_ptr<class Image> image, const QPoint& z, const QRect& q) noexcept;
	CmdDrawImage(std::shared_ptr<class Image> image, const QRect& z, const QPoint& q) noexcept;
	CmdDrawImage(std::shared_ptr<class Image> image, const QRect& z, const QRect& q) noexcept // scaling
		: image(image), z(z), q(q) {}

	void operator() (QPainter&) override;
};

class CmdSetTransformation : public DrawingCommand
{
public:
	QTransform transform;

	enum Flags
	{
		Replace,
		After,
		Before,
	};

	CmdSetTransformation(Flags=Before);
	CmdSetTransformation(const QTransform&, Flags=Before);

	CmdSetTransformation& translate();
	CmdSetTransformation& rotate();
	CmdSetTransformation& scale();
};




} // namespace

















