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

#include "kio/kio.h"
#include "Image.h"

namespace gterm
{

StaticImage::StaticImage (const QSize& sz, QRgb bkg)
	: Image(sz, bkg)
	, image(sz,QImage::Format_ARGB32)
{
	image.fill(bkg);	// TODO: alpha?
}

StaticImage::StaticImage (const QString& filename, QRgb bkg)
	: Image(QSize(), bkg)
	, image(filename)
{
	if (image.isNull()) return; // read failed

	_size = image.size();

	QImage::Format format = image.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32;
	if (image.format() != format)
		image.convertTo(format);
}

void StaticImage::lock()
{
	Image::lock();
	painter.reset(new QPainter(&image));
	painter->setRenderHint(QPainter::Antialiasing);
}

void StaticImage::unlock()
{
	painter.reset();	// delete painter
	Image::unlock();
}

bool StaticImage::try_lock()
{
	if (Image::try_lock())
	{
		painter.reset(new QPainter(&image));
		painter->setRenderHint(QPainter::Antialiasing);
		return true;
	}
	return false;
}

void StaticImage::resize(const QSize&)
{
	TODO();
}

void StaticImage::erase()
{
	// erase image.
	// the image is cleared with background color and alpha.

	image.fill(background_color);	// TODO: alpha?
}

void StaticImage::setPen(QRgb c, int lw)
{
	QPen pen(QBrush(c),lw);
	painter->setPen(pen);
}

void StaticImage::setBrush(QRgb c)
{
	QBrush brush(c);
	painter->setBrush(brush);
}

void StaticImage::setFont(const QString& name, int height, FontStyle style)
{
	QFont font(name, height, style&BOLD ? QFont::Bold : QFont::Normal, style&ITALIC);
	painter->setFont(font);
}

void StaticImage::setTransformation(const QTransform& t)
{
	painter->setTransform(t,true);
}

void StaticImage::drawLine(const QPoint& p1, const QPoint& p2)
{
	painter->drawLine(p1,p2);
}

void StaticImage::drawRect(const QRect& rect)
{
	painter->drawRect(rect);
}

void StaticImage::drawEllipse(const QRect& rect)
{
	painter->drawEllipse(rect);
}

void StaticImage::drawText(const QPoint& p1, const QString& text)
{
	painter->drawText(p1,text);
}

void StaticImage::drawImage(s_ptr<Image> source_image, const QPoint& p1)
{
	if (auto* static_image = dynamic_cast<StaticImage*>(source_image.get()))
	{
		painter->drawImage(p1, static_image->image);
	}
	else // dynamic image
	{
		auto* dynamic_image = dynamic_cast<DynamicImage*>(source_image.get());
		assert(dynamic_image != nullptr);

		if (p1.x() || p1.y())
		{
			QTransform trans = painter->transform();
			painter->translate(p1);
			dynamic_image->run(*painter);
			painter->setTransform(trans);
		}
		else
		{
			dynamic_image->run(*painter);
		}
	}
}

void StaticImage::drawImage(s_ptr<Image> source_image, const QRect& zrect)
{
	if (auto* static_image = dynamic_cast<StaticImage*>(source_image.get()))
	{
		painter->drawImage(zrect, static_image->image);
	}
	else // dynamic image
	{
		auto* dynamic_image = dynamic_cast<DynamicImage*>(source_image.get());
		assert(dynamic_image != nullptr);

		QTransform trans = painter->transform();
		painter->translate(zrect.topLeft());
		qreal fx = qreal(zrect.width()) / dynamic_image->size().width();
		qreal fy = qreal(zrect.height()) / dynamic_image->size().height();
		painter->scale(fx,fy);
		dynamic_image->run(*painter);
		painter->setTransform(trans);
	}
}

void StaticImage::drawImage(s_ptr<StaticImage> qimage, const QPoint& zpoint, const QRect& qrect)
{
	painter->drawImage(zpoint,qimage->image,qrect);
}


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


void DynamicImage::erase()
{
	// erase image.
	// the display pipeline is cleared.

	pipeline.purge();
}

void DynamicImage::setPen(QRgb c, int lw)
{
	QPen pen(QBrush(c),lw);
	pipeline.append([=](QPainter&p) { p.setPen(pen); });
}

void DynamicImage::setBrush(QRgb c)
{
	QBrush brush(c);
	pipeline.append([=](QPainter&p) { p.setBrush(brush); });
}

void DynamicImage::setFont(const QString& name, int height, FontStyle style)
{
	if (style & ~(BOLD|ITALIC)) TODO();

	int weight = style & BOLD ? QFont::Bold : QFont::Normal;
	bool italic = style & ITALIC;
	QFont font(name, height, weight, italic);
	pipeline.append([=](QPainter&p) { p.setFont(font); });
}

void DynamicImage::setTransformation(const QTransform& t)
{
	pipeline.append([=](QPainter&p) { p.setTransform(t,true); });
}

void DynamicImage::drawLine(const QPoint& p1, const QPoint& p2)
{
	pipeline.append([=](QPainter&p) { p.drawLine(p1,p2); });
}

void DynamicImage::drawRect(const QRect& rect)
{
	pipeline.append([=](QPainter&p) { p.drawRect(rect); });
}

void DynamicImage::drawEllipse(const QRect& rect)
{
	pipeline.append([=](QPainter&p) { p.drawEllipse(rect); });
}

void DynamicImage::drawText(const QPoint& p1, const QString& text)
{
	pipeline.append([=](QPainter&p) { p.drawText(p1,text); } );
}

void DynamicImage::drawImage(s_ptr<Image> qimage, const QPoint& zpoint)
{
	if (dynamic_cast<StaticImage*>(qimage.get()))
	{
		pipeline.append([=](QPainter&p) { p.drawImage(zpoint, static_cast<StaticImage*>(qimage.get())->image); });
	}
	else // dynamic image
	{
		assert(dynamic_cast<DynamicImage*>(qimage.get()) != nullptr);

		if (zpoint.x() || zpoint.y())
		{
			DrawingCommand cmd = [=](QPainter&p)
			{
				QTransform old = p.transform();
				p.translate(zpoint);
				static_cast<DynamicImage*>(qimage.get())->run(p);
				p.setTransform(old);
			};
			pipeline.append(std::move(cmd));
		}
		else
		{
			pipeline.append([=](QPainter&p)
			{
				static_cast<DynamicImage*>(qimage.get())->run(p);
			});
		}
	}
}

void DynamicImage::drawImage(s_ptr<Image> qimage, const QRect& zrect)
{
	if (dynamic_cast<StaticImage*>(qimage.get()))
	{
		pipeline.append([=](QPainter&p){ p.drawImage(zrect, static_cast<StaticImage*>(qimage.get())->image); });
	}
	else // dynamic image
	{
		assert(dynamic_cast<DynamicImage*>(qimage.get()) != nullptr);

		DrawingCommand cmd = [=](QPainter&p)
		{
			QTransform old = p.transform();
			p.translate(zrect.topLeft());
			auto* zimage = static_cast<DynamicImage*>(qimage.get());
			qreal fx = qreal(zrect.width()) / zimage->size().width();
			qreal fy = qreal(zrect.height()) / zimage->size().height();
			p.scale(fx,fy);
			zimage->run(p);
			p.setTransform(old);
		};
		pipeline.append(std::move(cmd));
	}
}

void DynamicImage::drawImage(s_ptr<StaticImage> qimage, const QPoint& zpoint, const QRect& qrect)
{
	pipeline.append([=](QPainter&p){ p.drawImage(zpoint,qimage->image,qrect); });
}


} // namespace




























