#include "Controls.h"
#include "ui_Controls.h"
#include "ExaminerViewer.h"
#include "RTMSimulator.h"
#include "SimNode.h"

#include <QValidator>
#include <QIntValidator>
#include <QDoubleValidator>

Controls::Controls(RTMSimulator* pSimulator) :
    QDockWidget(pSimulator),
    m_pSimulator(pSimulator),
    ui(new Ui::Controls) {
    ui->setupUi(this);
    QDoubleValidator* double_validator = new QDoubleValidator(this);
    m_pMinDistanceValidator = new QDoubleValidator(0.0,1.0,10,this);
    ui->lineEditMinDistance->setValidator(m_pMinDistanceValidator);
    ui->lineEditQ->setValidator(double_validator);
    ui->lineEditR->setValidator(double_validator);
    ui->lineEditS->setValidator(double_validator);
    ui->lineEditSeconds->setValidator(double_validator);
    ui->lineEditSeconds->setValidator(double_validator);
    ui->lineEditViscosity->setValidator(double_validator);
    ui->lineEditCycles->setValidator(new QIntValidator (0,100000,this));
    ui->lineEditRelevantNeighbors->setValidator(new QIntValidator(0,100,this));
    ui->lineEditVacuumPressure->setValidator(new QDoubleValidator(0,1,10,this));

    connect(m_pSimulator,SIGNAL(startButtonEnabled(bool)),    ui->buttonStart,   SLOT (setEnabled(bool)));
    connect(m_pSimulator,SIGNAL(stopButtonEnabled(bool)),     ui->buttonStop,    SLOT (setEnabled(bool)));
    connect(m_pSimulator,SIGNAL(pauseButtonEnabled(bool)),    ui->buttonPause,   SLOT (setEnabled(bool)));
    connect(m_pSimulator,SIGNAL(stepButtonEnabled(bool)),     ui->buttonStep,    SLOT (setEnabled(bool)));

    connect(ui->buttonInformationHelp,SIGNAL(clicked()),m_pSimulator,SLOT(showInfoHelp()));

    init();
}

Controls::~Controls() {
    delete ui;
}

void Controls::init() {
    setQRS(1,1,2);
    setViscosity(0.1);
    setVacuumPressure(10000);
    setPictureTimeInterval(1);
    setCycleNumber(1);
    setRelevantNeighbors(8);
}

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

void Controls::showAdvancedTab(bool show)
{
    if(show)
        ui->tabWidget->insertTab(2,ui->TabAdvanced,"Advanced");
    else
        ui->tabWidget->removeTab(2);

}

/**
 * This slot should be called whenever the sweep button is clicked.
 * Starts the sweep process of the RTMSimulator.
 */
void Controls::on_buttonSweep_clicked() {
    m_pSimulator->sweep(ui->lineEditMinDistance->text().toDouble());
}

/**
 * This slot should be called whenever the 'sweep once' button is clicked.
 * Starts the a single sweep cycle of the RTMSimulator.
 */
void Controls::on_buttonSweepOnce_clicked() {
    m_pSimulator->sweep(ui->lineEditMinDistance->text().toDouble(),false);
}

/**
 * This slot should be called whenever the 'delete swept nodes' button is clicked.
 * Deletes all the previously swept nodes from the scene graph.
 */
void Controls::on_buttonDeleteSwept_clicked() {
    m_pSimulator->deleteSweptNodes();
}

void Controls::on_checkBoxShowSweptNodes_stateChanged(int state) {
    m_pSimulator->showSweptNodes(state);
}

void Controls::on_checkBoxMarkNeighbors_stateChanged(int state) {
    m_pSimulator->showNeighbors(state);
}

void Controls::on_checkBoxMarkDirectNeighbors_stateChanged(int state) {
    m_pSimulator->showDirectNeighbors(state);
}

void Controls::on_checkBoxPermeabilityGradient_stateChanged(int state) {
    m_pSimulator->showPermeabilityGradient(state);
}

/**
 * This slot is just used to refresh the information inside the PreprocessingTab widget. \n
 * It is doing it's job just by simply calling the selectionChanged slot.
 */
void Controls::on_buttonRefresh_clicked() {
    selectionChanged();
}

void Controls::on_buttonSaveCellSettings_clicked() {
    saveNodeSettings();
}

void Controls::on_buttonStart_clicked() {
    emit start();
}

void Controls::on_buttonPause_clicked() {
    emit pause();
}

void Controls::on_buttonStop_clicked() {
    emit stop();
}

void Controls::on_buttonStep_clicked() {
    int steps = ui->lineEditCycles->text().toInt();
    emit step ( steps );
}

/**
 * This will emit the takePictures() SIGNAL with the boolean state as an argument.
 * Additionally it executes the updatePictureTimeInterval function.
 */
void Controls::on_checkBoxTakePictures_stateChanged(int state) {
    emit(takePictures(state == Qt::Checked));
    emit(updatePictureTimeInterval(ui->lineEditSeconds->text().toDouble()));
}

void Controls::on_pushButtonCheckUnlinkedNodes_clicked() {
    emit(startLonesomeNodesTest());
}

/**
 * Should be called when the value in lineEditViscosity has been changed.
 * Emits updateViscosity signal to notify listeners about new value.
 */
void Controls::on_lineEditViscosity_editingFinished() {
    emit (updateViscosity(ui->lineEditViscosity->text().toDouble()));
}

/**
 * This will emit the updatePictureTimeInterval() SIGNAL with the time delta in seconds as an argument.
 */
void Controls::on_lineEditSeconds_editingFinished() {
    emit( updatePictureTimeInterval(ui->lineEditSeconds->text().toDouble()) );
}

/**
 * Should be called when the value in lineEditQ has been changed.
 * Emits updateQ signal to notify listeners about new value.
 */
void Controls::on_lineEditQ_editingFinished() {
    emit (updateQ(ui->lineEditQ->text().toDouble()));
}

/**
 * Should be called when the value in lineEditR has been changed.
 * Emits updateR signal to notify listeners about new value.
 */
void Controls::on_lineEditR_editingFinished() {
    emit (updateR(ui->lineEditR->text().toDouble()));
}

/**
 * Should be called when the value in lineEditS has been changed.
 * Emits updateS signal to notify listeners about new value.
 */
void Controls::on_lineEditS_editingFinished() {
    emit (updateS(ui->lineEditS->text().toDouble()));
}

//------------------------------//------------------------------//------------------------------//------------------------------
//------------------------------//------------------------------//------------------------------//------------------------------
/**
 * This slot will be called, whenever the selection in the according scene graph is changed.\n
 * The information inside the PreprocessingTab widget will be updated according to the information of the selected Nodes.
 */
void Controls::selectionChanged() {
    std::vector<SimNode*> selectedNodes = ExaminerViewer::getSelection();
    if(!selectedNodes.empty())
    {
        SimNode* firstNode = selectedNodes.front();
        updateNumberOfSelectedNodes(selectedNodes.size());
        updateCurrentPressure(firstNode->getPressure());
        updatePivotNodeId(firstNode->id());
        if(selectedNodes.size() == 2)
        {
            updateCellDistance(SimNode::getDistance(selectedNodes.front(),selectedNodes.back()));
        }
        updateCellXYZ(firstNode->getX(),firstNode->getY(),firstNode->getZ());

        Qt::CheckState state = Qt::Unchecked;
        for(std::vector<SimNode*>::const_iterator it = selectedNodes.begin(); it != selectedNodes.end(); it++)
        {
            if((*it)->isInjectionPoint())
            {
                if(selectedNodes.size() == 1)
                    state = Qt::Checked;
                else
                    state = Qt::PartiallyChecked;
                break;
            }
        }

        updateInjectionDiameter(firstNode->getInjectionDiameter());
        updateInjectionPointCheckBox(state);
        updateInjectionPressure(firstNode->getInjectionPressure());

        updateCavityHeight(firstNode->getCavityHeight());
        updatePermeability(firstNode->getPermeability());
        updatePorosity(firstNode->getPorosity());
    }
    else
    {
        updateNumberOfSelectedNodes(0);
        updateCurrentPressure(0);
        updatePivotNodeId(0);
    }
}

/**
 * Updates the injection diameter value displyed in the according line edit control
 * @param injectionDiameter New value to be displyed
 */
void Controls::updateInjectionDiameter(double injectionDiameter) {
    ui->lineEditInjectionDiameter->setText(QString().setNum(injectionDiameter));
}

/**
 * Updates the state of the m_pInjectionPointCheckBox according to the provided state.
 * @param state New state for the check box
 */
void Controls::updateInjectionPointCheckBox(Qt::CheckState state) {
    ui->checkBoxInjectionPoint->setTristate(false); //we always need to reset this, because it will become a tristate again, if there is an injection node beneath the selection
    ui->checkBoxInjectionPoint->setCheckState(state);
}

/**
 * Updates the injection pressure value displyed in the according line edit control
 * @param injectionPressure New value to be displyed
 */
void Controls::updateInjectionPressure(int injectionPressure) {
    ui->lineEditInjectionPressure->setText(QString().setNum(injectionPressure));
}

/**
 * Updates the cavity height value displyed in the according line edit control
 * @param cavityHeight New value to be displyed
 */
void Controls::updateCavityHeight(double cavityHeight) {
    ui->lineEditCavityHeight->setText(QString().setNum(cavityHeight));
}

/**
 * Updates the permeability value displyed in the according line edit control
 * @param permeability New value to be displyed
 */
void Controls::updatePermeability(double permeability) {
    ui->lineEditPermeability->setText(QString().setNum(permeability));
}

/**
 * Updates the porosity value displyed in the according line edit control
 * @param porosity New value to be displyed
 */
void Controls::updatePorosity(double porosity) {
    ui->lineEditPorosity->setText(QString().setNum(porosity));
}

/**
 * This slot is called, whenever we want to save the new settings defined in the PreprocessingTab to all the selected Nodes.
 */
void Controls::saveNodeSettings() {
    m_pSimulator->invalidatePermeabilityGlobals();

    bool fIsInjectionPoint = FALSE;

    if(ui->checkBoxInjectionPoint->checkState() == Qt::Checked)
        fIsInjectionPoint = TRUE;

    std::vector<SimNode*> selectedNodes = ExaminerViewer::getSelection();
    if(!selectedNodes.empty())
    {
        double injectionPressure    = ui->lineEditInjectionPressure->text().toDouble();
        double injectionDiameter    = ui->lineEditInjectionDiameter->text().toDouble();
        double permeability         = ui->lineEditPermeability->text().toDouble();
        double cavityHeight         = ui->lineEditCavityHeight->text().toDouble();
        double porosity             = ui->lineEditPorosity->text().toDouble();
        double medianl              = m_pSimulator->medianCellDistance();

        for(std::vector<SimNode*>::iterator it = selectedNodes.begin(); it != selectedNodes.end(); it++)
        {
            if(ui->checkBoxInjectionPoint->checkState() != Qt::PartiallyChecked)
            {
                (*it)->updateNodeSettings(fIsInjectionPoint, injectionPressure, injectionDiameter, permeability, cavityHeight, porosity, medianl);
            }
            else
            {
                (*it)->updateNodeSettings(injectionPressure, injectionDiameter, permeability, cavityHeight, porosity, medianl);
            }
        }
    }
}

/**
 * Updates the state of the m_pCheckBoxShowSweptNodes according to the provided state.
 * @param state New state for the control
 */
void Controls::setShowSweptNodes(Qt::CheckState state) {
    ui->checkBoxShowSweptNodes->setCheckState(state);
}

/**
 * Updates the state of the m_pCheckBoxShowNeighbors according to the provided state.
 * @param state New state for the control
 */
void Controls::setShowNeighbors(Qt::CheckState state) {
    ui->checkBoxMarkNeighbors->setCheckState(state);
}

/**
 * Updates the state of the m_pCheckBoxShowDirectNeighbors according to the provided state.
 * @param state New state for the control
 */
void Controls::setShowDirectNeighbors(Qt::CheckState state) {
    ui->checkBoxMarkDirectNeighbors->setCheckState(state);
}

/**
 * Updates the state of the m_pCheckBoxShowPermeabilityGradient according to the provided state.
 * @param state New state for the control
 */
void Controls::setShowPermeabilityGradient(Qt::CheckState state) {
    ui->checkBoxPermeabilityGradient->setCheckState(state);
}

/**
 * @returns Vacuum pressure defined in m_pLineEditVacuumPressure.
 */
double Controls::getVacuumPressure() {
    return ui->lineEditVacuumPressure->text().toDouble();
}

double Controls::getQ() { return ui->lineEditQ->text().toDouble(); }
double Controls::getR() { return ui->lineEditR->text().toDouble(); }
double Controls::getS() { return ui->lineEditS->text().toDouble(); }
double Controls::getViscosity() { return ui->lineEditViscosity->text().toDouble(); }

/**
 * This slot is used to display the cycle number defined by argument cycleNr in the m_pLineEditCurrentCycle field.
 * @param cycleNr The current cycle number to display in m_pLineEditCurrentCycle
 */
void Controls::displayCycleNr ( int cycleNr ) {
    ui->lineEditCurrentCycle->setText ( QString().setNum ( cycleNr ) );
}

/**
 * This slot is used to display the elapsed simulation time defined by the argument time in the m_pLineEditCurrentTime field.
 * @param time Elapsed time to display.
 */
void Controls::displayTime ( double time ) {
    ui->lineEditCurrentTime->setText ( QString().setNum ( time ) );
}

/**
 * Updates m_pCheckBoxTakePictures with the provided value.
 * @param takePictures If set to true, m_pCheckBoxTakePictures will be checked.
 */
void Controls::setTakePictures(bool takePictures) {

    ui->checkBoxTakePictures->setChecked(takePictures);
}

/**
 * This is just a function to update the fields that display the q,r,s values all at once.\n
 * See also setQ(), setR(), setS()
 * @param q Current q value
 * @param r Current r value
 * @param s Current s value
 */
void Controls::setQRS(double q, double r, double s) {
    setQ(q);
    setR(r);
    setS(s);
}

void Controls::setViscosity(double viscosity) {
    ui->lineEditViscosity->setText(QString().setNum(viscosity));
}

/**
 * This function is used to display the Plot object wide valid constant quantifier q inside the m_pLineEditQ field of the SimulationTab.
 * @param q Current q value
 */
void Controls::setQ(double q) { ui->lineEditQ->setText(QString().setNum(q)); }
/**
 * This function is used to display the Plot object wide valid constant quantifier r inside the m_pLineEditR field of the SimulationTab.
 * @param r Current r value
 */
void Controls::setR(double r) { ui->lineEditR->setText(QString().setNum(r)); }
/**
 * This function is used to display the Plot object wide valid constant quantifier s inside the m_pLineEditS field of the SimulationTab.
 * @param s Current s value
 */
void Controls::setS(double s) { ui->lineEditS->setText(QString().setNum(s)); }
/**
 * This function is used to display the Plot object wide valid constant vacuum pressure inside the m_pLineEditVacuumPressure field of the SimulationTab.
 * @param pressure Current vacuum pressure.
 */
void Controls::setVacuumPressure(double pressure) { ui->lineEditVacuumPressure->setText(QString().setNum(pressure)); }

int Controls::getNumberOfCycles() { return ui->lineEditCycles->text().toInt(); }

void Controls::setCycleNumber(int nCycle) { ui->lineEditCurrentCycle->setText(QString().setNum(nCycle)); }

void Controls::setPictureTimeInterval(double pictureTimeInterval) { ui->lineEditSeconds->setText(QString().setNum(pictureTimeInterval)); }

double Controls::getPictureTimeInterval() { return ui->lineEditSeconds->text().toDouble(); }

bool Controls::isSetTakePictures() { return ui->checkBoxTakePictures->isChecked(); }

int Controls::numRelevantNeighbors() { return ui->lineEditRelevantNeighbors->text().toInt(); }

/**
 * This function is used to display the number of relevant neighbors for the current calculation inside the m_pLineEditNeighbors field.
 * @param n Amount of relevant neighbors for the current calculation.
 */
void Controls::setRelevantNeighbors(int n) { ui->lineEditRelevantNeighbors->setText(QString().setNum(n)); }

/**
 * Updates the displayed number of total cells.
 * @param totalCells New value to be displayed in lineEditTotalCells
 */
void Controls::updateTotalCells(int totalCells)
{
    m_totalCells = totalCells;
    ui->lineEditTotalCells->setText(QString().setNum(totalCells));
}

/**
 * Calculates and updates the displayed percentage of full nodes.
 * @param numFullCells New total number of full cells.
 */
void Controls::updateCellsFull(int numFullCells)
{
    m_fullCells = numFullCells;
    double percentage = 0;
    if(m_totalCells > 0)
        percentage = 100.0f/(m_totalCells-m_injectionCells)*numFullCells;
    QString text_percentage;
    text_percentage.setNum(percentage,'f',1);
    text_percentage.append('%');
    ui->lineEditFilledVolumeFraction->setText(text_percentage);
}

/**
 * Updates the displayed number of injection nodes.
 * @param numInjectionNodes New total number of injection nodes defined
 */
void Controls::updateNumberOfInjectionCells(int numInjectionNodes)
{
    m_injectionCells = numInjectionNodes;
    ui->lineEditInjectionCells->setText(QString().setNum(numInjectionNodes));
}

/**
 * Updates the displayed pivot node id, which is the ID of the first node in a selection.
 * @param pivotNodeId New ID to be displayed
 */
void Controls::updatePivotNodeId(int pivotNodeId)
{
    ui->lineEditPivotNodeID->setText(QString().setNum(pivotNodeId));
}

/**
 * This slot is called to display the median cell distance of the plot inside the m_pMedianCellDistanceLineEdit field.
 * @param medianCellDistance The new median cell distance
 */
void Controls::updateMedianCellDistance(double medianCellDistance)
{
    ui->lineEditMedianDistance->setText(QString().setNum(medianCellDistance));
}

/**
 * Updates the displayed value for the current pressure of a selected node.
 */
void Controls::updateCurrentPressure(int currentPressure)
{
    ui->lineEditCurrentPressure->setText(QString().setNum(currentPressure));
}

/**
 * Updates the displayed value in m_pNumberOfSelectedNodesLineEdit.
 * @param numberOfSelectedNodes New total number of selected nodes
 */
void Controls::updateNumberOfSelectedNodes(int numberOfSelectedNodes)
{
    ui->lineEditSelectedCells->setText(QString().setNum(numberOfSelectedNodes));
}

/**
 * Updates the displayed distance between two selected nodes.
 * @param cell2cellDistance New value to be displayed for the distance between two selected nodes.
 */
void Controls::updateCellDistance(double cell2cellDistance)
{
    ui->lineEditCell2CellDistance->setText(QString().setNum(cell2cellDistance));
}

/**
 * Updates the displayed coordinates for the currently selected cell.
 * @param x X coordinate to be displayed
 * @param y Y coordinate to be displayed
 * @param z Z coordinate to be displayed
 */
void Controls::updateCellXYZ(double x, double y, double z)
{
    ui->lineEditX->setText(QString().setNum(x));
    ui->lineEditY->setText(QString().setNum(y));
    ui->lineEditZ->setText(QString().setNum(z));
}


