//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/Model/Tune/FitParameterItem.h
//! @brief     Defines classes FitTypeItem FitDoubleItem FitEditableDoubleItem FitParameterItem.
//!
//! @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)
//
//  ************************************************************************************************

#ifndef BORNAGAIN_GUI_MODEL_TUNE_FITPARAMETERITEM_H
#define BORNAGAIN_GUI_MODEL_TUNE_FITPARAMETERITEM_H

#include "Base/Type/OwningVector.h"
#include "Fit/Param/AttLimits.h"
#include "GUI/Model/Descriptor/ComboProperty.h"

class FitParameterLinkItem;

//! Holds ComboProperty values, accessible from FitparameterModel.
class FitTypeItem : public QObject {
    Q_OBJECT
public:
    explicit FitTypeItem(ComboProperty type, QObject* parent = nullptr);

    const ComboProperty& type() const { return m_type; }
    void setType(const ComboProperty& c) { m_type = c; }

    void writeTo(QXmlStreamWriter* w) const;
    void readFrom(QXmlStreamReader* r);

private:
    ComboProperty m_type;
};

//! Holds a floating-point variable, accessible from FitparameterModel.
class FitDoubleItem : public QObject {
    Q_OBJECT
public:
    explicit FitDoubleItem(double value, QObject* parent = nullptr);

    double value() const { return m_value; }
    void setValue(double value) { m_value = value; }

    const RealLimits& limits() const { return m_limits; }
    void setLimits(const RealLimits& l) { m_limits = l; }

    int decimals() const { return m_decimals; }
    void setDecimals(int v) { m_decimals = v; }

    virtual void writeTo(QXmlStreamWriter* w) const;
    virtual void readFrom(QXmlStreamReader* r);

private:
    double m_value;
    int m_decimals;      // no need to save/load
    RealLimits m_limits; // no need to save/load
};

//! Holds floating-point variables which can be enabled or disabled, accessible from
//! FitparameterModel
class FitEditableDoubleItem : public FitDoubleItem {
public:
    explicit FitEditableDoubleItem(double value, bool isEnabled, QObject* parent = nullptr);

    bool isEnabled() const { return m_is_enabled; }
    void setEnabled(bool b) { m_is_enabled = b; }

    void writeTo(QXmlStreamWriter* w) const override;
    void readFrom(QXmlStreamReader* r) override;

private:
    bool m_is_enabled;
};

//! Represents a fit parameter in GUI. Contains links to corresponding ParameterItems in a tuning
//! tree.
class FitParameterItem : public QObject {
    Q_OBJECT
public:
    explicit FitParameterItem(QObject* parent = nullptr);

    void initMinMaxValues(const RealLimits& limits);

    AttLimits attLimits() const;

    bool isValid() const;

    QString displayName() const { return m_display_name; }
    void setDisplayName(QString displayName);

    double startValue() const;
    void setStartValue(double start_value);
    QObject* startValueItem() const;

    double minimum() const;
    void setMinimum(double minimum);
    QObject* minimumItem() const;

    double maximum() const;
    void setMaximum(double maximum);
    QObject* maximumItem() const;

    FitParameterLinkItem* addLinkItem(const QString& title, const QString& link);
    void removeLink(const QString& link);

    QVector<FitParameterLinkItem*> linkItems() const;
    QStringList links() const;

    void setTypeCombo(const ComboProperty& type);
    QString currentType() const;
    QObject* typeItem() const;

    void writeTo(QXmlStreamWriter* w) const;
    void readFrom(QXmlStreamReader* r);

private:
    void onTypeChange();
    void setLimitEnabled(FitEditableDoubleItem* propertyItem, bool enabled);
    bool isLimited() const;
    bool isFree() const;
    bool isLowerLimited() const;
    bool isUpperLimited() const;
    bool isFixed() const;

    QString m_display_name;
    std::unique_ptr<FitTypeItem> m_type_item;
    std::unique_ptr<FitDoubleItem> m_start_value_item;
    std::unique_ptr<FitEditableDoubleItem> m_min_item;
    std::unique_ptr<FitEditableDoubleItem> m_max_item;
    OwningVector<FitParameterLinkItem> m_links;
};

#endif // BORNAGAIN_GUI_MODEL_TUNE_FITPARAMETERITEM_H
