/****************************************************************************
* MeshLab                                                           o o     *
* A versatile mesh processing toolbox                             o     o   *
*                                                                _   O  _   *
* Copyright(C) 2005                                                \/)\/    *
* Visual Computing Lab                                            /\/|      *
* ISTI - Italian National Research Council                           |      *
*                                                                    \      *
* All rights reserved.                                                      *
*                                                                           *
* This program is free software; you can redistribute it and/or modify      *   
* it under the terms of the GNU General Public License as published by      *
* the Free Software Foundation; either version 2 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 General Public License (http://www.gnu.org/licenses/gpl.txt)          *
* for more details.                                                         *
*                                                                           *
****************************************************************************/
/****************************************************************************
History
Revision 1.0 2008/02/20 Alessandro Maione, Federico Bellucci
FIRST RELEASE

****************************************************************************/

#include "eqhandle.h"
#include <QMouseEvent>
#include <QGraphicsSceneMouseEvent>
#include <math.h>

EqHandle::EqHandle(CHART_INFO *environment_info, QColor color, QPointF position, 
				   EQUALIZER_HANDLE_TYPE type, EqHandle** handles, qreal* midHandlePercentilePosition, QDoubleSpinBox* spinbox,
				   int zOrder, int size):
	Handle (environment_info, color, position, zOrder, size)
{
	setSize(size);
	_type = type;
	_barHeight = environment_info->chartHeight();

	_handlesPointer = handles;
	_midHandlePercentilePosition = midHandlePercentilePosition;
	_spinBoxPointer = spinbox;	

	// Setting triangle position above EqHandle
	_triangle[0] = QPointF(-_size/2,-_size);
	_triangle[1] = QPointF(_size/2, -_size);
	_triangle[2] = QPointF(0, -1.87f*_size);
}


EqHandle::~EqHandle(void)
{
}

void EqHandle::paint ( QPainter * painter, const QStyleOptionGraphicsItem * /*option*/, QWidget * /*widget*/ )
{
	painter->setPen(_color);
	painter->setBrush(_color);
	painter->drawLine(0, -_size, 0, -_barHeight);		// painting the vertical bar
	
	painter->drawPolygon ( _triangle, 3);				// painting the triangle above EqHandle
	painter->drawRect(-_size/2, -_size, _size, _size);	// painting the base rect of  EqHandle
}

QRectF EqHandle::boundingRect () const
{
	return QRectF(-_size/2, -_barHeight, _size, _barHeight);
}


/* Overload method implementing the movement of EqHandle
Note: Left [Right] handle can also move "outside" the histogram causing a stretching of it. 
Moving the handle again "inside" the histogram reset gradually its original size 
(the histogram is recalculated for every MouseMoveEvent call "outside" of it). 
The process of stretching is managed by QualityMapperDialog. 
The handle just notify its position relative to he histogram. 
*/
void EqHandle::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
	setCursor(Qt::OpenHandCursor);

	QPointF newPos = event->scenePos();
	qreal handleOffset = fabs(newPos.x()-pos().x());

	if (handleOffset >= std::numeric_limits<float>::epsilon())
	{
		switch (_type)
		{
		case MID_HANDLE:
			// Mid handle can move only between left and right handle
			if ( (newPos.x() > _handlesPointer[LEFT_HANDLE]->pos().x()) && (newPos.x() < _handlesPointer[RIGHT_HANDLE]->pos().x()) )
			{
				*_midHandlePercentilePosition = calculateMidHandlePercentilePosition(newPos.x());
				moveMidHandle();
			}
			emit positionChanged(); // for gammaCorrectionLabel
			break;
		case LEFT_HANDLE:
			{
				// calculating new spinbox value associated to handle
				qreal newQualityValue = positionToQuality(newPos.x());
				if (newQualityValue < _spinBoxPointer->minimum()) 
					break;
				
				// Left handle can move only on the left of right handle
				if (newPos.x() < _handlesPointer[RIGHT_HANDLE]->pos().x()) 
				{					
					// Changing minimum/maximum value of others spinboxes
					_handlesPointer[RIGHT_HANDLE]->_spinBoxPointer->setMinimum(newQualityValue);
					_handlesPointer[MID_HANDLE]->_spinBoxPointer->setMinimum(newQualityValue);

					// Emitting new quality value of the handle to spinbox
					_spinBoxPointer->blockSignals(true);
					emit positionChangedToSpinBox((double)newQualityValue);
					_spinBoxPointer->blockSignals(false);

					// if left handle is "inside" histogram
					if ( newQualityValue >= _chartInfo->minX )
					{
						emit insideHistogram(this,true); // for redrawing equalizerHistogram bars, if needed
						setPos(newPos.x(), pos().y());
					}
					else
						emit insideHistogram(this,false);

					emit positionChanged();  // for redrawing transferFunctionScene and moving midHandle

				}
			}
			break;
		case RIGHT_HANDLE:
			{
				// calculating new spinbox value associated to handle
				qreal newQualityValue = positionToQuality(newPos.x());
				if (newQualityValue > _spinBoxPointer->maximum()) // Why this control??? It seems to be never reached!
					break;

				// Right handle can move only on the right of left handle
				if (newPos.x() > _handlesPointer[LEFT_HANDLE]->pos().x()) 
				{		
					// Changing minimum/maximum value of others spinboxes
					_handlesPointer[LEFT_HANDLE]->_spinBoxPointer->setMaximum(newQualityValue);
					_handlesPointer[MID_HANDLE]->_spinBoxPointer->setMaximum(newQualityValue);

					// Emitting new quality value of the handle to spinbox
					_spinBoxPointer->blockSignals(true);
					emit positionChangedToSpinBox((double)newQualityValue);
					_spinBoxPointer->blockSignals(false);

					// if right handle is "inside" histogram
					if ( newQualityValue <= _chartInfo->maxX )
					{
						emit insideHistogram(this,true); // for redrawing equalizerHistogram bars, if needed
						setPos(newPos.x(), pos().y());
					}
					else
						emit insideHistogram(this,false);

					emit positionChanged();  // for redrawing transferFunctionScene and moving midHandle
				}
			}
			break;
		case NUMBER_OF_EQHANDLES:
			break;
		}
	}
}

/* 
Method for moving midHandle and setting its associated spinbox
*/
void EqHandle::moveMidHandle()
{
	assert(_type==MID_HANDLE);
	// Calculating new absolute position of the mid handle depending on its relative pos to the other handles
	qreal newPosX = _handlesPointer[LEFT_HANDLE]->pos().x() + *_midHandlePercentilePosition * (_handlesPointer[RIGHT_HANDLE]->pos().x() - _handlesPointer[LEFT_HANDLE]->pos().x());
	setPos(newPosX, pos().y());
	qreal newSpinboxValue = positionToQuality(newPosX);

	// Emitting new quality value of the handle to spinbox
	_spinBoxPointer->blockSignals(true);
	emit positionChangedToSpinBox((double)newSpinboxValue);
	_spinBoxPointer->blockSignals(false);

	//emit positionChanged(); // for gammaCorrectionLabel
}

/* 
Method invoked by spinbox value changing. Updates the position of the associated handle.
*/
void EqHandle::setXBySpinBoxValueChanged(double spinBoxValue)
{
	// Min and Max quality values are normally the values of the min e max spinboxes, but they are different if an handle is "outside" the histogram
	float maxQ = (_handlesPointer[RIGHT_HANDLE]->_spinBoxPointer->value() > _chartInfo->maxX)?_handlesPointer[RIGHT_HANDLE]->_spinBoxPointer->value():_chartInfo->maxX;
	float minQ = (_handlesPointer[LEFT_HANDLE]->_spinBoxPointer->value()  < _chartInfo->minX)?_handlesPointer[LEFT_HANDLE]->_spinBoxPointer->value() :_chartInfo->minX;
	qreal newHandleX;

	switch (_type)
	{
	case MID_HANDLE:
		newHandleX = qualityToPosition(spinBoxValue, minQ, maxQ);
		if ( (newHandleX > _handlesPointer[LEFT_HANDLE]->pos().x()) && (newHandleX < _handlesPointer[RIGHT_HANDLE]->pos().x()) )
		{
			*_midHandlePercentilePosition = calculateMidHandlePercentilePosition(newHandleX);
			moveMidHandle();
			emit positionChanged();  // for redrawing transferFunctionScene and moving midHandle
		}
		break;
	case LEFT_HANDLE:
		newHandleX = qualityToPosition(spinBoxValue, _chartInfo->minX, _chartInfo->maxX);
		if (newHandleX < _handlesPointer[RIGHT_HANDLE]->pos().x()) 
		{
			// Changing minimum/maximum value of others spinboxes
			_handlesPointer[RIGHT_HANDLE]->_spinBoxPointer->setMinimum(spinBoxValue);
			_handlesPointer[MID_HANDLE]->_spinBoxPointer->setMinimum(spinBoxValue);

			// if left handle is "inside" histogram
			if ( newHandleX >= _chartInfo->leftBorder() )
			{
				setPos(newHandleX, pos().y());
				emit insideHistogram(this,true); // for redrawing equalizerHistogram bars, if needed
			}
			else
			{
				setPos(_chartInfo->leftBorder(), pos().y());
				emit insideHistogram(this,false);
			}


			emit positionChanged();  // for redrawing transferFunctionScene and moving midHandle
		}
		break;
	case RIGHT_HANDLE:
		newHandleX = qualityToPosition(spinBoxValue, _chartInfo->minX, _chartInfo->maxX);
		if (newHandleX > _handlesPointer[LEFT_HANDLE]->pos().x()) 
		{
			// Changing minimum/maximum value of other spinboxes
			_handlesPointer[LEFT_HANDLE]->_spinBoxPointer->setMaximum(spinBoxValue);
			_handlesPointer[MID_HANDLE]->_spinBoxPointer->setMaximum(spinBoxValue);

			// if right handle is "inside" histogram
			if ( newHandleX <= _chartInfo->rightBorder() )
			{
				setPos(newHandleX, pos().y());
				emit insideHistogram(this,true); // invalidates equalizerHistogram scene
			}
			else
			{
				setPos(_chartInfo->rightBorder(), pos().y());
				emit insideHistogram(this,false);
			}



			emit positionChanged();  // for redrawing transferFunctionScene and moving midHandle
		}
		break;
	case NUMBER_OF_EQHANDLES:
		break;
	}
}

