/*
    QGLView
    Ivan Pelipenko peri4ko@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this programap->  If not, see <http://www.gnu.org/licenses/>.
*/

#include "scroll_spin_box.h"

#include "ui_scroll_spin_box.h"

#include <QApplication>
#include <QDebug>
#include <QMouseEvent>
#include <QStyle>
#include <QStyleOptionSpinBox>
#include <evalspinbox.h>
#include <qad_types.h>


ScrollSpinBox::ScrollSpinBox(QWidget * parent): QWidget(parent) {
	ui = new Ui::ScrollSpinBox();
	ui->setupUi(this);
	ui->spin->setPrecision(3);
	ui->handle->installEventFilter(this);
	connect(ui->spin, SIGNAL(valueChanged(double)), this, SIGNAL(valueChanged(double)));
	last_value   = 0.;
	sensivity_   = 0.2;
	m_maximum    = 2147483647;
	m_minimum    = -m_maximum;
	scroll_scale = sensivity_ / 10;
	canceled     = false;
}


EvalSpinBox * ScrollSpinBox::spin() {
	return ui->spin;
}


double ScrollSpinBox::value() const {
	return ui->spin->value();
}


void ScrollSpinBox::setReadOnly(bool r) {
	m_read_only = r;
	ui->spin->setReadOnly(r);
}


void ScrollSpinBox::changeEvent(QEvent * e) {
	QWidget::changeEvent(e);
	switch (e->type()) {
	case QEvent::LanguageChange: ui->retranslateUi(this); break;
	default: break;
	}
}


void ScrollSpinBox::resizeEvent(QResizeEvent * e) {
	QStyleOptionSpinBox so;
	so.initFrom(ui->spin);
	QRect r = qApp->style()->subControlRect(QStyle::CC_SpinBox, &so, QStyle::SC_SpinBoxUp);
	ui->handle->setFixedWidth(qMax<int>(ui->handle->height() / 3, r.width()));
	scroll_scale = sensivity_ / appScale(this) / 10;
}


bool ScrollSpinBox::eventFilter(QObject * o, QEvent * e) {
	switch (e->type()) {
	case QEvent::MouseButtonPress: mousePress((QMouseEvent *)e); return true;
	case QEvent::MouseButtonRelease: mouseRelease((QMouseEvent *)e); return true;
	case QEvent::MouseMove: mouseMove((QMouseEvent *)e); return true;
	default: break;
	}
	return QWidget::eventFilter(o, e);
}


void ScrollSpinBox::mousePress(QMouseEvent * e) {
	if (canceled || !isEnabled() || m_read_only) return;
	if (e->button() == Qt::RightButton) {
		canceled = true;
		ui->spin->setExpression(last_text);
	}
	if (e->button() == Qt::LeftButton) {
		down_pos   = e->pos();
		last_text  = ui->spin->expression();
		last_value = qAbs(ui->spin->value());
		if (last_value == 0.) last_value = 1.;
	}
}


void ScrollSpinBox::mouseRelease(QMouseEvent * e) {
	if (!isEnabled() || m_read_only) return;
	if (e->buttons() == Qt::NoButton) canceled = false;
}


void ScrollSpinBox::mouseMove(QMouseEvent * e) {
	if (canceled || !isEnabled() || m_read_only) return;
	if (e->buttons().testFlag(Qt::LeftButton)) {
		double dv = (down_pos.y() - e->pos().y()) * scroll_scale;
		if (dv != 0.) {
			QCursor::setPos(ui->handle->mapToGlobal(down_pos));
			setValue(ui->spin->value() + last_value * dv);
		}
	}
}


void ScrollSpinBox::setValue(double v) {
	ui->spin->setValue(qBound(m_minimum, v, m_maximum));
}


void ScrollSpinBox::setMinimum(int minimum) {
	m_minimum = minimum;
	setValue(value());
}


void ScrollSpinBox::setMaximum(int maximum) {
	m_maximum = maximum;
	setValue(value());
}
