/***************************** LICENSE START ***********************************

 Copyright 2018 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#pragma once

#include <set>

#include <QDate>
#include <QIcon>
#include <QMap>
#include <QWidget>

class CalendarDialog;
class QComboBox;
class QGridLayout;
class QLabel;
class QLineEdit;
class QToolButton;
class QRadioButton;

class EditLine;
class MvQProperty;

class EditLineHeader : public QWidget
{
    Q_OBJECT
public:
    EditLineHeader(QString name, QGridLayout* grid, QWidget* parent = nullptr);
    void addLine(EditLine*);
    void init();
    void expand();
    void collapse();
    bool hasEdited() const;
    void setGroupProp(MvQProperty* gr) { groupProp_ = gr; }
    MvQProperty* groupProp() const { return groupProp_; }

public Q_SLOTS:
    void slotClear();

protected Q_SLOTS:
    void slotToggleExpand();
    void lineValueChanged(EditLine*, QString);

Q_SIGNALS:
    void expandChanged(bool);
    void needRepaint();

protected:
    void paintEvent(QPaintEvent*) override;
    void mousePressEvent(QMouseEvent*) override;
    void adjustLabel(bool hasEdited);
    void setChangeStatus(bool b);
    void forceRepaint();

    bool expanded_;
    bool needRepaint_;
    QLabel* label_;
    QToolButton* expandTb_;
    QToolButton* clearTb_;
    QList<EditLine*> lines_;
    QIcon openIcon_;
    QIcon closeIcon_;
    MvQProperty* groupProp_;
    QString title_;
};

class EditLineFactory
{
public:
    EditLineFactory(QGridLayout* grid, QWidget* parent) :
        grid_(grid),
        parent_(parent) {}

    EditLine* make(MvQProperty* prop, EditLineHeader* header);

protected:
    QGridLayout* grid_;
    QWidget* parent_;
};

class EditLine : public QObject
{
    Q_OBJECT

public:
    EditLine(MvQProperty* prop, QGridLayout* grid, EditLineHeader* header, QWidget* parent);

    void setVisible(bool b);
    void setHiddenByRule(bool b);
    virtual QString value() const = 0;
    virtual void clear() = 0;
    bool isSet() const;
    MvQProperty* property() const { return prop_; }
    void setHelper(QString s) { helper_ = s; }
    virtual void setCompleter(const std::set<std::string>&) {}
    virtual void updateCompleter(const std::set<std::string>&) {}
    virtual void applyChange() = 0;

protected Q_SLOTS:
    void slotReset();

Q_SIGNALS:
    void valueChanged(EditLine*, QString);

protected:
    enum ColumnPos
    {
        ResetColumn = 0,
        NameColumn = 1,
        HelperColumn = 2,
        WidgetColumn = 3,
        SuffixColumn = 4
    };

    virtual QWidget* widget() = 0;
    void show(bool);
    QString editorValue(QLineEdit*) const;
    void setCompleterForLe(const std::set<std::string>& coVals, QLineEdit* le);
    void updateCompleterForLe(const std::set<std::string>& coVals, QLineEdit* le);
    void checkState();
    void setLabelBold(bool b);

    MvQProperty* prop_;
    QString name_;
    QString oriVal_;
    QWidget* parentWidget_;
    QToolButton* resetTb_;
    QLabel* nameLabel_;
    QToolButton* helpTb_;
    QLabel* suffixLabel_;
    QString helper_;
    int row_;
    bool visible_;
    bool hiddenByRule_;
};

class EditStringLine : public EditLine
{
    Q_OBJECT
public:
    EditStringLine(MvQProperty* prop, QGridLayout* grid, EditLineHeader* header, QWidget* parent);
    QString value() const override;
    void clear() override;
    void applyChange() override;
    void setCompleter(const std::set<std::string>&) override;
    void updateCompleter(const std::set<std::string>&) override;

protected Q_SLOTS:
    void currentChanged(QString s);

protected:
    QWidget* widget() override;
    QLineEdit* le_;
};

class EditLabelLine : public EditLine
{
    Q_OBJECT

public:
    EditLabelLine(MvQProperty* prop, QGridLayout* grid, EditLineHeader* header, QWidget* parent);
    QString value() const override { return {}; }
    void clear() override {}
    void applyChange() override {}

protected:
    QWidget* widget() override;
    QLabel* label_;
};

class EditComboLine : public EditLine
{
    Q_OBJECT
public:
    EditComboLine(MvQProperty* prop, QGridLayout* grid, EditLineHeader* header, QWidget* parent);
    void init(QStringList);
    QString value() const override;
    void clear() override;
    void applyChange() override;

protected Q_SLOTS:
    void currentChanged(QString s);

protected:
    QWidget* widget() override;
    QComboBox* cb_;
};

class EditDateLine : public EditLine
{
    Q_OBJECT
public:
    EditDateLine(MvQProperty* prop, QGridLayout* grid, EditLineHeader* header, QWidget* parent);
    QString value() const override;
    void clear() override;
    void applyChange() override;

protected Q_SLOTS:
    void slotHelp();
    void slotDateSelected(QDate);
    void currentChanged(QString s);

protected:
    void hideCalendar();

    QWidget* widget() override;
    QLineEdit* le_;
    CalendarDialog* cal_;
};

class EditTimeLine : public EditLine
{
public:
    EditTimeLine(MvQProperty* prop, QGridLayout* grid, EditLineHeader* header, QWidget* parent);
    QString value() const override;
    void clear() override;
    void applyChange() override;

protected:
    QWidget* widget() override;
    QLineEdit* le_;
};

class EditAreaLine : public EditLine
{
    Q_OBJECT
public:
    EditAreaLine(MvQProperty* prop, QGridLayout* grid, EditLineHeader* header, QWidget* parent);
    QString value() const override;
    void clear() override;
    void applyChange() override;

protected Q_SLOTS:
    void currentChanged(QString s);

protected:
    QWidget* widget() override;
    QLineEdit* nLe_;
    QLineEdit* sLe_;
    QLineEdit* wLe_;
    QLineEdit* eLe_;
    QWidget* w_;
};

class EditAddRemoveLine : public EditLine
{
    Q_OBJECT
public:
    EditAddRemoveLine(MvQProperty* prop, QGridLayout* grid, EditLineHeader* header, QWidget* parent);
    QString value() const override;
    void clear() override;
    void applyChange() override;

protected Q_SLOTS:
    void slotAdd();
    void slotRemove();

protected:
    void currentChanged();
    QWidget* widget() override;
    void checkButtonStatus();

    QWidget* w_;
    QLabel* cntLabel_;
    QToolButton* addTb_;
    QToolButton* removeTb_;
    int minimum_;
    int maximum_;
    int current_;
};

class EditBoolLine : public EditLine
{
    Q_OBJECT
public:
    EditBoolLine(MvQProperty* prop, QGridLayout* grid, EditLineHeader* header, QWidget* parent);
    QString value() const override;
    void clear() override;
    void applyChange() override;

protected Q_SLOTS:
    void slotStatusChanged(bool);

protected:
    QWidget* widget() override;

    QWidget* w_;
    QRadioButton* radioOn_;
    QRadioButton* radioOff_;
    QString strOnData_;
    QString strOffData_;
};

class EditConditionLine : public EditLine
{
    Q_OBJECT
public:
    EditConditionLine(MvQProperty* prop, QGridLayout* grid, EditLineHeader* header, QWidget* parent);
    QString value() const override;
    void clear() override;
    void applyChange() override;

protected Q_SLOTS:
    void condOperChanged(int);
    void condValueChanged(QString);

protected:
    void currentChanged();
    QWidget* widget() override;
    void checkButtonStatus();

    QWidget* w_;
    QComboBox* condOperCb_;
    QLineEdit* condValueLe_;
    MvQProperty* condOperProp_;
    QString condOperOriVal_;
    bool broadcastChange_;
};

class EditMultiConditionLine : public EditLine
{
    Q_OBJECT
public:
    EditMultiConditionLine(MvQProperty* prop, QGridLayout* grid, EditLineHeader* header, QWidget* parent);
    QString value() const override;
    void clear() override;
    void applyChange() override;
    void setCompleter(const std::set<std::string>&) override;
    void updateCompleter(const std::set<std::string>&) override;

protected Q_SLOTS:
    void paramChanged(QString);
    void condRankChanged(QString);
    void condRankTbChanged(bool);
    void condControlChanged(bool);
    void condOperChanged(int);
    void condValueChanged(QString);

protected:
    void currentChanged();
    QWidget* widget() override;
    void checkButtonStatus();

    QWidget* w_;
    QLineEdit* paramLe_;
    QToolButton* condRankTb_;
    QLabel* condRankLabel_;
    QLineEdit* condRankLe_;
    QToolButton* condControlTb_;
    QLabel* condControlLabel_;
    QComboBox* condOperCb_;
    QLineEdit* condValueLe_;
    MvQProperty* condRankProp_;
    MvQProperty* condOperProp_;
    MvQProperty* condValueProp_;
    QString condRankOriVal_;
    QString condOperOriVal_;
    QString condValueOriVal_;
    bool broadcastChange_;
};
