/**
 * This file is a part of Luminance HDR package.
 * ---------------------------------------------------------------------- 
 * Copyright (C) 2006,2007 Giuseppe Rota
 * Copyright (C) 2002-2005 Nicholas Phillips
 * 
 *  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 for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * ---------------------------------------------------------------------- 
 *
 * based on previous GPL code from qpfstmo, by Nicholas Phillips.
 *
 * Original Work
 * @author Giuseppe Rota <grota@users.sourceforge.net>
 * 
 * Improvements, bugfixing
 * @author Franco Comida <fcomida@users.sourceforge.net>
 *
 */

#include <math.h>

#include "Gang.h"

//#include <iostream>
using namespace std;

Gang::Gang(QSlider* slider, QDoubleSpinBox* doublespinbox, 
		QCheckBox *chkbox1, QCheckBox *chkbox2, QRadioButton *rb1, QRadioButton *rb2,
		const float minvalue, const float maxvalue, 
		const float vv, const bool logs) : 
		s(slider), dsb(doublespinbox), cbx1(chkbox1), cbx2(chkbox2), rb1(rb1), rb2(rb2),
		minv(minvalue), maxv(maxvalue), defaultv(vv), logscaling(logs),
		undoState(false), redoState(false)
{
	if (cbx1)
		isCbx1Checked_default = cbx1->isChecked();
	if (cbx2)
		isCbx2Checked_default = cbx2->isChecked();
	if (rb1)
		isRb1Checked_default = rb1->isChecked();
	if (rb2)
		isRb2Checked_default = rb2->isChecked();

	tmoSettingsList = new TmoSettingsList();
	graphics_only = false;

	if (s)
		s->setTracking(false);
	
	if (s) {	
		connect( s, SIGNAL(sliderMoved(int)),  this, SLOT(sliderMoved(int)));
		connect( s, SIGNAL(valueChanged(int)), this, SLOT(sliderValueChanged(int)));
	}

	if (dsb)
		//connect( dsb, SIGNAL(editingFinished()), this, SLOT(spinboxFocusEnter()));
		connect( dsb, SIGNAL(valueChanged(double)), this, SLOT(spinboxValueChanged(double)));

	if (cbx1)
		connect( cbx1, SIGNAL(toggled(bool)), this, SLOT(checkBox1Checked(bool)));
	
	if (cbx2)
		connect( cbx2, SIGNAL(toggled(bool)), this, SLOT(checkBox2Checked(bool)));

	if (rb1)
		connect( rb1, SIGNAL(clicked(bool)), this, SLOT(radioButton1Checked(bool)));

	if (rb2)
		connect( rb2, SIGNAL(clicked(bool)), this, SLOT(radioButton2Checked(bool)));
	
	setDefault();
}

Gang::~Gang()
{
    delete tmoSettingsList;
} 

float Gang::p2v(const int p) const
{
	float x = (p-s->minimum())/( (float) (s->maximum() - s->minimum() ) ) ;
	if( logscaling ) {
		//cout << "p:  " << p << ", x:  " << x << ", " << minv*exp(log(maxv/minv)*x ) << endl;
		return minv*exp(log(maxv/minv)*x );
	}
	return (maxv-minv)*x + minv; 
}

int Gang::v2p(const float x) const
{
	float y = (x - minv)/(maxv - minv);
	if( logscaling ) {
		y = (log(x)-log(minv))/(log(maxv)-log(minv));
		//cout << "x:  " << x << ", y:  " << y << ", " << log(x) << endl;
	}
	return  (int) ( (s->maximum() - s->minimum() )*y + s->minimum() );
}

void Gang::sliderMoved(int p)
{
	//qDebug("Slider moved");
	if( value_from_text ) {
		value_from_text = false;
	//qDebug("bailing out");
		return;
	}
	value_from_slider = true;
	value = p2v(p);
	dsb->setValue(value);
	changed_ = true;
	value_from_slider = false;
}
void Gang::sliderValueChanged(int p)
{
	//qDebug("Slider changed");
	if( value_from_text ) {
		value_from_text = false;
	//qDebug("bailing out");
		if (!graphics_only)
			emit finished();
		return;
	}
	value_from_slider = true;
	value = p2v(p);
	dsb->setValue(value);
	value = dsb->value();
	changed_ = true;
	value_from_slider = false;
	if (!graphics_only)
		emit finished();
}

// void Gang::spinboxFocusEnter()
// {
// 	qDebug("Spinbox lost_focus/enter");
// 	if( value_from_slider ) {
// 		value_from_slider = false;
// 		qDebug("bailing out");
// 		return;
// 	}
// 	double x = dsb->value();
// 	value = x;
// // 	value_from_text = true;
// 	qDebug("0");
// 	s->setValue( v2p(value ) );
// 	qDebug("1");
// 	changed_ = true;
// 	return;
// }

void Gang::spinboxValueChanged(double x)
{
	//qDebug("Spinbox value_changed");
	if( value_from_slider ) {
		value_from_slider = false;
	//qDebug("bailing out");
		return;
	}
	value = x;
	value_from_text = true;
	s->setValue( v2p(value ) );
	changed_ = true;
}

void Gang::checkBox1Checked(bool b) {
	isCbx1Checked = b;
}

void Gang::checkBox2Checked(bool b) {
	isCbx2Checked = b;
}

void Gang::radioButton1Checked(bool b) {
	isRb1Checked = b;
	isRb2Checked = !b;
	//cout << "isRb1Checked: " << b << endl;
	//cout << "isRb2Checked: " << !b << endl;
}

void Gang::radioButton2Checked(bool b) {
	isRb2Checked = b;
	isRb1Checked = !b;
	//cout << "isRb1Checked: " << !b << endl;
	//cout << "isRb2Checked: " << b << endl;
}

void Gang::setDefault()
{
// 	qDebug("def");
	graphics_only = true;
	value = defaultv;
	value_from_slider = true;
	if (dsb) {
		dsb->setValue(value);
		value = dsb->value();
	}
	value_from_text = true;
	if (s)
		s->setValue( v2p(value) );
	changed_ = false;
	value_from_text = false;
	value_from_slider = false;
	graphics_only = false;
	if (cbx1) 
		isCbx1Checked = isCbx1Checked_default;
	if  (cbx2) 
		isCbx2Checked = isCbx2Checked_default;
	if (rb1) 
		isRb1Checked = isRb1Checked_default;
	if (rb2) 
		isRb2Checked = isRb2Checked_default;

	//cout << "Gang::setDefault()" << endl;
	//cout << "v: " << value << endl;
	//cout << "cbx1: " << isCbx1Checked << endl;
	//cout << "cbx2: " << isCbx2Checked << endl;
	//cout << "rb1: " << isRb1Checked << endl;
	//cout << "rb2: " << isRb2Checked << endl;
	//cout << "/Gang::setDefault()" << endl;
}

QString Gang::flag(const QString f) const
{
    if( ! changed() )
	return "";
    return QString(" %1 %2").arg(f).arg(value);
}

QString Gang::fname(const QString f) const
{
    if( ! changed() )
	return "";
    return QString(".%1%2").arg(f).arg(value);
}

void Gang::setupUndo() {
	bool isCbx1Checked = false;
	bool isCbx2Checked = false;
	bool isRb1Checked = false;
	bool isRb2Checked = false;
	float v = 0.0;
	
	if (s)
		v = value;
	if (cbx1) 
		isCbx1Checked = cbx1->isChecked();
	if  (cbx2) 
		isCbx2Checked = cbx2->isChecked();
	if (rb1) 
		isRb1Checked = rb1->isChecked();
	if (rb2) 
		isRb2Checked = rb2->isChecked();

        tmoSettingsList->append( TmoSettings(this, v, isCbx1Checked, isCbx2Checked, isRb1Checked, isRb2Checked) );
	if (tmoSettingsList->index() == 1) {
		emit enableUndo(true);
		undoState = true;
	}
	if (tmoSettingsList->index() == tmoSettingsList->size() - 1) {
		emit enableRedo(false);
		redoState = false;
	}

	//cout << "Gang::setupUndo()" << endl;
	//cout << "size: " << tmoSettingsList->size() << endl;
	//cout << "index: " << tmoSettingsList->index() << endl;
	//cout << "v: " << v << endl;
	//cout << "cbx1: " << isCbx1Checked << endl;
	//cout << "cbx2: " << isCbx2Checked << endl;
	//cout << "rb: " << isRbChecked << endl;
	//cout << "/Gang::setupUndo()" << endl;
}

void Gang::undo() {
	//cout << "Gang::undo(): size: " << tmoSettingsList->size() << endl;
	//cout << "Gang::undo(): index: " << tmoSettingsList->index() << endl;
	if (tmoSettingsList->index() == tmoSettingsList->size() - 1) {
		emit enableRedo(true);
		redoState = true;
	}
	
	tmoSettingsList->previous();

	if (tmoSettingsList->index() == 0) {
		emit enableUndo(false);
		undoState = false;
	}
	//cout << "/Gang::undo(): size: " << tmoSettingsList->size() << endl;
	//cout << "/Gang::undo(): index: " << tmoSettingsList->index() << endl;
}

void Gang::redo() {
	//cout << "Gang::redo()" << endl;
	//cout << "Gang::redo(): size: " << tmoSettingsList->size() << endl;
	//cout << "Gang::redo(): index: " << tmoSettingsList->index() << endl;
	if (tmoSettingsList->index() == 0) {
		emit enableUndo(true);
		undoState = true;
	}

	tmoSettingsList->next();

	if (tmoSettingsList->index() == tmoSettingsList->size() - 1) {
		emit enableRedo(false);
		redoState = false;
	}

	//cout << "/Gang::redo(): size: " << tmoSettingsList->size() << endl;
	//cout << "/Gang::redo(): index: " << tmoSettingsList->index() << endl;
}

void Gang::updateUndoState() {
	//cout << "Gang::updateUndoState()" << endl;
	//cout << "undoState: " << undoState << endl;
	//cout << "redoState: " << redoState << endl;
	//cout << "/Gang::updateUndoState()" << endl;
	emit enableUndo(undoState);
	emit enableRedo(redoState);
}

//
//===================================== Undo/Redo ============================================
//
TmoSettings::TmoSettings(Gang *gangPtr, float v, bool isCbx1, bool isCbx2, bool isRB1C, bool isRB2C):
	gangPtr(gangPtr)
{
	if (gangPtr->cbx1) {
		isCbx1Checked = isCbx1;
	}
	if (gangPtr->cbx2) {
		isCbx2Checked = isCbx2;
	}
	if (gangPtr->rb1) {
		isRb1Checked = isRB1C;

	}
	if (gangPtr->rb2) {
		isRb2Checked = isRB2C;

	}
	if (gangPtr->s) {
		value = v;
	}
}

void TmoSettings::apply() const {
	//cout << "TmoSettings::apply()" << endl;
 	if (gangPtr->s) 
		gangPtr->s->setValue(gangPtr->v2p(value));
	if (gangPtr->dsb) {
		gangPtr->dsb->setValue(value);
		gangPtr->value = gangPtr->dsb->value();	
	}
	if (gangPtr->cbx1)
		gangPtr->cbx1->setChecked(isCbx1Checked);
	if (gangPtr->cbx2)
		gangPtr->cbx2->setChecked(isCbx2Checked);
	if (gangPtr->rb1)
		gangPtr->rb1->setChecked(isRb1Checked);
	if (gangPtr->rb2)
		gangPtr->rb2->setChecked(isRb2Checked);
}

//
// TmoSettingsList Implementation
//
TmoSettingsList::TmoSettingsList(): QList<TmoSettings>(), m_index(-1) {}	
TmoSettingsList::~TmoSettingsList() {}

void TmoSettingsList::previous() {
	if (m_index > 0) {
		m_index--;
		at(m_index).apply();
	}
}

void TmoSettingsList::next() {
	if (m_index < size()) {
		m_index++;
		at(m_index).apply();
	}
}

int TmoSettingsList::index() {
	return m_index;
}

void TmoSettingsList::append(const TmoSettings &value) {
	QList<TmoSettings>::append(value);
	m_index++;
}
