/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2025 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK 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 Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/

#include "ui_PmlExplorerWidget.h"
#include "ui_AddNewPropertyWidget.h"
#include "PMLExplorerWidget.h"
#include "CamiTKPropertyList.h"

//CamiTK includes
#include <PMLComponent.h>
#include <PhysicalModel.h>
#include <MultiComponent.h>
#include <StructuralComponent.h>
#include <Atom.h>
#include <Cell.h>
#include <CellProperties.h>
#include <Log.h>

#include <ObjectController.h>

// Qt includes
#include <QTreeWidgetItem>
#include <QPixmap>
#include <QMap>
#include <QVector3D>
#include <QDialog>

// vtk
#include <vtkPointData.h>

// --------------- constructor -------------------
PMLExplorerWidget::PMLExplorerWidget(PMLComponent* comp) : QWidget() {
    ui = new Ui::ui_PmlExplorerWidget();
    ui->setupUi(this);

    objectController = new camitk::ObjectController(ui->propEditorFrame, camitk::ObjectController::BUTTON);
    ui->propEditorFrame->layout()->addWidget(objectController);

    // connect the buttons
    QObject::connect(ui->applyButton, SIGNAL(clicked()), objectController, SLOT(apply()));
    QObject::connect(ui->applyButton, SIGNAL(clicked()), this, SLOT(propertyChanged()));
    QObject::connect(ui->revertButton, SIGNAL(clicked()), objectController, SLOT(revert()));
    QObject::connect(ui->addButton, SIGNAL(clicked()), this, SLOT(createNewProperty()));
    QObject::connect(ui->showAtomGlyphCB, SIGNAL(toggled(bool)), this, SLOT(showAtomGlyphToggled(bool)));

    physicalModelItem = nullptr;
    atomsItem = nullptr;
    exclusiveComponentItem = nullptr;
    informativeComponentItem = nullptr;
    init(comp);
}

// --------------- Destructor -------------------
PMLExplorerWidget::~PMLExplorerWidget() {
    delete objectController;
    objectController = nullptr;

    for (QObject* o : widgetPropertiesMap) {
        delete o;
    }

    if (ui) {
        delete ui;
    }
}

// --------------- buildPhysicalModelTreeWidgetItem -------------------
void PMLExplorerWidget::buildPhysicalModelTreeWidgetItem() {
    // This can't take time on big PML file
    QApplication::setOverrideCursor(Qt::WaitCursor);

    editedItem = nullptr;

    // create the main PhysicalModel node
    physicalModelItem = new QTreeWidgetItem(ui->treeWidget);

    // get extra text from unknown prop added on the atom
    QString extraProp("");

    for (unsigned int prop_index = 0; prop_index < physicalModel->getProperties()->numberOfFields(); prop_index++) {
        extraProp += "[" + QString(physicalModel->getProperties()->getField(prop_index).c_str()) + " = " + QString(physicalModel->getProperties()->getString(physicalModel->getProperties()->getField(prop_index)).c_str()) + "]";
    }

    QString pmlText = "Physical Model \"" + QString(physicalModel->getName().c_str());
    physicalModelItem->setText(0, pmlText + extraProp);
    physicalModelItem->setIcon(0, pmlComponent->getIcon());

    // Create the Atoms structure Tree wigdet
    atomsItem = buildStructuralComponentTreeWidgetItem(physicalModel->getAtoms(), physicalModelItem);

    // Create the Exclusive components Tree wigdet
    exclusiveComponentItem = buildMultiComponentTreeWidgetItem(physicalModel->getExclusiveComponents(), physicalModelItem);

    // informative components
    if (physicalModel->getNumberOfInformativeComponents() > 0) {
        // Create the Informative components Tree wigdet
        informativeComponentItem = buildMultiComponentTreeWidgetItem(physicalModel->getInformativeComponents(), physicalModelItem);
    }

    QApplication::restoreOverrideCursor();
}


// --------------- buildMultiComponentTreeWidgetItem -------------------
QTreeWidgetItem* PMLExplorerWidget::buildMultiComponentTreeWidgetItem(MultiComponent* inputMC, QTreeWidgetItem* parent) {
    // Add the current item in the QTreeView list
    auto* mcItem = new QTreeWidgetItem(parent);
    widgetComponentMap.insert(mcItem, inputMC);

    mcItem->setText(0, inputMC->getName().c_str());
    mcItem->setIcon(0, QPixmap(":/MultiComponentIcon"));

    // recursively consider sub components
    for (unsigned int i = 0; i < inputMC->getNumberOfSubComponents(); i++) {
        if (MultiComponent* mc = dynamic_cast<MultiComponent*>(inputMC->getSubComponent(i))) {
            buildMultiComponentTreeWidgetItem(mc, mcItem);
        }
        else if (StructuralComponent* sc = dynamic_cast<StructuralComponent*>(inputMC->getSubComponent(i))) {
            buildStructuralComponentTreeWidgetItem(sc, mcItem);
        }
    }

    return mcItem;
}

// --------------- buildStructuralComponentTreeWidgetItem -------------------
QTreeWidgetItem* PMLExplorerWidget::buildStructuralComponentTreeWidgetItem(StructuralComponent* sc, QTreeWidgetItem* parent) {
    // Add the structural item in the QTreeView list
    auto* scItem = new QTreeWidgetItem(parent);
    widgetComponentMap.insert(scItem, sc);

    scItem->setText(0, sc->getName().c_str());
    scItem->setIcon(0, QPixmap(":/StructuralComponentIcon"));

    // add its atoms / cells content
    for (unsigned int i = 0; i < sc->getNumberOfStructures(); i++) {
        if (Atom* a = dynamic_cast<Atom*>(sc->getStructure(i))) {
            buildAtomTreeWidgetItem(a, scItem);
        }
        else if (Cell* c = dynamic_cast<Cell*>(sc->getStructure(i))) {
            buildCellTreeWidgetItem(c, scItem);
        }
    }

    return scItem;
}

// --------------- buildAtomTreeWidgetItem -------------------
QTreeWidgetItem* PMLExplorerWidget::buildAtomTreeWidgetItem(Atom* atom, QTreeWidgetItem* parent) {
    auto* atomItem = new QTreeWidgetItem(parent);
    widgetAtomMap.insert(atomItem, atom);

    QString atomItemText = "Atom #" + QString::number(atom->getIndex());
    atomItem->setText(0, atomItemText);
    atomItem->setIcon(0, QPixmap(":/AtomIcon"));

    return atomItem;
}

// --------------- buildCellTreeWidgetItem -------------------
QTreeWidgetItem* PMLExplorerWidget::buildCellTreeWidgetItem(Cell* cell, QTreeWidgetItem* parent) {
    auto* cellItem = new QTreeWidgetItem(parent);
    widgetComponentMap.insert(cellItem, cell);
    QString cellItemText = "Cell ";

    if (cell->hasIndex) {
        cellItemText += "#" + QString::number(cell->getIndex());
    }

    cellItem->setText(0, cellItemText);
    cellItem->setIcon(0, QPixmap(":/CellIcon"));

    // Add the cell's atoms
    for (unsigned int i = 0; i < cell->getNumberOfStructures(); i++) {
        if (Atom* a = dynamic_cast<Atom*>(cell->getStructure(i))) {
            buildAtomTreeWidgetItem(a, cellItem);
        }
    }

    return cellItem;

}

// --------------- showAtomGlyphToggled -------------------
void PMLExplorerWidget::showAtomGlyphToggled(bool checkBoxState) {
    showAtomGlyph = checkBoxState;
    selectItems();
}

// --------------- selectItems -------------------
void PMLExplorerWidget::selectItems() {
    QApplication::setOverrideCursor(Qt::WaitCursor);

    // First, unselect all previously selected items
    pmlComponent->unselectItems();

    // let's find out the corresponding Atom, SC, MC or Cell selected and ask to select them in the 3D structure
    QList<QTreeWidgetItem*> itemsSelected = ui->treeWidget->selectedItems();

    for (QTreeWidgetItem* item : itemsSelected) {
        if (widgetAtomMap.contains(item)) {
            // Is it an atom ?
            Atom* atomSelected = widgetAtomMap.value(item);
            pmlComponent->selectAtom(atomSelected);
            // create the corresponding QObject
            updateProperty(item);
        }
        else if (widgetComponentMap.contains(item)) {
            Component* comp = widgetComponentMap.value(item);

            if (Cell* cellSelected = dynamic_cast<Cell*>(comp)) {        // Is it a cell ?
                pmlComponent->selectCell(cellSelected, showAtomGlyph);
            }
            else if (MultiComponent* mc = dynamic_cast<MultiComponent*>(comp)) {         // Is it a MC ?
                pmlComponent->selectMC(mc, showAtomGlyph);
            }
            else if (StructuralComponent* sc = dynamic_cast<StructuralComponent*>(comp)) {         // Is it a SC ?
                pmlComponent->selectSC(sc, showAtomGlyph);
            }

            // create the corresponding QObject
            updateProperty(item);
        }

        // in any case (even for item == physicalModelItem), update the property
        updateProperty(item);
    }

    pmlComponent->updateSelection();
    pmlComponent->refreshDisplay();

    QApplication::restoreOverrideCursor();
}

// --------------- updateTarget -------------------
void PMLExplorerWidget::updateTarget(PMLComponent* comp) {
    editedItem = nullptr;
    objectController->setObject(nullptr);
    // Disconnect the selection changed signal, avoiding
    // vicious bugs (Qt slots thread may call signal method while
    // constructing the tree content).
    disconnect(ui->treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(selectItems()));

    for (QObject* o : widgetPropertiesMap) {
        delete o;
    }

    for (QObject* o : atomPropertiesMap) {
        delete o;
    }

    // Clear maps content
    widgetAtomMap.clear();
    widgetComponentMap.clear();
    widgetPropertiesMap.clear();
    atomPropertiesMap.clear();

    // Clear the tree content (remove items and delete them)
    ui->treeWidget->clear();
    physicalModelItem = nullptr;
    atomsItem = nullptr;
    exclusiveComponentItem = nullptr;
    informativeComponentItem = nullptr;

    init(comp);
}

// --------------- init -------------------
void PMLExplorerWidget::init(PMLComponent* comp) {
    // Build back the tree for the new component
    pmlComponent = comp;
    this->physicalModel = comp->getPhysicalModel();

    // build the tree view
    buildPhysicalModelTreeWidgetItem();

    // show/display the first level
    physicalModelItem->setExpanded(true);
    exclusiveComponentItem->setExpanded(true);

    if (exclusiveComponentItem->childCount() > 0) {
        exclusiveComponentItem->child(0)->setExpanded(true);
    }

    if (informativeComponentItem) {
        informativeComponentItem->setExpanded(true);

        if (informativeComponentItem->childCount() > 0) {
            informativeComponentItem->child(0)->setExpanded(true);
        }
    }

    ui->treeWidget->setHeaderLabel(comp->getName());

    // Reconnect back the SLOT
    connect(ui->treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(selectItems()));

    // reset the atom glyph status
    showAtomGlyph = ui->showAtomGlyphCB->isChecked();
}

// --------------- updateProperty -------------------
void PMLExplorerWidget::updateProperty(QTreeWidgetItem* item) {
    // get or create
    QObject* itemProperties = widgetPropertiesMap.value(item);

    // update properties
    if (!itemProperties) {
        if (widgetAtomMap.contains(item)) {
            Atom* a = widgetAtomMap.value(item);

            if (a) {
                // check if this atom already has an list of properties
                itemProperties = atomPropertiesMap.value(a);
                // always recreate the property list as there might be some new data array
                delete itemProperties;
                itemProperties = createAtomPropertyList(a);
                atomPropertiesMap.insert(a, itemProperties);
            }
        }
        else if (widgetComponentMap.contains(item)) {
            Component* comp = widgetComponentMap.value(item);

            // cell properties
            if (Cell* c = dynamic_cast<Cell*>(comp)) {
                itemProperties = createCellPropertyList(c);
            }
            else if (StructuralComponent* sc = dynamic_cast<StructuralComponent*>(comp)) {
                itemProperties = createSCPropertyList(sc);
            }
            else if (MultiComponent* mc = dynamic_cast<MultiComponent*>(comp)) {
                itemProperties = createMCPropertyList(mc);
            }

            // insert the new property list in the map
            widgetPropertiesMap.insert(item, itemProperties);

        }
        else if (item == physicalModelItem) {
            // the component has already set all the dynamic properties
            itemProperties = pmlComponent;
        }
        else {
            editedItem = nullptr;
        }
    }

    // update the ObjectController
    editedItem = item;
    objectController->setObject(itemProperties);

}

// --------------- createNewProperty -------------------
void PMLExplorerWidget::createNewProperty() {
    if (editedItem != nullptr) {
        QDialog* newPropertyDialog = new QDialog(this);
        Ui::ui_AddNewPropertyWidget addNewPropertyUI;
        addNewPropertyUI.setupUi(newPropertyDialog);
        newPropertyDialog->setModal(true);
        QRegularExpression noSpaceStartWithLetter("[a-z][a-z0-9-_]*", QRegularExpression::CaseInsensitiveOption);
        addNewPropertyUI.nameLineEdit->setValidator(new QRegularExpressionValidator(noSpaceStartWithLetter));

        if (newPropertyDialog->exec() == QDialog::Accepted) {
            QString propName = addNewPropertyUI.nameLineEdit->text();
            QString propValue = addNewPropertyUI.valueLineEdit->text();
            camitk::Property* newProp = new camitk::Property(propName, propValue, "", "");

            if (editedItem == physicalModelItem) {
                physicalModel->getProperties()->set(propName.toStdString(), propValue.toStdString());
                pmlComponent->addProperty(newProp);
                // update object controller
                objectController->setObject(nullptr);
                objectController->setObject(pmlComponent);
            }
            else {
                QObject* editedObject = nullptr;

                if (Atom* a = widgetAtomMap.value(editedItem)) {
                    editedObject = atomPropertiesMap.value(a);
                }
                else if (widgetComponentMap.contains(editedItem)) {
                    editedObject = widgetPropertiesMap.value(editedItem);
                }

                // the cast should always be ok
                auto* propList = dynamic_cast<CamiTKPropertyList*>(editedObject);

                if (propList != nullptr) {
                    propList->addProperty(newProp);
                    // update object controller
                    objectController->setObject(nullptr);
                    objectController->setObject(propList);
                }
                else {
                    // to prevent compiler warning about potential memory link (should never come to that)
                    delete newProp;
                }
            }

        }

        delete newPropertyDialog;
    }
}

// --------------- createAtomPropertyList -------------------
CamiTKPropertyList* PMLExplorerWidget::createAtomPropertyList(Atom* a) {
    auto* itemProperties = new CamiTKPropertyList;

    // index
    camitk::Property* indexProp = new camitk::Property("index", QVariant((int) a->getIndex()), "Unique index of the atom (can be different than its order number in the atom structure, atom indexes do not have to be contiguous). <br/><b>WARNING :</b> if physical model not valid if two atoms have the same index, becareful when you modify this value!", "unsigned int");
    itemProperties->addProperty(indexProp);

    // position
    double pos[3];
    a->getPosition(pos);
    itemProperties->addProperty(new camitk::Property("Position", QVector3D(pos[0], pos[1], pos[2]), "Atom properties", "same unit as the PML"));

    // atom name
    QString atomName(a->getName().c_str());

    if (atomName != "") {
        itemProperties->addProperty(new camitk::Property("name", atomName, "(optional) name of this atom", ""));
    }

    // get extra prop
    for (unsigned int propIndex = 0; propIndex < a->getProperties()->numberOfFields(); propIndex++) {
        itemProperties->addProperty(new camitk::Property(a->getProperties()->getField(propIndex).c_str(), QString(a->getProperties()->getString(a->getProperties()->getField(propIndex)).c_str()), "Additional property", ""));
    }

    // add all data array value
    vtkSmartPointer<vtkPointData> allPointData = pmlComponent->getPointSet()->GetPointData();
    std::string activeDataName;

    if (allPointData->GetScalars()) {
        activeDataName = allPointData->GetScalars()->GetName();
    }

    for (int i = 0; i < allPointData->GetNumberOfArrays(); i++) {
        vtkSmartPointer<vtkDataArray> array = allPointData->GetArray(i);
        std::string arrayName = array->GetName();
        std::string dataName = "Monitoring Data";
        bool isActive = (activeDataName.compare(arrayName) == 0);

        // check if this array is the active one
        if (isActive) {
            arrayName += " (*)";
            dataName += " (active/displayed data point)";
        }

        double* val = array->GetTuple(pmlComponent->getPointId(a));
        camitk::Property* dataProp = new camitk::Property(arrayName.c_str(), *val, dataName.c_str(), "");
        dataProp->setGroupName("Monitors");
        itemProperties->addProperty(dataProp);
    }

    return itemProperties;
}

// --------------- createCellPropertyList -------------------
CamiTKPropertyList* PMLExplorerWidget::createCellPropertyList(Cell* c) {
    auto* itemProperties = new CamiTKPropertyList;

    // index
    camitk::Property* indexProp = new camitk::Property("index", QVariant((int) c->getIndex()), "Unique index of the cell (can be totally arbitrary, cell indexes do not have to be contiguous)", "unsigned int");
    indexProp->setReadOnly(true);
    itemProperties->addProperty(indexProp);

    // number of structures
    camitk::Property* nbOfStructureProp = new camitk::Property("Number Of Atoms", QVariant((int) c->getNumberOfStructures()), "Number of atoms composing this cell (linked with the cell type)", "");
    nbOfStructureProp->setReadOnly(true);
    itemProperties->addProperty(nbOfStructureProp);

    camitk::Property* typeProp = new camitk::Property("Type", StructureProperties::toString(c->getProperties()->getType()).c_str(), "Geometric Type", "");
    typeProp->setReadOnly(true);
    itemProperties->addProperty(typeProp);

    // cell name
    QString cellName(((Structure*) c)->getName().c_str());

    if (cellName != "") {
        itemProperties->addProperty(new camitk::Property("name", cellName, "(optional) name of this cell", ""));
    }

    /* TODO manage read-write color, add this property + on update modify the physicalModel data + interactively change the VTK representation
    double *cellColorValues;
    cellColorValues = c->getColor();
    QColor cellColor(cellColorValues[0], cellColorValues[1], cellColorValues[2], cellColorValues[3]);
    camitk::Property *colorProp = new camitk::Property("Color", cellColor, "Color of the cell (not always visible)", "");
    itemProperties->addProperty(colorProp);

    */

    // get extra prop
    for (unsigned int propIndex = 0; propIndex < c->getProperties()->numberOfFields(); propIndex++) {
        itemProperties->addProperty(new camitk::Property(c->getProperties()->getField(propIndex).c_str(), QString(c->getProperties()->getString(c->getProperties()->getField(propIndex)).c_str()), "Additional property", ""));
    }

    return itemProperties;
}

// --------------- createSCPropertyList -------------------
CamiTKPropertyList* PMLExplorerWidget::createSCPropertyList(StructuralComponent* sc) {
    auto* itemProperties = new CamiTKPropertyList;

    // sc name
    QString scName(sc->getName().c_str());

    if (scName != "") {
        itemProperties->addProperty(new camitk::Property("name", scName, "(optional) name of this structural component", ""));
    }

    // composed by
    QString composedByValue;

    switch (sc->composedBy()) {
        case StructuralComponent::ATOMS:
            composedByValue = "Atoms";
            break;

        case StructuralComponent::CELLS:
            composedByValue = "Cells";
            break;

        default:
            composedByValue = "Nothing !";
    }

    camitk::Property* composedByProp = new camitk::Property("Composed By", composedByValue, "Nature of the structures (cells or atoms)", "");
    composedByProp->setReadOnly(true);
    itemProperties->addProperty(composedByProp);

    // number of structures
    camitk::Property* nbOfStructureProp = new camitk::Property("Number Of Structures", QVariant((int) sc->getNumberOfStructures()), "Number of cells or atoms (depending on the nature of the structure) composing this structural component", "");
    nbOfStructureProp->setReadOnly(true);
    itemProperties->addProperty(nbOfStructureProp);

    // RO color
    double* scColorValues;
    scColorValues = sc->getColor();
    QColor cellColor(scColorValues[0], scColorValues[1], scColorValues[2], scColorValues[3]);
    camitk::Property* colorProp = new camitk::Property("Color", cellColor, "Color of the structural component (not always visible)", "");
    colorProp->setReadOnly(true);
    itemProperties->addProperty(colorProp);

    // get extra prop
    for (unsigned int propIndex = 0; propIndex < sc->getProperties()->numberOfFields(); propIndex++) {
        itemProperties->addProperty(new camitk::Property(sc->getProperties()->getField(propIndex).c_str(), QString(sc->getProperties()->getString(sc->getProperties()->getField(propIndex)).c_str()), "Additional property", ""));
    }

    return itemProperties;
}

// --------------- createMCPropertyList -------------------
CamiTKPropertyList* PMLExplorerWidget::createMCPropertyList(MultiComponent* mc) {
    auto* itemProperties = new CamiTKPropertyList;

    // mc name
    QString mcName(mc->getName().c_str());

    if (mcName != "") {
        itemProperties->addProperty(new camitk::Property("name", mcName, "(optional) name of this multi-component", ""));
    }

    // number of subcomponents
    camitk::Property* nbOfComponentProp = new camitk::Property("Number Of Subcomponents", QVariant((int) mc->getNumberOfSubComponents()), "Number of subcomponents composing this multi-component", "");
    nbOfComponentProp->setReadOnly(true);
    itemProperties->addProperty(nbOfComponentProp);

    // get extra prop
    for (unsigned int propIndex = 0; propIndex < mc->getProperties()->numberOfFields(); propIndex++) {
        itemProperties->addProperty(new camitk::Property(mc->getProperties()->getField(propIndex).c_str(), QString(mc->getProperties()->getString(mc->getProperties()->getField(propIndex)).c_str()), "Additional property", ""));
    }

    return itemProperties;
}


// --------------- propertyChanged -------------------
void PMLExplorerWidget::propertyChanged() {

    if (editedItem == physicalModelItem) {
        // in the case of the physicalModelItem, nothing to do, this will be taken into account by the property explorer of the component, just refres
        pmlComponent->refresh();
    }
    else {

        if (Atom* a = widgetAtomMap.value(editedItem)) {
            QObject* editedObject = atomPropertiesMap.value(a);
            auto* propList = dynamic_cast<CamiTKPropertyList*>(editedObject);

            QVector3D pos = propList->property("Position").value<QVector3D>();
            a->setPosition(pos.x(), pos.y(), pos.z());

            QString name = propList->property("name").value<QString>();

            if (!name.isEmpty()) {
                a->setName(name.toStdString());
            }

            int index = propList->property("index").value<int>();
            a->setIndex(index);

            // all other properties
            for (unsigned int i = 0; i < propList->getNumberOfProperties(); i++) {
                QString propName = propList->getProperty(i)->getName();

                if (propName != "index" &&  propName != "Position" && propName != "name" && !propList->getProperty(i)->getReadOnly()) {
                    // any other property
                    QString value = propList->property(propName.toStdString().c_str()).value<QString>();
                    a->getProperties()->set(propName.toStdString(), value.toStdString());
                }
            }

            // just in case the position was changed, refresh the whole scene
            pmlComponent->refreshDisplay();

        }
        else if (widgetComponentMap.contains(editedItem)) {
            QObject* editedObject = widgetPropertiesMap.value(editedItem);
            auto* propList = dynamic_cast<CamiTKPropertyList*>(editedObject);

            Component* comp = widgetComponentMap.value(editedItem);
            QString name = propList->property("name").value<QString>();

            Cell* c = dynamic_cast<Cell*>(comp);

            if (c != nullptr) {
                ((Structure*) c)->setName(name.toStdString());

                // all other properties
                for (unsigned int i = 0; i < propList->getNumberOfProperties(); i++) {
                    QString propName = propList->getProperty(i)->getName();

                    if (propName != "name" && !propList->getProperty(i)->getReadOnly()) {
                        // any other property
                        QString value = propList->property(propName.toStdString().c_str()).value<QString>();
                        c->getProperties()->set(propName.toStdString(), value.toStdString());
                    }
                }
            }
            else {
                if (comp != nullptr) {
                    comp->setName(name.toStdString());

                    // all other properties
                    for (unsigned int i = 0; i < propList->getNumberOfProperties(); i++) {
                        QString propName = propList->getProperty(i)->getName();

                        if (propName != "name" && !propList->getProperty(i)->getReadOnly()) {
                            // any other property
                            QString value = propList->property(propName.toStdString().c_str()).value<QString>();
                            comp->getProperties()->set(propName.toStdString(), value.toStdString());
                        }
                    }
                }
            }
        }
    }

    pmlComponent->setModified();
}









