/*****************************************************************************
 * $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$
 ****************************************************************************/


#ifndef __QTPROPERTY_FIELD_EDITOR__
#define __QTPROPERTY_FIELD_EDITOR__

#include "FieldEditor.h"

#include <QTabWidget>
#include <QEvent>

/**
 *  @brief Field editor for atomic type QVariant managed using a single line edit using QtPropertyBrowser
 *
 * \note with a little revamp this can become a full replacement for QtPropertyBrowser.
 *
 * This field editor can manage
 * - enum (editable or not)
 * - string (with regexp or titleCase constraints or not)
 * - boolean (with custom checkbox text)
 * - double/int values (with min/max/decimals constraints or not)
 *
 * A QtPropertyFieldEditor
 * - can be read only or not
 * - can have a place holder text (displayed when the value is empty) or not
 * - can be declared as mandatory. In this case if the field value is empty a warning message is sent to the CamiTKExtensionModelPresenter presenter
 * - can be declared as tab widget title. In this case its parent tab widget title will be interactively modified when the field edited value changes
 * - can be given a label to interactively update when the field edited value changes
 *
 * For string setting a specific format will modify the user input to follow a given regexp everytime
 * the user modify the field value (e.g. to ensure the field starts with an upper
 * case letter or a number use setFormat(R"(^[A-Z0-9].*)"))
 *
 * Usage example:
 * \code
 * maximumValueEditor = new QtPropertyFieldEditor(presenter, dataModel["maximum"], "Maximum Value", "If set this will constrain the parameter maximum value", maxValue);
 * maximumValueEditor->setPlaceHolderText("Maximum Value");
 * ... // use desired set methods here
 * myLayout->addWidget(maximumValueEditor->getWidget());
 * \endcode
 */
class QtPropertyFieldEditor : public FieldEditor {
    Q_OBJECT
public:
    /// Constructor
    /// If the model value is invalid (not yet present in the CamiTKExtensionModel), use the provided default value to create a new data model.
    /// @param defaultValue if given, used to set the data model if it does not exist yet
    QtPropertyFieldEditor(CamiTKExtensionModelPresenter* presenter, VariantDataModel& dataModel, const QString& name = "", const QString& description = "", const QVariant& defaultValue = QVariant());

    /// if isReadOnly is true the field value cannot be modified
    void setReadOnly(bool isReadOnly);

    /// set the displayed text when the field value is empty
    void setPlaceHolderText(const QString& placeHolderText);

    /// if isMandatory is true, the presenter gets a warning message if the value becomes empty
    void setMandatory(bool isMandatory);

    /// it isTabWidgetTitle is true, look for a parent widget that is a TabWidget and sets its text to the current field value every time it changes
    void setTabWidgetTitle(bool isTabWidgetTitle);

    /// set the label to update/synchronize when the current field is modified
    void setLabelToUpdateWithFieldValue(QLabel* labelToUpdate);

    /// For enums: set the values
    void setEnumValues(const QStringList& enumValues);

    /// if true, the last item is editable
    void setEditableEnum(bool editableEnums);

    /// For strings: set the regexp validator for QString properties and editable enums
    void setRegExp(const QString& regExp);

    /// For strings: ensure the value has a title case format
    void setTitleCase(bool isTitleCase);

    /// for boolean: set the text to appear besides the check box (instead of default True/False) (this is useful for compact field editor)
    void setCheckBoxText(QString checkBoxText);

    /// for double/int: set the minimum value
    void setMinimum(QVariant minimum);

    /// for double/int: set the maximum value
    void setMaximum(QVariant maximum);

    /// for double/int: set the number of decimal values
    void setDecimals(int decimals);

    /// Build an line edit for the encapsulated VariantDataModel
    virtual QWidget* getWidget() override;

    /// filters wheel events for enum (edited as QComboBox) as it is too easy to modify the value when
    /// the mouse is just hovering on the QComboBox and could have disastrous consequences
    bool eventFilter(QObject* obj, QEvent* event) override;

public slots:

    /// for anything but enum: called when the edited value has changed
    void valueChangedInPropertyField(QtProperty* property, const QVariant& value);

    /// for editable enum: called when the modifiable value has changed
    void editTextChanged(const QString& newValue);

    /// for editable enum: called when the selected item has changed in order to let the user edit only the last line (not the other values)
    void currentIndexChanged(int index);

private:
    /// the main property manager (does not include the read only property)
    QtVariantPropertyManager* propManager;

    /// the factory to create widget from property
    QtVariantEditorFactory* factory;

    /// the initial value
    QVariant initialValue;

    /// can be empty if not an enum
    QStringList enumValues;

    /// can be empty if none is provided
    QString regExp;

    /// if true the value will be transformed to title case
    bool isTitleCase;

    /// if true the widget can not be edited (default is false)
    bool isReadOnly;

    /// equals to -1 if not provided
    int decimals;

    /// invalid value if not provided
    QVariant minimum, maximum;

    /// place holder text for the widget
    QString placeHolderText;

    /// if not empty, then use it as the QCheckBox text (when myWidget contains a QCheckBox, i.e. for boolean values)
    QString checkBoxText;

    /// if true, when this field be empty the stylesheet will make the user react!
    bool isMandatory;

    /// if true, the user can add one more values (only used for enums)
    bool editableEnums;

    /// if true, then a modification in this field will modify the current tab title
    bool isTabWidgetTitle;

    /// if not null, then a modification in this field will modify the label's text
    QLabel* labelToUpdate;

    /// if this is an enum field and editable is true, this is the editable line edit (otherwise nullptr)
    QLineEdit* comboBoxEditableLineEdit;

    /// the underlying QtVariantProperty (build thanks to qtpropertymanager)
    QtVariantProperty* myProp;

    /// build a property of the given name and value, the type is automatically deduced from value (uses QtPropertyBrowser)
    /// if the QVariant type is not supported, myProp is equals to nullptr after calling this method
    void buildProperty(QString name, QVariant value, QStringList enumValues = {});

    // return the first parent that is a tab widget (used only when this is declared as a tab widget title)
    QTabWidget* findParentTabWidget();
};

#endif // __QTPROPERTY_FIELD_EDITOR__