#include "pch.h"
#include <QLayout>
#include "ColorGradient.h"
#include "util.h"

#define LeftRightGap 10
#define ColorExampleStart 30
#define ColorExmapleHeight 80
#define SliderBeginHeight 83
#define SliderBody 14
#define SliderPeak 6
#define SelectedPreview 20
#define SelectedHeight 115
ColorGradient::ColorGradient(QWidget *parent)
	: QWidget(parent)
{
	interpolationCombobox = new QComboBox(this);
	interpolationCombobox->addItem("RGB");
	interpolationCombobox->addItem("HLS");
	connect(interpolationCombobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
		this, [this](int index) {this->setInterpolationMode((ColorGradient::ColorInterpolationMode)index); });
	modeCombobox = new QComboBox(this);
	modeCombobox->addItem("Interpolate");
	modeCombobox->addItem("Discrete");
	connect(modeCombobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
		this, [this](int index) {this->setMode((ColorGradient::ColorMode)index);});
	label = new QLabel("Gradient", this);
	colorbutton = new ColorButton(this, QColor(0,0,0));
	connect(colorbutton, &ColorButton::colorChanged, this, [this](QColor color) {
		if (selected != nullptr) selected->color = color;
		});

	QGridLayout* layout = new QGridLayout(this);
	layout->setContentsMargins(0, 0, 0, 0);
	layout->setAlignment(Qt::AlignTop);
	spacerH = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
	layout->addWidget(label, 0, 0);
	layout->addItem(spacerH, 0, 1);
	layout->addWidget(colorbutton, 0, 2);
	layout->addWidget(modeCombobox, 0, 3);
	layout->addWidget(interpolationCombobox, 0, 4);
	//layout->(spacer);
	
	this->setLayout(layout);
	pointList.push_back(ColorPoint(0.0, QColor(0, 100, 200)));
	pointList.push_back(ColorPoint(0.5, QColor(0, 100, 50)));
	pointList.push_back(ColorPoint(0.7, QColor(200, 10, 10)));
	this->setMinimumHeight(150);
	//this->setMinimumHeight(150);
	this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);

}

ColorGradient::~ColorGradient()
{

}

QColor ColorGradient::getColor(double alpha)
{
	alpha = std::clamp(alpha, 0.0, 1.0);
	switch(mode) {
	case ColorMode::Discrete: {
		QColor result(255,255,255);
		for (ColorPoint& point : pointList) {
			if (point.alpha >= alpha) {
				result = point.color;
				break;
			}
		}
		return result;
	}
	case ColorMode::Interpolate: {
		ColorPoint before(0.0, QColor(255, 255, 255));
		ColorPoint last(1.0, QColor(255, 255, 255));
		ColorPoint* first = &before;
		ColorPoint* second = &last;
		for (ColorPoint& point : pointList) {
			if (point.alpha >= alpha) {
				second = &point;
				break;
			}
			first = &point;
		}
		double alphaBetween = util::inverseLinearInterpolation(first->alpha, second->alpha, alpha);
		switch (interpolationMode) {
		case ColorInterpolationMode::HSL:
			return util::interpolateHSL(first->color, second->color, alphaBetween);
		case ColorInterpolationMode::RGB:
		default:
			return util::interpolateRGB(first->color, second->color, alphaBetween);
		}
		
	}
	default: {
		return QColor(0, 0, 0);
	}
	}
	
}

void ColorGradient::addColorPoint(double alpha)
{
	auto iter = pointList.begin();
	for (; iter != pointList.end(); iter++) {
		if (iter->alpha >= alpha) {
			break;
		}
	}
	selected = &*pointList.insert(iter, ColorPoint(alpha, getColor(alpha)));
	colorbutton->setVisible(true);
	colorbutton->setColor(selected->color);
	emit gradientChanged();
}

void ColorGradient::removeColorPoint(ColorPoint& pointToRemove)
{
	pointList.erase(std::remove_if(pointList.begin(), pointList.end(),
			[&pointToRemove](ColorPoint& point) {return &point == &pointToRemove; }));
	selected = nullptr;
	emit gradientChanged();
}

void ColorGradient::setMode(ColorMode mode)
{
	this->mode = mode;
	update();
	emit gradientChanged();
	interpolationCombobox->setEnabled(mode != ColorGradient::ColorMode::Discrete);
}

void ColorGradient::setInterpolationMode(ColorInterpolationMode mode)
{
	this->interpolationMode = mode;
	update();
	emit gradientChanged();
}

void ColorGradient::paintEvent(QPaintEvent* event)
{
	QPainter painter(this);
	painter.setRenderHint(QPainter::RenderHint::HighQualityAntialiasing);
	QRect colorExampleRect(QPoint(rect().left() + LeftRightGap, ColorExampleStart), QPoint(rect().right() - LeftRightGap, ColorExmapleHeight));
	painter.drawRect(colorExampleRect);
	//painter.drawRect()
	//drawSlider
	QPen sliderPen;
	sliderPen.setWidth(2);
	painter.setPen(Qt::NoPen);
	
	
	for (ColorPoint& point : pointList) {
		QPainterPath slider;
		if (&point == selected) {
			sliderPen.setColor(QColor(0, 0, 0));
			painter.setPen(sliderPen);
		}
		painter.setBrush(point.color);
		int middlePosition = util::linearInterpolate(colorExampleRect.left(), colorExampleRect.right(), point.alpha);
		slider.moveTo(middlePosition, SliderBeginHeight);
		slider.lineTo(middlePosition - SliderPeak, SliderBeginHeight + SliderPeak);
		slider.lineTo(middlePosition - SliderPeak, SliderBeginHeight + SliderPeak + SliderBody);
		slider.lineTo(middlePosition + SliderPeak, SliderBeginHeight + SliderPeak + SliderBody);
		slider.lineTo(middlePosition + SliderPeak, SliderBeginHeight + SliderPeak);
		slider.closeSubpath();
		painter.drawPath(slider);
		if (&point == selected) {
			painter.setPen(Qt::NoPen);
		}
	}
	for (int pixelX = colorExampleRect.left() + 1; pixelX < colorExampleRect.right()+1; pixelX++) {
		double alpha = util::inverseLinearInterpolation(colorExampleRect.left(), colorExampleRect.right()+1, pixelX);
		QColor color = getColor(alpha);
		painter.setPen(color);
		painter.drawLine(pixelX, colorExampleRect.top() + 1, pixelX, colorExampleRect.bottom());
	}
	if (selected != nullptr) {
		QRect currentSelected(QPoint(rect().right()/2 , SelectedHeight), QPoint(rect().right() / 2 + SelectedPreview, SelectedHeight + SelectedPreview));
		painter.fillRect(currentSelected, selected->color);
	}




}

void ColorGradient::mousePressEvent(QMouseEvent* event)
{
	QPoint mousePos = event->pos();
	QRect area(QPoint(rect().left() + LeftRightGap, ColorExmapleHeight), QPoint(rect().right() - LeftRightGap, ColorExmapleHeight+SliderBody+SliderPeak));
	if (area.contains(mousePos)) {

		// Check for existing color point
		// Select with left mouse Button
		// Remove Point with right mouse Button
		for (ColorPoint& point : pointList) {
			double posX = util::linearInterpolate(area.left(), area.right() + 1, point.alpha);
			if (posX - SliderPeak * 2 < mousePos.x() && mousePos.x() < posX + SliderPeak * 2) {
				if (event->buttons() & Qt::LeftButton) {
					selected = &point;
					colorbutton->setVisible(true);
					colorbutton->setColor(selected->color);
				}
				else if(event->buttons() & Qt::RightButton){
					removeColorPoint(point);
					colorbutton->setVisible(false);
				}
				update();
				return;
			}
		}
		


		// Insert new Color Point
		double alpha = util::inverseLinearInterpolation(area.left(), area.right() + 1, mousePos.x());
		addColorPoint(alpha);
		update();
	}
}

void ColorGradient::mouseMoveEvent(QMouseEvent* event)
{
	if (selected == nullptr) return;
	//TODO(Bug #1) when mouse press button mouse move gets emitted and move the selected
	//currently a workarround to not move the selected colorpoint when selecting a button
	if (event->pos().y() < ColorExmapleHeight) return;
	QPoint mousePos = event->pos();
	QRect area(QPoint(rect().left() + LeftRightGap, ColorExmapleHeight), QPoint(rect().right() - LeftRightGap, ColorExmapleHeight + SliderBody + SliderPeak));
	double alpha = util::inverseLinearInterpolation(area.left(), area.right() + 1, mousePos.x());
	selected->alpha = std::clamp(alpha, 0.0, 1.0);

	pointList.sort([](ColorPoint& a, ColorPoint& b) {
		return a.alpha < b.alpha;
		});
	emit gradientChanged();
	update();
}
