//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      GUI/View/Sample/HeinzFormLayout.h
//! @brief     Defines class HeinzFormLayout.
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2021
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#ifndef BORNAGAIN_GUI_VIEW_SAMPLE_HEINZFORMLAYOUT_H
#define BORNAGAIN_GUI_VIEW_SAMPLE_HEINZFORMLAYOUT_H

#include "GUI/Model/Descriptor/PolyItem.h"
#include "GUI/View/Sample/SelectionContainerForm.h"
#include <QFormLayout>
#include <QSpinBox>

class SampleEditorController;
class VectorProperty;

//! Extends QFormLayout, for use in SampleDesigner.
//!
//! Helps to add edit controls which operate on Properties (DoubleProperty, VectorProperty,
//! PolyItem). Also takes care of bold printed labels, the connection of labels and edit
//! controls (buddies), which is necessary to realize the "label shows unit of value" feature.
//! Establishes connections to the SampleEditorController.
class HeinzFormLayout : public QFormLayout {
public:
    //! Create a layouter which operates on the given parent widget.
    //!
    //! If the parent has no layout yet, a QFormLayout is installed. If the parentWidget already has
    //! a QFormLayout, this will be used for the calls later on.
    //! The given SampleEditorController is used to create connections e.g. when a DoubleSpinBox
    //! value has been changed.
    HeinzFormLayout(SampleEditorController* ec);

    //! Insert a row with a bold printed label.
    //!
    //! If necessary, a colon (':') is added to the label.
    void insertRow(int row, QString label, QWidget* w);

    void insertRow(int row, QWidget* w);

    void addBoldRow(const QString& label, QWidget* w);

    //! Add a row with a selection.
    //!
    //! The whole selection is realized by adding a SelectionContainerForm. This
    //! SelectionContainerForm is limited to contain the selection combo box and a list of double
    //! values represented by DoubleProperties. To add more complex selections (e.g. with
    //! sub-selections or different value types), this method and the SelectionContainerForm is not
    //! sufficient. It has to be done "manually".
    //! For more details, see SelectionContainerForm.
    //! Returns the newly added row.
    template <typename T> void addSelection(PolyItem<T>& d)
    {
        addBoldRow(d.label(), new SelectionContainerForm(QFormLayout::parentWidget(), d, m_ec));
    }

    //! Adds a row with a bold printed label and a DoubleSpinBox.
    //!
    //! The DoubleSpinBox is initialized with the contents found in the DoubleProperty (e.g. limits,
    //! decimals, unit). The DoubleSpinBox is set as the "buddy" of the label. This is necessary to
    //! realize the "label shows unit of value" feature. Changes of the DoubleSpinBox are signaled
    //! to the SampleEditorController which has been overhanded in the constructor of this.
    //! It is connected to SampleEditorController::setDouble(). If a different method
    //! should be called (e.g. for a special undo functionality), this method is not sufficient. It
    //! would have to be done "manually" or with the overload which takes a slot (see below).
    //! Returns the newly added row.
    void addValue(DoubleProperty& d);

    //! Adds a row with a bold printed label and a DoubleSpinBox.
    //!
    //! Same as above, but the called slot in case of a value change has to be overhanded.
    //! Use this only if the standard (calling SampleEditorController::setDouble()) is not
    //! sufficient.
    void addValue(DoubleProperty& d, std::function<void(double)> onValueChange);

    //! Inserts a row with a bold printed label and a DoubleSpinBox.
    //!
    //! Same functionality as addValue(), please read there.
    void insertValue(int row, DoubleProperty& d);

    //! Inserts a row with a bold printed label and a DoubleSpinBox.
    //!
    //! Same functionality as addValue(), please read there.
    void insertValue(int row, DoubleProperty& d, std::function<void(double)> onValueChange);

    //! Adds a row with a bold printed label and a set of DoubleProperties.
    //!
    //! The label describes the set of the DoubleProperties and is located in the first column of
    //! the layout. The DoubleSpinBoxes for each DoubleProperty are created as children of a newly
    //! created widget, which is positioned in the second column of the layout. If the number of
    //! values is greater than 1, the related labels are shown above them, to limit the necessary
    //! space to the right. For the creation, signaling, unit handling etc. of one DoubleProperty
    //! the same applies as when adding a single DoubleProperty with the method addValue(),
    //! therefore please read there for more details. Returns the newly added row.
    void addGroupOfValues(const QString& labelText, const DoubleProperties& values);

    //! Adds a row with a bold printed label and the 3 values of a 3D vector.
    //!
    //! Works the same as addGroupOfValues. The label is taken from the VectorProperty.
    //! In addition, the caller can define whether the labels are above the values
    //! (vertically=true), or whether labels and values are all placed in one row
    //! (vertically=false).
    void addVector(VectorProperty& d, bool vertically = true);

    //! Shows or hides the widgets in a row.
    void setRowVisible(int row, bool visible);

    //! Adds a button for structure editing.
    //!
    //! Creates a widget, places the given button as a child left-aligned into this widget and adds
    //! the newly created widget as a row in the layout.
    void addStructureEditingRow(QPushButton* button);

    //! Find a widget in the given position.
    template <typename T> T widgetAt(int row, QFormLayout::ItemRole role)
    {
        return qobject_cast<T>(QFormLayout::itemAt(row, role)->widget());
    }

private:
    SampleEditorController* m_ec;
};

#endif // BORNAGAIN_GUI_VIEW_SAMPLE_HEINZFORMLAYOUT_H
