#include "rangeslider.h"

#include "qad_types.h"

#include <QPalette>


RangeSlider::RangeSlider(QWidget * parent)
	: QWidget(parent)
	, m_minimum(0)
	, m_maximum(100)
	, m_lowerValue(0)
	, m_upperValue(100)
	, mFirstHandlePressed(false)
	, mSecondHandlePressed(false)
	, orientation(Qt::Horizontal)
	, m_mode(DoubleHandles) {
	init();
}


RangeSlider::RangeSlider(Qt::Orientation ori, Mode m, QWidget * parent)
	: QWidget(parent)
	, m_minimum(0)
	, m_maximum(100)
	, m_lowerValue(0)
	, m_upperValue(100)
	, mFirstHandlePressed(false)
	, mSecondHandlePressed(false)
	, orientation(ori)
	, m_mode(m) {
	init();
}


void RangeSlider::init() {
	setMouseTracking(true);
	scHandleSideLength = 16 * lineThickness(this);
	scSliderBarHeight  = 8 * lineThickness(this);
	scLeftRightMargin  = 2 * lineThickness(this);
}

void RangeSlider::paintEvent(QPaintEvent *) {
	QPainter painter(this);
	// Background
	QRectF backgroundRect;
	if (orientation == Qt::Horizontal)
		backgroundRect = QRectF(scLeftRightMargin, (height() - scSliderBarHeight) / 2, width() - scLeftRightMargin * 2, scSliderBarHeight);
	else
		backgroundRect = QRectF((width() - scSliderBarHeight) / 2, scLeftRightMargin, scSliderBarHeight, height() - scLeftRightMargin * 2);

	QPen pen(palette().color(QPalette::Disabled, QPalette::Text));
	painter.setPen(pen);
	if (isEnabled()) {
		painter.setBrush(palette().color(QPalette::Active, QPalette::Base));
	} else {
		painter.setBrush(palette().color(QPalette::Disabled, QPalette::Base));
	}
	painter.drawRect(backgroundRect);

	// First value handle rect
	painter.setPen(pen);
	painter.setRenderHint(QPainter::Antialiasing);
	painter.setBrush(palette().color(QPalette::Active, QPalette::Button));
	QRectF leftHandleRect = firstHandleRect();
	if (m_mode.testFlag(LeftHandle)) painter.drawEllipse(leftHandleRect);

	// Second value handle rect
	QRectF rightHandleRect = secondHandleRect();
	if (m_mode.testFlag(RightHandle)) painter.drawEllipse(rightHandleRect);

	// Handles
	painter.setRenderHint(QPainter::Antialiasing, false);
	QRectF selectedRect(backgroundRect);
	if (orientation == Qt::Horizontal) {
		selectedRect.setLeft((m_mode.testFlag(LeftHandle) ? leftHandleRect.right() : leftHandleRect.left()) + 0.5);
		selectedRect.setRight((m_mode.testFlag(RightHandle) ? rightHandleRect.left() : rightHandleRect.right()) - 0.5);
	} else {
		selectedRect.setTop((m_mode.testFlag(LeftHandle) ? leftHandleRect.bottom() : leftHandleRect.top()) + 0.5);
		selectedRect.setBottom((m_mode.testFlag(RightHandle) ? rightHandleRect.top() : rightHandleRect.bottom()) - 0.5);
	}
	if (isEnabled()) {
		painter.setBrush(palette().color(QPalette::Active, QPalette::Highlight));
	} else {
		painter.setBrush(palette().color(QPalette::Disabled, QPalette::Text));
	}
	painter.drawRect(selectedRect);
}


QRectF RangeSlider::firstHandleRect() const {
	float percentage = (m_lowerValue - m_minimum) * 1.0 / interval();
	return handleRect(percentage * validLength() + scLeftRightMargin);
}


QRectF RangeSlider::secondHandleRect() const {
	float percentage = (m_upperValue - m_minimum) * 1.0 / interval();
	return handleRect(percentage * validLength() + scLeftRightMargin + (m_mode.testFlag(LeftHandle) ? scHandleSideLength : 0));
}


QRectF RangeSlider::handleRect(int value) const {
	if (orientation == Qt::Horizontal)
		return QRect(value, (height() - scHandleSideLength) / 2, scHandleSideLength, scHandleSideLength);
	else
		return QRect((width() - scHandleSideLength) / 2, value, scHandleSideLength, scHandleSideLength);
}


void RangeSlider::mousePressEvent(QMouseEvent * e) {
	if (e->buttons() & Qt::LeftButton) {
		int posCheck, posMax, posValue, firstHandleRectPosValue, secondHandleRectPosValue;
		posCheck                 = (orientation == Qt::Horizontal) ? e->pos().y() : e->pos().x();
		posMax                   = (orientation == Qt::Horizontal) ? height() : width();
		posValue                 = (orientation == Qt::Horizontal) ? e->pos().x() : e->pos().y();
		firstHandleRectPosValue  = (orientation == Qt::Horizontal) ? firstHandleRect().x() : firstHandleRect().y();
		secondHandleRectPosValue = (orientation == Qt::Horizontal) ? secondHandleRect().x() : secondHandleRect().y();

		mSecondHandlePressed     = secondHandleRect().contains(e->pos());
		mFirstHandlePressed      = !mSecondHandlePressed && firstHandleRect().contains(e->pos());
		if (mFirstHandlePressed) {
			mDelta = posValue - (firstHandleRectPosValue + scHandleSideLength / 2);
		} else if (mSecondHandlePressed) {
			mDelta = posValue - (secondHandleRectPosValue + scHandleSideLength / 2);
		}
		if (posCheck >= 2 && posCheck <= posMax - 2) {
			int step = interval() / 10 < 1 ? 1 : interval() / 10;
			if (posValue < firstHandleRectPosValue)
				setLowerValue(m_lowerValue - step);
			else if (((posValue > firstHandleRectPosValue + scHandleSideLength) || !m_mode.testFlag(LeftHandle)) &&
			         ((posValue < secondHandleRectPosValue) || !m_mode.testFlag(RightHandle))) {
				if (m_mode.testFlag(DoubleHandles)) {
					if (posValue - (firstHandleRectPosValue + scHandleSideLength) <
					    (secondHandleRectPosValue - (firstHandleRectPosValue + scHandleSideLength)) / 2) {
						setLowerValue((m_lowerValue + step < m_upperValue) ? m_lowerValue + step : m_upperValue);
					} else {
						setUpperValue((m_upperValue - step > m_lowerValue) ? m_upperValue - step : m_lowerValue);
					}
				} else if (m_mode.testFlag(LeftHandle)) {
					setLowerValue((m_lowerValue + step < m_upperValue) ? m_lowerValue + step : m_upperValue);
				} else if (m_mode.testFlag(RightHandle)) {
					setUpperValue((m_upperValue - step > m_lowerValue) ? m_upperValue - step : m_lowerValue);
				}
			} else if (posValue > secondHandleRectPosValue + scHandleSideLength)
				setUpperValue(m_upperValue + step);
		}
	}
}


void RangeSlider::mouseMoveEvent(QMouseEvent * e) {
	if (e->buttons() & Qt::LeftButton) {
		int posValue, firstHandleRectPosValue, secondHandleRectPosValue;
		posValue                 = (orientation == Qt::Horizontal) ? e->pos().x() : e->pos().y();
		firstHandleRectPosValue  = (orientation == Qt::Horizontal) ? firstHandleRect().x() : firstHandleRect().y();
		secondHandleRectPosValue = (orientation == Qt::Horizontal) ? secondHandleRect().x() : secondHandleRect().y();
		if (mFirstHandlePressed && m_mode.testFlag(LeftHandle)) {
			if (posValue - mDelta + scHandleSideLength / 2 <= secondHandleRectPosValue) {
				setLowerValue((posValue - mDelta - scLeftRightMargin - scHandleSideLength / 2) * 1.0 / validLength() * interval() +
				              m_minimum);
			} else {
				setLowerValue(m_upperValue);
			}
		} else if (mSecondHandlePressed && m_mode.testFlag(RightHandle)) {
			if (firstHandleRectPosValue + scHandleSideLength * (m_mode.testFlag(DoubleHandles) ? 1.5 : 0.5) <= posValue - mDelta) {
				setUpperValue((posValue - mDelta - scLeftRightMargin - scHandleSideLength / 2 -
				               (m_mode.testFlag(DoubleHandles) ? scHandleSideLength : 0)) *
				                  1.0 / validLength() * interval() +
				              m_minimum);
			} else {
				setUpperValue(m_lowerValue);
			}
		}
	}
}


void RangeSlider::mouseReleaseEvent(QMouseEvent *) {
	mFirstHandlePressed  = false;
	mSecondHandlePressed = false;
}


void RangeSlider::changeEvent(QEvent * e) {
	if (e->type() == QEvent::EnabledChange) {
		update();
	}
}


QSize RangeSlider::minimumSizeHint() const {
	return QSize(scHandleSideLength * 2 + scLeftRightMargin * 2, scHandleSideLength);
}


void RangeSlider::setLowerValue(int value) {
	if (value > m_maximum) value = m_maximum;
	if (value < m_minimum) value = m_minimum;
	m_lowerValue = value;
	emit lowerValueChanged(m_lowerValue);
	emit valueChanged(m_lowerValue, m_upperValue);
	update();
}


void RangeSlider::setUpperValue(int value) {
	if (value > m_maximum) value = m_maximum;
	if (value < m_minimum) value = m_minimum;
	m_upperValue = value;
	emit upperValueChanged(m_upperValue);
	emit valueChanged(m_lowerValue, m_upperValue);
	update();
}


void RangeSlider::setMinimum(int minimum) {
	if (minimum <= m_maximum) {
		m_minimum = minimum;
	} else {
		int oldMax = m_maximum;
		m_minimum  = oldMax;
		m_maximum  = minimum;
	}
	update();
	setLowerValue(m_minimum);
	setUpperValue(m_maximum);
	emit minimumChanged(m_maximum);
	emit rangeChanged(m_minimum, m_maximum);
}


void RangeSlider::setMaximum(int maximum) {
	if (maximum >= m_minimum) {
		m_maximum = maximum;
	} else {
		int oldMin = m_minimum;
		m_maximum  = oldMin;
		m_minimum  = maximum;
	}
	update();
	setLowerValue(m_minimum);
	setUpperValue(m_maximum);
	emit maximumChanged(m_maximum);
	emit rangeChanged(m_minimum, m_maximum);
}


int RangeSlider::validLength() const {
	int len = (orientation == Qt::Horizontal) ? width() : height();
	return len - scLeftRightMargin * 2 - scHandleSideLength * (m_mode.testFlag(DoubleHandles) ? 2 : 1);
}


int RangeSlider::interval() const {
	return m_maximum - m_minimum;
}


void RangeSlider::setRange(int minimum, int maximum) {
	setMinimum(minimum);
	setMaximum(maximum);
}


void RangeSlider::setMode(Mode m) {
	m_mode = m;
	update();
	emit modeChanged(m_mode);
}
