#include "NinePatch.h"
#include <QPainter>
#include <vector>
#include <cmath>

struct Part {
	bool stretchable = false;
	int pos = 0;
	int len = 0;
	Part() = default;
	Part(int pos, int len, bool stretchable)
		: stretchable(stretchable)
		, pos(pos)
		, len(len)
	{
	}
};

static inline bool isStretchableMarker(QRgb pixel)
{
	return (qAlpha(pixel) >> 7) & 1;
}

static QImage resize9patch(QImage const &image, int dw, int dh)
{
	int sw = image.width();
	int sh = image.height();
	if (sw > 2 && sh > 2 && dw > 0 && dh > 0) {
		QImage newimage(dw, dh, image.format());
		newimage.fill(Qt::transparent);
		QPainter pr(&newimage);
		pr.setRenderHint(QPainter::SmoothPixmapTransform);

		std::vector<Part> horz;
		std::vector<Part> vert;
		int horz_stretch = 0;
		int vert_stretch = 0;
		{
			int pos;
			QRgb last;
			QRgb next;
			pos = 0;
			last = image.pixel(1, 0);
			for (int x = 1; x < sw - 1; x++) {
				next = image.pixel(x + 1, 0);
				if (isStretchableMarker(last) != isStretchableMarker(next) || x == sw - 2) {
					bool stretchable = isStretchableMarker(last);
					int len = x - pos;
					horz.emplace_back(pos, len, stretchable);
					if (stretchable) horz_stretch += len;
					last = next;
					pos = x;
				}
			}
			pos = 0;
			last = image.pixel(0, 1);
			for (int y = 1; y < sh - 1; y++) {
				next = image.pixel(0, y + 1);
				if (isStretchableMarker(last) != isStretchableMarker(next) || y == sh - 2) {
					bool stretchable = isStretchableMarker(last);
					int len = y - pos;
					vert.emplace_back(pos, len, stretchable);
					if (stretchable) vert_stretch += len;
					last = next;
					pos = y;
				}
			}
		}
		double horz_mul = 0;
		double vert_mul = 0;
		if (horz_stretch > 0) horz_mul = (double)(dw - (sw - 2 - horz_stretch)) / horz_stretch;
		if (vert_stretch > 0) vert_mul = (double)(dh - (sh - 2 - vert_stretch)) / vert_stretch;
		int dy0 = 0;
		int dy1 = 0;
		double vstretch = 0;
		for (int i = 0; i < (int)vert.size(); i++) {
			int sy0 = vert[i].pos;
			int sy1 = vert[i].pos + vert[i].len;
			if (i + 1 == (int)vert.size()) {
				dy1 = dh;
			} else if (vert[i].stretchable) {
				vstretch += (double)vert[i].len * vert_mul;
				double s = floor(vstretch);
				vstretch -= s;
				dy1 += (int)s;
			} else {
				dy1 += vert[i].len;
			}
			int dx0 = 0;
			int dx1 = 0;
			double hstretch = 0;
			for (int j = 0; j < (int)horz.size(); j++) {
				int sx0 = horz[j].pos;
				int sx1 = horz[j].pos + horz[j].len;
				if (j + 1 == (int)horz.size()) {
					dx1 = dw;
				} else if (horz[j].stretchable) {
					hstretch += (double)horz[j].len * horz_mul;
					double s = floor(hstretch);
					hstretch -= s;
					dx1 += (int)s;
				} else {
					dx1 += horz[j].len;
				}
				pr.drawImage(QRect(dx0, dy0, dx1 - dx0, dy1 - dy0), image, QRect(sx0 + 1, sy0 + 1, sx1 - sx0, sy1 - sy0));
				dx0 = dx1;
			}
			dy0 = dy1;
		}

		return newimage;
	}
	return QImage();
}

QImage createImageFromNinePatchImage(QImage const &image, int dw, int dh)
{
	int w = dw;
	int h = dh;
	if (w < image.width() || h < image.height()) { // shrink
		if (w < image.width()) w = image.width();
		if (h < image.height()) h = image.height();
		QImage pm1 = resize9patch(image, w, h);
		if (pm1.isNull()) return QImage();
		QImage pm2(dw, dh, image.format());
		pm2.fill(Qt::transparent);
		QPainter pr(&pm2);
		pr.setRenderHint(QPainter::SmoothPixmapTransform);
		pr.drawImage(QRect(0, 0, dw, dh), pm1, QRect(0, 0, w, h));
		return pm2;
	}
	return resize9patch(image, w, h);
}
