/*
 ** This file is part of the ViTE project.
 **
 ** This software is governed by the CeCILL-A license under French law
 ** and abiding by the rules of distribution of free software. You can
 ** use, modify and/or redistribute the software under the terms of the
 ** CeCILL-A license as circulated by CEA, CNRS and INRIA at the following
 ** URL: "http://www.cecill.info".
 **
 ** As a counterpart to the access to the source code and rights to copy,
 ** modify and redistribute granted by the license, users are provided
 ** only with a limited warranty and the software's author, the holder of
 ** the economic rights, and the successive licensors have only limited
 ** liability.
 **
 ** In this respect, the user's attention is drawn to the risks associated
 ** with loading, using, modifying and/or developing or reproducing the
 ** software by the user in light of its specific status of free software,
 ** that may mean that it is complicated to manipulate, and that also
 ** therefore means that it is reserved for developers and experienced
 ** professionals having in-depth computer knowledge. Users are therefore
 ** encouraged to load and test the software's suitability as regards
 ** their requirements in conditions enabling the security of their
 ** systems and/or data to be ensured and, more generally, to use and
 ** operate it in the same conditions as regards security.
 **
 ** The fact that you are presently reading this means that you have had
 ** knowledge of the CeCILL-A license and that you accept its terms.
 **
 **
 ** ViTE developers are (for version 0.* to 1.0):
 **
 **        - COULOMB Kevin
 **        - FAVERGE Mathieu
 **        - JAZEIX Johnny
 **        - LAGRASSE Olivier
 **        - MARCOUEILLE Jule
 **        - NOISETTE Pascal
 **        - REDONDY Arthur
 **        - VUCHENER Clément
 **
 */
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <map>
#include <set>
#include <list>
#include <stack>
#include <vector>

/* -- */
#include "common/common.hpp"
#include "common/Info.hpp"
#include "common/Message.hpp"
#include "interface/resource.hpp"
#include "interface/Interface.hpp"

#include "core/Core.hpp"
#include "interface/Interface_graphic.hpp"
/* -- */
#include "trace/values/Values.hpp"
#include "trace/EntityValue.hpp"
#include "trace/EntityTypes.hpp"
#include "trace/Entitys.hpp"
#include "trace/tree/Interval.hpp"
#include "trace/Container.hpp"
/* -- */
#include <QKeyEvent>

#include "interface/Interval_select.hpp"

#undef max
#include <limits>

using namespace std;

Interval_select::Interval_select(Interface_graphic * console,QWidget *parent) : QDialog(parent), _trace(nullptr),_console(console) {
    setupUi(this);
    connect( minSpinBox, SIGNAL(valueChanged( double )), SLOT(minSpinBoxValueChanged( double )) );
    connect( maxSpinBox, SIGNAL(valueChanged( double )), SLOT(maxSpinBoxValueChanged( double )) );

    connect( interval_slider, SIGNAL(lowerValueChanged( int )), SLOT(minSliderValueChanged( int )) );
    connect( interval_slider, SIGNAL(upperValueChanged( int )), SLOT(maxSliderValueChanged( int )) );

    QMetaObject::connectSlotsByName(nullptr);

    _auto_refresh = auto_refresh_box->isChecked();
    _applied      = false;
}

Interval_select::~Interval_select() = default;

Trace* Interval_select::get_trace() {
    return _trace;
}

void Interval_select::set_trace(Trace *trace) {
    // Initialize _trace
    _trace = trace;

    min_value->setText(QString().setNum (Info::Entity::x_min));
    max_value->setText(QString().setNum (Info::Entity::x_max));

    minSpinBox->setMinimum(Info::Entity::x_min);
    minSpinBox->setMaximum(Info::Entity::x_max);
    maxSpinBox->setMinimum(Info::Entity::x_min);
    maxSpinBox->setMaximum(Info::Entity::x_max);

    //the slider stays in int, we will translate its values
    /*interval_slider->setMinimum(0);
     interval_slider->setMaximum(99);*/

    double delta=Info::Entity::x_max - Info::Entity::x_min;
    double step= 0.001 * delta; //make steps of .1% for spinboxes
    minSpinBox->setSingleStep(step);
    maxSpinBox->setSingleStep(step);

    update_values();
}

//called if a zoom is triggered outside of the window, to update slider position and spinboxes with the real values
void Interval_select::update_values() {

    if ( _trace != nullptr )
    {
        Element_pos _min;
        Element_pos _max;

        //choose which value to load the actual position from (can change if we are loading from a splitted file set)
        if(Info::Splitter::preview == true){
            _min = Info::Entity::x_min;
            _max = Info::Entity::x_max;
        }
        else if(Info::Splitter::load_splitted == true) {
            Info::Render::_x_min = Info::Splitter::_x_min;
            Info::Render::_x_max = Info::Splitter::_x_max;
            auto_refresh_box->setChecked(false);//set the checkbox to false because reload is expensive for splitted mode
            _min = Info::Splitter::_x_min;
            _max = Info::Splitter::_x_max;
        }
        else {
            _min = Info::Render::_x_min;
            _max = Info::Render::_x_max;
        }
        bool v  = minSpinBox->blockSignals(true);
        bool v2 = maxSpinBox->blockSignals(true);
        bool v3 = interval_slider->blockSignals(true);
        int maxvalue=int(_max*(interval_slider->maximum() - interval_slider->minimum())/(_trace->get_max_date() - minSpinBox->minimum()));
        interval_slider->setUpperValue(maxvalue);
        interval_slider->setLowerValue(int(_min*(interval_slider->maximum() - interval_slider->minimum())/(_trace->get_max_date() - minSpinBox->minimum())));
        maxSpinBox->setValue(_max);
        minSpinBox->setValue(_min);
        minSpinBox->blockSignals(v);
        maxSpinBox->blockSignals(v2);
        interval_slider->blockSignals(v3);
        _applied=false;
    }
}


void Interval_select::minSpinBoxValueChanged( double value )
{
    QPalette myPalette(minSpinBox->palette());

    if(value < maxSpinBox->value()) {

        bool v = interval_slider->blockSignals(true);
        double position = (interval_slider->maximum() - interval_slider->minimum())
            /             (minSpinBox->maximum()      - minSpinBox->minimum());
        position *= value;

        interval_slider->setLowerValue( int(position) );
        interval_slider->blockSignals(v);

        emit minValueChanged( minSpinBox->value() );

        myPalette.setColor(QPalette::Active, QPalette::Text,            Qt::black);
        myPalette.setColor(QPalette::Active, QPalette::HighlightedText, Qt::white);

        if(_auto_refresh)
            apply_settings();
    }
    else {
        //min cannot be greater than max
        myPalette.setColor(QPalette::Active, QPalette::Text,            Qt::red);
        myPalette.setColor(QPalette::Active, QPalette::HighlightedText, Qt::red);
        //minSliderValueChanged(  maxSpinBox->value() );
    }

    minSpinBox->setPalette(myPalette);
    maxSpinBox->setPalette(myPalette);

    _applied = false;
}

void Interval_select::maxSpinBoxValueChanged( double value )
{
    QPalette myPalette(minSpinBox->palette());

    if( value > minSpinBox->value() ) {

        bool v = interval_slider->blockSignals(true);
        double position = (interval_slider->maximum() - interval_slider->minimum())
            /             (maxSpinBox->maximum()      - maxSpinBox->minimum());
        position *= value;

        interval_slider->setLowerValue( int(position) );
        interval_slider->blockSignals(v);

        emit maxValueChanged( maxSpinBox->value() );

        myPalette.setColor(QPalette::Active, QPalette::Text,            Qt::black);
        myPalette.setColor(QPalette::Active, QPalette::HighlightedText, Qt::white);

        if(_auto_refresh)
            apply_settings();

    }
    else {
        //max cannot be lower than min
        myPalette.setColor(QPalette::Active, QPalette::Text, Qt::red);
        myPalette.setColor(QPalette::Active, QPalette::HighlightedText, Qt::red);
        //maxSliderValueChanged(  minSpinBox->value() ) ;
    }

    maxSpinBox->setPalette(myPalette);
    minSpinBox->setPalette(myPalette);

    _applied = false;
}

void Interval_select::minSliderValueChanged( int _value )
{
    if(_value < interval_slider->upperValue()){
        bool v = minSpinBox->blockSignals(true);
        minSpinBox->setValue( _value *(_trace->get_max_date() - minSpinBox->minimum())/(interval_slider->maximum() - interval_slider->minimum()));
        minSpinBox->blockSignals(v);
        emit minValueChanged( minSpinBox->value() );
        if(_auto_refresh)apply_settings();
    }
    _applied = false;
}

void Interval_select::maxSliderValueChanged( int _value )
{

    if(_value > interval_slider->lowerValue()){
        bool v = maxSpinBox->blockSignals(true);
        maxSpinBox->setValue( _value * (_trace->get_max_date() - maxSpinBox->minimum())/(interval_slider->maximum() - interval_slider->minimum()));
        maxSpinBox->blockSignals(v);
        emit maxValueChanged( maxSpinBox->value() );
        if(_auto_refresh)apply_settings();
    }
    _applied = false;

    std::cerr << "maxvalue changed" << std::endl;
}


void Interval_select::apply_settings(){
    if (! _applied) {
        if(minSpinBox->value()!=maxSpinBox->value()){
            Element_pos min = minSpinBox->value();
            Element_pos max = maxSpinBox->value();
            Element_pos zoom[2] = { min, max };

            //Info::Render::_x_min_visible = 0.0;
            //Info::Render::_x_max_visible = 0.0;
            _console->get_console()->launch_action(Core:: _STATE_CLEAN_RENDER_AREA);

            //reload data from disk if needed
#if defined(USE_ITC) && defined(BOOST_SERIALIZE)
            Info::Splitter::_x_min=zoom[0];
            Info::Splitter::_x_max=zoom[1];
            _trace->updateTrace(new Interval(zoom[0], zoom[1]));
#endif

            _console->get_console()->draw_trace(_console->get_filename(),Core::_DRAW_OPENGL);

            _console->get_console()->launch_action(Core:: _STATE_ZOOM_IN_AN_INTERVAL, &zoom);
            _console->get_console()->launch_action(Core:: _STATE_RENDER_UPDATE);
        }
    }
    _applied = true;
}


/*
 * Apply changes
 */
void Interval_select::on_apply_button_clicked(){
    apply_settings();
}

/*
 * Forgot about changes
 */
void Interval_select::on_cancel_button_clicked(){
    //reset()
    hide();
}

/*
 * Apply changes and quit
 */
void Interval_select::on_ok_button_clicked(){
    apply_settings();
    hide();
}

/*
 * Restore min/max values
 */
void Interval_select::on_reset_button_clicked() {
    interval_slider->setUpperValue( interval_slider->maximum() );
    interval_slider->setLowerValue( interval_slider->minimum() );
}

/*
 * Change status of auto-refresh
 */
void Interval_select::on_auto_refresh_box_stateChanged(){
    _auto_refresh = auto_refresh_box->isChecked();
}
