////////////////////////////////////////////////////////////////////////////////////////
//
//  Copyright 2026 OVITO GmbH, Germany
//  Copyright 2020 Peter Mahler Larsen
//
//  This file is part of OVITO (Open Visualization Tool).
//
//  OVITO is free software; you can redistribute it and/or modify it either under the
//  terms of the GNU General Public License version 3 as published by the Free Software
//  Foundation (the "GPL") or, at your option, under the terms of the MIT License.
//  If you do not alter this notice, a recipient may use your version of this
//  file under either the GPL or the MIT License.
//
//  You should have received a copy of the GPL along with this program in a
//  file LICENSE.GPL.txt.  You should have received a copy of the MIT License along
//  with this program in a file LICENSE.MIT.txt
//
//  This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
//  either express or implied. See the GPL or the MIT License for the specific language
//  governing rights and limitations.
//
////////////////////////////////////////////////////////////////////////////////////////

#include <ovito/crystalanalysis/CrystalAnalysis.h>
#include <ovito/crystalanalysis/modifier/grains/GrainSegmentationModifier.h>
#include <ovito/gui/desktop/properties/FloatParameterUI.h>
#include <ovito/gui/desktop/properties/IntegerParameterUI.h>
#include <ovito/gui/desktop/properties/BooleanParameterUI.h>
#include <ovito/gui/desktop/properties/IntegerRadioButtonParameterUI.h>
#include <ovito/gui/desktop/properties/ObjectStatusDisplay.h>
#include <ovito/gui/desktop/mainwin/MainWindowUI.h>
#include <ovito/core/dataset/DataSetContainer.h>
#include <ovito/core/dataset/pipeline/ModificationNode.h>
#include "GrainSegmentationModifierEditor.h"

#include <3rdparty/qwt/qwt_plot_zoneitem.h>

namespace Ovito {

IMPLEMENT_CREATABLE_OVITO_CLASS(GrainSegmentationModifierEditor);
SET_OVITO_OBJECT_EDITOR(GrainSegmentationModifier, GrainSegmentationModifierEditor);

/******************************************************************************
* Sets up the UI widgets of the editor.
******************************************************************************/
void GrainSegmentationModifierEditor::createUI(const RolloutInsertionParameters& rolloutParams)
{
    // Create the rollout.
    QWidget* rollout = createRollout(tr("Grain segmentation"), rolloutParams, "manual:particles.modifiers.grain_segmentation");

    QVBoxLayout* layout = new QVBoxLayout(rollout);
    layout->setContentsMargins(4,4,4,4);
    layout->setSpacing(6);

    QGroupBox* paramsBox = new QGroupBox(tr("Parameters"));
    layout->addWidget(paramsBox);
    QGridLayout* sublayout2 = new QGridLayout(paramsBox);
    sublayout2->setContentsMargins(4,4,4,4);
    sublayout2->setSpacing(4);
    sublayout2->setColumnStretch(1, 1);

    IntegerRadioButtonParameterUI* algorithmTypeUI = createParamUI<IntegerRadioButtonParameterUI>(PROPERTY_FIELD(GrainSegmentationModifier::mergeAlgorithm));
    QGridLayout* sublayout3 = new QGridLayout();
    sublayout3->setContentsMargins(0,0,0,0);
    sublayout3->setSpacing(4);
    sublayout2->setColumnStretch(1, 1);
    sublayout3->addWidget(new QLabel(tr("Algorithm:")), 0, 0);
    QRadioButton* automaticModeButton = algorithmTypeUI->addRadioButton(GrainSegmentationModifier::GraphClusteringAutomatic, tr("Graph Clustering (automatic)"));
    sublayout3->addWidget(automaticModeButton, 0, 1);
    sublayout3->addWidget(algorithmTypeUI->addRadioButton(GrainSegmentationModifier::GraphClusteringManual, tr("Graph Clustering (manual)")), 1, 1);
    sublayout3->addWidget(algorithmTypeUI->addRadioButton(GrainSegmentationModifier::MinimumSpanningTree, tr("Minimum Spanning Tree")), 2, 1);
    sublayout2->addLayout(sublayout3, 0, 0, 1, 2);

    FloatParameterUI* mergingThresholdUI = createParamUI<FloatParameterUI>(PROPERTY_FIELD(GrainSegmentationModifier::mergingThreshold));
    sublayout2->addWidget(mergingThresholdUI->label(), 1, 0);
    sublayout2->addLayout(mergingThresholdUI->createFieldLayout(), 1, 1);
    connect(automaticModeButton, &QAbstractButton::toggled, mergingThresholdUI, &ParameterUI::setDisabled);

    IntegerParameterUI* minGrainAtomCountUI = createParamUI<IntegerParameterUI>(PROPERTY_FIELD(GrainSegmentationModifier::minGrainAtomCount));
    sublayout2->addWidget(minGrainAtomCountUI->label(), 2, 0);
    sublayout2->addLayout(minGrainAtomCountUI->createFieldLayout(), 2, 1);

    QGroupBox* optionsBox = new QGroupBox(tr("Options"));
    layout->addWidget(optionsBox);
    sublayout2 = new QGridLayout(optionsBox);
    sublayout2->setContentsMargins(4,4,4,4);
    sublayout2->setSpacing(4);
    sublayout2->setColumnStretch(1, 1);

    // Orphan atom adoption
    BooleanParameterUI* orphanAdoptionUI = createParamUI<BooleanParameterUI>(PROPERTY_FIELD(GrainSegmentationModifier::orphanAdoption));
    sublayout2->addWidget(orphanAdoptionUI->checkBox(), 0, 0, 1, 2);

    // Stacking fault handling
    BooleanParameterUI* handleCoherentInterfacesUI = createParamUI<BooleanParameterUI>(PROPERTY_FIELD(GrainSegmentationModifier::handleCoherentInterfaces));
    sublayout2->addWidget(handleCoherentInterfacesUI->checkBox(), 1, 0, 1, 2);

    // Grain coloring
    BooleanParameterUI* colorParticlesByGrainUI = createParamUI<BooleanParameterUI>(PROPERTY_FIELD(GrainSegmentationModifier::colorParticlesByGrain));
    sublayout2->addWidget(colorParticlesByGrainUI->checkBox(), 2, 0, 1, 2);

    // Output bonds
    BooleanParameterUI* outputBondsUI = createParamUI<BooleanParameterUI>(PROPERTY_FIELD(GrainSegmentationModifier::outputBonds));
    sublayout2->addWidget(outputBondsUI->checkBox(), 3, 0, 1, 2);

    // Status label.
    layout->addWidget(createParamUI<ObjectStatusDisplay>()->statusWidget());

    QPushButton* btn = new QPushButton(tr("Show list of grains"));
    connect(btn, &QPushButton::clicked, this, [this]() {
        if(modificationNode())
            ui().mainWindow()->openDataInspector(modificationNode(), QStringLiteral("grains"), 1); // Note: Mode hint "1" switches to table view.
    });
    layout->addWidget(btn);

    // Create plot widget for merge distances
    _mergePlotWidget = new DataTablePlotWidget();
    _mergePlotWidget->setMinimumHeight(200);
    _mergePlotWidget->setMaximumHeight(200);
    _mergeRangeIndicator = new QwtPlotZoneItem();
    _mergeRangeIndicator->setOrientation(Qt::Vertical);
    _mergeRangeIndicator->setZ(1);
    _mergeRangeIndicator->attach(_mergePlotWidget);
    _mergeRangeIndicator->hide();
    layout->addSpacing(10);
    layout->addWidget(_mergePlotWidget);

    // Create plot widget for log distances
    _logPlotWidget = new DataTablePlotWidget();
    _logPlotWidget->setMinimumHeight(200);
    _logPlotWidget->setMaximumHeight(200);
    _logRangeIndicator = new QwtPlotZoneItem();
    _logRangeIndicator->setOrientation(Qt::Horizontal);
    _logRangeIndicator->setZ(1);
    _logRangeIndicator->attach(_logPlotWidget);
    _logRangeIndicator->hide();
    layout->addSpacing(10);
    layout->addWidget(_logPlotWidget);

    connect(this, &PropertiesEditor::pipelineOutputChanged, this, &GrainSegmentationModifierEditor::plotMerges);
}

/******************************************************************************
* Replots the merge sequence computed by the modifier.
******************************************************************************/
void GrainSegmentationModifierEditor::plotMerges()
{
    handleExceptions([&]() {
        GrainSegmentationModifier* modifier = static_object_cast<GrainSegmentationModifier>(editObject());

        if(modifier && modificationNode()) {
            // Request the modifier's pipeline output.
            const PipelineFlowState& state = getPipelineOutput();

            // Look up the data table in the modifier's pipeline output.
            _mergePlotWidget->setTable(state.getObjectBy<DataTable>(modificationNode(), QStringLiteral("grains-merge")));

            // Indicate the current merge threshold in the plot.
            FloatType mergingThreshold = modifier->mergingThreshold();
            if(modifier->mergeAlgorithm() == GrainSegmentationModifier::GraphClusteringAutomatic) {
                mergingThreshold = state.getAttributeValue(modificationNode(), QStringLiteral("GrainSegmentation.auto_merge_threshold"), mergingThreshold).value<FloatType>();
            }
            _mergeRangeIndicator->setInterval(std::numeric_limits<double>::lowest(), mergingThreshold);
            _mergeRangeIndicator->show();

            // Look up the data table in the modifier's pipeline output.
            _logPlotWidget->setTable(state.getObjectBy<DataTable>(modificationNode(), QStringLiteral("grains-log")));

            // Indicate the current log threshold in the plot.
            _logRangeIndicator->setInterval(0, mergingThreshold);
            _logRangeIndicator->show();
        }
        else {
            _mergePlotWidget->setTable({});
            _mergeRangeIndicator->hide();

            _logPlotWidget->setTable({});
            _logRangeIndicator->hide();
        }
    });
}

}   // End of namespace
