////////////////////////////////////////////////////////////////////////////////////////
//
//  Copyright 2026 OVITO GmbH, Germany
//
//  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/particles/gui/ParticlesGui.h>
#include <ovito/particles/modifier/analysis/StructureIdentificationModifier.h>
#include <ovito/particles/gui/util/ParticleSettingsPage.h>
#include <ovito/stdobj/table/DataTable.h>
#include <ovito/gui/desktop/properties/PropertiesEditor.h>
#include <ovito/core/dataset/pipeline/ModificationNode.h>
#include "StructureListParameterUI.h"

namespace Ovito {

IMPLEMENT_ABSTRACT_OVITO_CLASS(StructureListParameterUI);

/******************************************************************************
* Constructor.
******************************************************************************/
void StructureListParameterUI::initializeObject(PropertiesEditor* parentEditor, bool showCheckBoxes)
{
    RefTargetListParameterUI::initializeObject(parentEditor, PROPERTY_FIELD(StructureIdentificationModifier::structureTypes));

     _showCheckBoxes = showCheckBoxes;

#ifdef Q_OS_MACOS
    int tableWidgetHeight = 240;
#else
    int tableWidgetHeight = 300;
#endif
    QTableView* table = tableWidget(tableWidgetHeight);

    connect(table, &QTableWidget::doubleClicked, this, &StructureListParameterUI::onDoubleClickStructureType);
    connect(parentEditor, &PropertiesEditor::contentsReplaced, table, &QTableView::resizeRowsToContents);
    table->setAutoScroll(false);

    // Update the structure count and fraction columns of the data model whenever the structure identification
    // modifier has generated new results.
    connect(parentEditor, &PropertiesEditor::pipelineOutputChanged, this, [this]() {
        updateStructureCounts();
        _model->updateColumns(2, 3);
    });
}

/******************************************************************************
* This method is called when a new editable object has been activated.
******************************************************************************/
void StructureListParameterUI::resetUI()
{
    // Refresh our internal structure count list.
    updateStructureCounts();

    // Call base implementation.
    RefTargetListParameterUI::resetUI();

    // Clear initial selection by default.
    tableWidget()->selectionModel()->clear();
}

/******************************************************************************
* Creates a standard QLabel displaying usage instructions for the structure list.
******************************************************************************/
QLabel* StructureListParameterUI::createNotesLabel()
{
    QLabel* label = new QLabel(tr("<p style=\"font-size: small;\">Double-click to change a color or name. Defaults can be set in the <a href=\"settings\">application settings</a>.</p>"));
    label->setWordWrap(true);
    label->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
    connect(label, &QLabel::linkActivated, this, [this]() {
        ApplicationSettingsDialog dlg(ui(), &ParticleSettingsPage::OOClass());
        dlg.exec();
    });
    return label;
}

/******************************************************************************
* Obtains the current structure counts from the pipeline.
******************************************************************************/
void StructureListParameterUI::updateStructureCounts()
{
    if(ModificationNode* modNode = editor()->modificationNode()) {

        // Get the current data pipeline output generated by the modifier.
        const PipelineFlowState& state = editor()->getPipelineOutput();

        // Access the data table in the pipeline state containing the structure counts.
        if(const DataTable* table = state.getObjectBy<DataTable>(modNode, QStringLiteral("structures"))) {
            _structureCounts = table->y();
            if(_structureCounts && _structureCounts->dataType() == Property::Int64)
                return;
        }
    }
    _structureCounts.reset();
}

/******************************************************************************
* Returns a data item from the list data model.
******************************************************************************/
QVariant StructureListParameterUI::getItemData(RefTarget* target, const QModelIndex& index, int role)
{
    ElementType* stype = dynamic_object_cast<ElementType>(target);
    StructureIdentificationModifier* modifier = dynamic_object_cast<StructureIdentificationModifier>(editObject());

    if(stype && modifier) {
        if(role == Qt::DisplayRole) {
            if(index.column() == 1) {
                return stype->nameOrNumericId();
            }
            else if(index.column() == 2) {
                if(_structureCounts && stype->numericId() >= 0 && stype->numericId() < _structureCounts->size())
                    return static_cast<qlonglong>(BufferReadAccess<int64_t>(_structureCounts)[stype->numericId()]);
            }
            else if(index.column() == 3) {
                if(_structureCounts && stype->numericId() >= 0 && stype->numericId() < _structureCounts->size()) {
                    size_t totalCount = 0;
                    BufferReadAccess<int64_t> structureArray(_structureCounts);
                    for(auto c : structureArray)
                        totalCount += c;
                    return QString("%1%").arg((double)structureArray[stype->numericId()] * 100.0 / std::max((size_t)1, totalCount), 0, 'f', 1);
                }
            }
            else if(index.column() == 4) {
                return stype->numericId();
            }
        }
        else if(role == Qt::DecorationRole) {
            if(index.column() == 0)
                return (QColor)stype->color();
        }
        else if(role == Qt::CheckStateRole && _showCheckBoxes) {
            if(index.column() == 0)
                return stype->enabled() ? Qt::Checked : Qt::Unchecked;
        }
        else if(role == Qt::EditRole) {
            if(index.column() == 1) {
                return stype->name();
            }
        }
    }
    return {};
}

/******************************************************************************
* Returns the model/view item flags for the given entry.
******************************************************************************/
Qt::ItemFlags StructureListParameterUI::getItemFlags(RefTarget* target, const QModelIndex& index)
{
    if(index.column() == 0 && _showCheckBoxes)
        return RefTargetListParameterUI::getItemFlags(target, index) | Qt::ItemIsUserCheckable;
    else if(index.column() == 1)
        return RefTargetListParameterUI::getItemFlags(target, index) | Qt::ItemIsEditable;
    else
        return RefTargetListParameterUI::getItemFlags(target, index);
}

/******************************************************************************
* Sets the role data for the item at index to value.
******************************************************************************/
bool StructureListParameterUI::setItemData(RefTarget* target, const QModelIndex& index, const QVariant& value, int role)
{
    if(index.column() == 0 && role == Qt::CheckStateRole) {
        if(ElementType* stype = static_object_cast<ElementType>(objectAtIndex(index.row()))) {
            bool enabled = (value.toInt() == Qt::Checked);
            performTransaction(tr("Enable/disable structure type"), [&]() {
                stype->setEnabled(enabled);
            });
            return true;
        }
    }
    else if(index.column() == 1 && role == Qt::EditRole) {
        if(ElementType* stype = static_object_cast<ElementType>(objectAtIndex(index.row()))) {
            performTransaction(tr("Rename structure type"), [&]() {
                stype->setName(value.toString().trimmed());
            });
            return true;
        }
    }

    return RefTargetListParameterUI::setItemData(target, index, value, role);
}

/******************************************************************************
* Is called when the user has double-clicked on one of the structure
* types in the list widget.
******************************************************************************/
void StructureListParameterUI::onDoubleClickStructureType(const QModelIndex& index)
{
    // User must click on first table column, which contains the type colors.
    if(index.column() != 0) return;

    // Let the user select a color for the structure type.
    ElementType* stype = static_object_cast<ElementType>(selectedObject());
    if(!stype) return;

    QColor oldColor = (QColor)stype->color();
    QColorDialog dlg(oldColor, editor()->container());
    if(dlg.exec() != QDialog::Accepted) return;
    QColor newColor = dlg.selectedColor();
    if(newColor == oldColor) return;

    performTransaction(tr("Change structure type color"), [&]() {
        stype->setColor(Color(newColor));
    });
}

}   // End of namespace
