//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Models/ParameterTreeUtils.cpp
//! @brief     Implements ParameterTreeUtils namespace
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#include "GUI/Models/ParameterTreeUtils.h"
#include "GUI/Models/Error.h"
#include "GUI/Models/FitParameterHelper.h"
#include "GUI/Models/GroupItem.h"
#include "GUI/Models/InstrumentItems.h"
#include "GUI/Models/JobItem.h"
#include "GUI/Models/MaterialItemContainer.h"
#include "GUI/Models/ModelPath.h"
#include "GUI/Models/MultiLayerItem.h"
#include "GUI/Models/ParameterTreeItems.h"
#include "GUI/Models/SampleModel.h"
#include <QStack>

namespace {

void handleItem(SessionItem* tree, const SessionItem* source)
{
    if (tree->hasModelType<ParameterLabelItem>()) {
        tree->setDisplayName(source->itemName());
    }

    else if (tree->hasModelType<ParameterItem>()) {
        tree->setDisplayName(source->itemName());

        double sourceValue = source->value().toDouble();
        tree->setValue(QVariant(sourceValue));
        QString path = GUI::Model::Path::getPathFromIndex(source->index());
        int firstSlash = path.indexOf('/');
        path = path.mid(firstSlash + 1);
        tree->setItemValue(ParameterItem::P_LINK, path);
        tree->setItemValue(ParameterItem::P_BACKUP, sourceValue);
        return;
    }

    else
        return;

    for (SessionItem* child : source->children()) {
        if (child->isVisible() && child->isEnabled()) {
            if (child->modelType() == "Property") {
                if (child->value().type() == QVariant::Double) {
                    auto branch = tree->model()->insertItem<ParameterItem>(tree);
                    handleItem(branch, child);
                }

            } else if (child->modelType() == "GroupProperty") {
                SessionItem* currentItem = dynamic_cast<GroupItem*>(child)->currentItem();
                if (currentItem && currentItem->numberOfChildren() > 0) {
                    auto branch = tree->model()->insertItem<ParameterLabelItem>(tree);
                    handleItem(branch, currentItem);
                }
            } else {
                auto branch = tree->model()->insertItem<ParameterLabelItem>(tree);
                handleItem(branch, child);
            }
        }
    }
}

//! Populates ParameterContainer with ParameterItem's corresponding to all properties found
//! in a source item.

void populateParameterContainer(SessionItem* container, const SessionItem* source)
{
    if (!container->hasModelType<ParameterContainerItem>())
        throw Error("GUI::Model::ParameterTreeUtils::populateParameterContainer() -> Error. "
                    "Not a ParameterContainerType.");

    auto sourceLabel = container->model()->insertItem<ParameterLabelItem>(container);
    handleItem(sourceLabel, source);
}

} // namespace

void GUI::Model::ParameterTreeUtils::createParameterTree(JobItem* jobItem)
{
    auto container = jobItem->createParameterContainerItem();

    populateParameterContainer(container, jobItem->materialContainerItem());

    populateParameterContainer(container, jobItem->sampleItem());

    populateParameterContainer(container, jobItem->instrumentItem());
}