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

 Copyright 2019 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 "MvQPalette.h"

#include <vector>

#include <QConicalGradient>
#include <QMap>
#include <QPen>
#include <QWidget>
#include <QComboBox>

class QAbstractButton;
class QGridLayout;
class QLabel;
class QLineEdit;
class QPushButton;
class QSlider;
class QSpinBox;
class QTabWidget;
class QToolButton;
class QVBoxLayout;
class QButtonGroup;
class QStackedWidget;

struct MvQColourItem
{
    MvQColourItem(QColor col, QString name, const std::vector<int>& shades = {}) :
        col_(col),
        name_(name),
        shades_(shades) {}
    QColor col_;
    QString name_;
    std::vector<int> shades_;
};

class MvQCompactColourGrid : public QWidget
{
    Q_OBJECT

public:
    enum LayoutMode
    {
        RowMode,
        SingleRowMode,
        ColumnMode,
        SingleColumnMode
    };
    MvQCompactColourGrid(int minSize, QList<MvQColourItem> items, LayoutMode layoutMode, QWidget* parent = 0);
    void replace(QList<QColor>);
    int itemCount() const { return items_.count(); }


signals:
    void selected(QColor);

protected:
    void paintEvent(QPaintEvent* event);
    void resizeEvent(QResizeEvent* event);
    void mousePressEvent(QMouseEvent* event);
    void mouseMoveEvent(QMouseEvent* event);
    void leaveEvent(QEvent*);
    void makeItems(QList<MvQColourItem> baseItems);
    void createGrid();
    int index(QPoint);
    void sort();

    int minSize_;
    int minCellSize_{12};
    LayoutMode layoutMode_{SingleRowMode};
    int cellGap_{3};
    QList<MvQColourItem> items_;
    int rowNum_{0};
    int gridSizeX_{0};
    int gridSizeY_{0};
    int cellSize_{12};
    int cellsPerRow_{10};
    QPixmap pix_;
    QColor highlightCol_;
    QColor gridCol_;
    int highlightIndex_{-1};
};


class MvQColourGrid : public QWidget, public PaletteScanner
{
    Q_OBJECT

public:
    MvQColourGrid(int minSize, QWidget* parent = 0);
    void next(const std::string&, QColor, bool);

signals:
    void selected(QColor);

protected slots:
    void slotContextMenu(const QPoint& pos);

protected:
    void paintEvent(QPaintEvent* event);
    void resizeEvent(QResizeEvent* event);
    void mousePressEvent(QMouseEvent* event);
    void mouseMoveEvent(QMouseEvent* event);
    void leaveEvent(QEvent*);
    void createGrid();
    int index(QPoint);
    void sort();

    int minSize_;
    QList<MvQColourItem> items_;
    int gridSize_;
    int maxGridSize_{128};
    int cellSize_;
    int cellsPerRow_{8};
    int marginX_{0};
    int marginY_{0};
    QPixmap pix_;
    int highlightIndex_{-1};
    QColor highlightCol_;
};


class MvQColourWheel : public QWidget
{
    Q_OBJECT

public:
    MvQColourWheel(int minSize, QWidget* parent = 0);
    virtual void initColour(QColor) = 0;

public slots:
    virtual void slotSetColour(QColor) = 0;

signals:
    void colourSelected(QColor colour);
    void hslSelected(int, int, int);
    void dragFinished();

protected:
    enum DragType
    {
        NoDrag,
        RingDrag,
        ShapeDrag
    };

    void paintEvent(QPaintEvent* event);
    void resizeEvent(QResizeEvent* event);
    void mousePressEvent(QMouseEvent* event);
    void mouseMoveEvent(QMouseEvent* event);
    void mouseReleaseEvent(QMouseEvent* event);

    void createRing();
    virtual QConicalGradient makeRingGradient() = 0;
    virtual void createShape() = 0;
    void renderRingSelector(QPainter*);
    void renderShapeSelector(QPainter*);
    virtual DragType checkPoint(QPoint) = 0;
    void dragRingSelector(QPoint);
    void dragShapeSelector(QPoint);
    virtual bool isDark() const = 0;

    virtual void setHue(int) = 0;
    virtual void setCls(int, int) = 0;
    virtual bool clsToPos(QPointF&) = 0;
    virtual bool posToCls(QPointF, int&, int&) = 0;
    virtual bool projectPosToCls(QPointF, int&, int&) = 0;

protected:
    int minSize_;
    QPixmap ringPix_;
    QPixmap shapePix_;
    int ringWidth_{10};
    int outerRadius_;
    int innerRadius_;
    int margin_{4};
    int hue_{0};
    int sat_{128};
    int lgt_{128};
    int shapeSelectorRadius_{4};
    QPen huePenBlack_;
    QPen huePenWhite_;
    QPoint centre_;
    DragType dragType_{NoDrag};
    QPoint shapePos_;
    QPoint shapeOffset_;
    QPoint dragPos_;
};

class MvQHslWheel : public MvQColourWheel
{
    Q_OBJECT

public:
    MvQHslWheel(int minSize, QWidget* parent = 0);
    void initColour(QColor) override;

public slots:
    void slotSetColour(QColor) override;

protected:
    QConicalGradient makeRingGradient() override;
    void createShape() override;
    DragType checkPoint(QPoint) override;
    void setHue(int) override;
    void setCls(int, int) override;
    bool clsToPos(QPointF&) override;
    bool posToCls(QPointF, int&, int&) override;
    bool projectPosToCls(QPointF, int&, int&) override;

    int saturation(int, int);
    int chroma(int, int);
    bool isDark() const override;
};

class MvQHclWheel : public MvQColourWheel
{
    Q_OBJECT

public:
    MvQHclWheel(int minSize, QWidget* parent = 0);
    void initColour(QColor) override;

public slots:
    void slotSetColour(QColor) override;

protected:
    QConicalGradient makeRingGradient() override;
    void createShape() override;
    DragType checkPoint(QPoint) override;
    void setHue(int) override;
    void setCls(int, int) override;
    bool clsToPos(QPointF&) override;
    bool posToCls(QPointF, int&, int&) override;
    bool projectPosToCls(QPointF, int&, int&) override;
    bool isDark() const override;
    int scaleChromaFrom255(float v) const;
    int scaleLumFrom255(float v) const;
    int clipTo(int v, int minVal, int maxVal) const;

    int chroma_{0};
    int lum_{0};
    int ringChroma_{100};
    int ringLum_{76};
    int maxShapeChroma_{150};
    int maxShapeLum_{100};
};


class MvQColourWheelControl : public QWidget
{
    Q_OBJECT
public:
    MvQColourWheelControl(QWidget* parent);

    void initColour(QColor);
    void setColour(QColor);

protected slots:
    void buttonSelection(QAbstractButton* b);

signals:
    void hslSelected(int, int, int);
    void colourSelected(QColor);
    void dragFinished();

private:
    MvQColourWheel* currentWheel() const;

    QButtonGroup* bGroup_{nullptr};
    QStackedWidget* stacked_{nullptr};
    std::vector<MvQColourWheel*> wheels_;
};

#if 0
class MvQHslWheel : public QWidget
{
    Q_OBJECT

public:
    MvQHslWheel(int minSize, QWidget* parent = 0);
    void initColour(QColor);

public slots:
    void slotSetColour(int, int, int);

signals:
    void colourSelected(QColor colour);
    void hslSelected(int, int, int);
    void dragFinished();

protected:
    enum DragType
    {
        NoDrag,
        RingDrag,
        TriangleDrag
    };

    void paintEvent(QPaintEvent* event);
    void resizeEvent(QResizeEvent* event);
    void mousePressEvent(QMouseEvent* event);
    void mouseMoveEvent(QMouseEvent* event);
    void mouseReleaseEvent(QMouseEvent* event);

    void createRing();
    void createTriangle();
    void renderHueSelector(QPainter*);
    void renderLgtSatSelector(QPainter*);
    DragType checkPoint(QPoint);
    void dragRingSelector(QPoint);
    void dragTriangleSelector(QPoint);
    void setHue(int);
    void setLgtSat(int, int);
    bool lgtSatToPos(QPointF&);
    bool posToLgtSat(QPointF, int&, int&);
    bool projectPosToLgtSat(QPointF, int&, int&);
    int saturation(int, int);
    int chroma(int, int);

private:
    int minSize_;
    QPixmap ringPix_;
    QPixmap triPix_;
    int ringWidth_{14};
    int outerRadius_;
    int innerRadius_;
    int margin_{4};
    int hue_{0};
    int sat_{128};
    int lgt_{128};
    int lgtSatSelectorRadius_{4};
    QPen huePenBlack_;
    QPen huePenWhite_;
    QPoint centre_;
    DragType dragType_{NoDrag};
    QPoint lgtSatPos_;
    QPoint lgtSatOffset_;
    QPoint dragPos_;
};
#endif

class ColourSpaceSliderWidget : public QWidget
{
    Q_OBJECT
public:
    ColourSpaceSliderWidget(QWidget* parent) :
        QWidget(parent) {}
    virtual ~ColourSpaceSliderWidget() {}
    virtual void setColour(QColor col) = 0;

protected slots:
    void sliderChanged(int);
    void spinChanged(int);

signals:
    void colourChanged(QColor);

protected:
    void adjustGrooves(int ignoreIndex);
    virtual void adjustGroove(int index) = 0;
    virtual QColor currentColour() const = 0;

    QList<QSpinBox*> spins_;
    QList<QSlider*> sliders_;
    bool ignoreSpin_{false};
    bool ignoreSlider_{false};
};


class RGBWidget : public ColourSpaceSliderWidget
{
public:
    RGBWidget(QWidget* parent);
    void setColour(QColor col) override;

protected:
    void adjustGroove(int index) override;
    QColor currentColour() const override;
};


class HSLWidget : public ColourSpaceSliderWidget
{
public:
    HSLWidget(QWidget* parent);
    void setColour(QColor col) override;

protected:
    void adjustGroove(int index) override;
    QColor currentColour() const override;
};

class HCLabWidget : public ColourSpaceSliderWidget
{
public:
    HCLabWidget(QWidget* parent);
    void setColour(QColor col) override;

protected:
    void adjustGroove(int index) override;
    QColor currentColour() const override;
};

class GreyScaleWidget : public ColourSpaceSliderWidget
{
public:
    GreyScaleWidget(QWidget* parent);
    void setColour(QColor col) override;

protected:
    void adjustGroove(int index) override;
    QColor currentColour() const override;
};


class HCLuvWidget : public ColourSpaceSliderWidget
{
public:
    HCLuvWidget(QWidget* parent);
    void setColour(QColor col) override;

protected:
    void adjustGroove(int index) override;
    QColor currentColour() const override;
};


class AlphaSlider : public QWidget
{
    Q_OBJECT
public:
    AlphaSlider(QWidget* parent);

    int value() const;
    void setColour(QColor col);
    void setValue(int alpha);

signals:
    void valueChanged(int);

protected:
    void paintEvent(QPaintEvent*);
    void resizeEvent(QResizeEvent*);
    void mousePressEvent(QMouseEvent*);
    void mouseMoveEvent(QMouseEvent*);
    void mouseReleaseEvent(QMouseEvent*);

private:
    void moveHandlerBy(int dy);
    void adjustHandler();

    QRect grooveRect_{0, 0, 14, 1};
    QRect handlerRect_{0, 0, 14, 10};
    int min_{0};
    int max_{255};
    QColor colour_;
    bool inDrag_{false};
    QPoint dragStartPos_;
    int dragStartAlpha_{0};
    QPixmap pix_;
    QPixmap bgPix_;
};

class AlphaWidget : public QWidget
{
    Q_OBJECT
public:
    AlphaWidget(QWidget* parent, QVBoxLayout* layout = nullptr);

    void setColour(QColor col);
    void setValue(int value);
    int currentValue() const;

protected slots:
    void sliderChanged(int val);
    void spinChanged(int val);

signals:
    void valueChanged(int);

private:
    QColor colour_;
    bool ignoreSpin_{false};
    bool ignoreSlider_{false};
    AlphaSlider* slider_{nullptr};
    QSpinBox* spin_{nullptr};
};

class ColourTextWidget : public QWidget
{
    Q_OBJECT
public:
    ColourTextWidget(QWidget* parent);
    void setColour(QColor col);

protected slots:
    void htmlChanged(QString txt);

signals:
    void colourChanged(QColor);

private:
    enum LabelType
    {
        RgbInt,
        RgbFloat,
        HslInt,
        HslFloat
    };
    void setLabels(QColor col);

    bool ignoreHtmlEdit_{false};
    QLineEdit* htmlEdit_{nullptr};
    QMap<LabelType, QLabel*> labels_;
};

class MvQColourTextControl : public QObject
{
    Q_OBJECT
public:
    MvQColourTextControl(QGridLayout*, QWidget*);
    QLineEdit* htmlEdit() const { return htmlEdit_; }
    QLineEdit* mvEdit() const { return mvEdit_; }
    void setColour(QColor);

protected slots:
    void copyHtml();
    void copyMv();
    void htmlChanged(QString txt);
    void mvChanged(QString txt);

signals:
    void colourChanged(QColor);

private:
    void adjustHtmlEdit(QColor col);
    void adjustMvEdit(QColor col);

    QLineEdit* htmlEdit_{nullptr};
    QLineEdit* mvEdit_{nullptr};
    bool ignoreHtmlEdit_{false};
    bool ignoreMvEdit_{false};
};

class MvQColourSelectionWidget : public QWidget
{
    Q_OBJECT

public:
    explicit MvQColourSelectionWidget(QWidget* parent, bool embedded);
    void setColour(QColor);
    QColor currentColour() const { return currentColour_; }

protected slots:
    void slotWheelChangedHsl(int hue, int sat, int lgt);
    void slotWheelChangedColour(QColor col);
    void gridChangedColour(QColor col);
    void rgbChanged(QColor col);
    void hslChanged(QColor col);
    void hclAbChanged(QColor col);
    void hclUvChanged(QColor col);
    void greyChanged(QColor col);
    void alphaChanged(int value);
    void textChanged(QColor col);
    void tabIndexChanged(int index);
    void buttonSelection(QAbstractButton* b);

signals:
    void colourChanged(int, int, int);
    void colourSelected(QColor);
    void dragFinished();

protected:
    void paintEvent(QPaintEvent*);

private:
    void setCurrentColour(QColor);
    void adjustWheel(QColor col);
    void adjustRgb(QColor col);
    void adjustHsl(QColor col);
    void adjustHclUv(QColor col);
    void adjustGrey(QColor col);
    void adjustText(QColor col);

    QButtonGroup* bGroup_{nullptr};
    QStackedWidget* stacked_{nullptr};
    QTabWidget* tab_{nullptr};
    MvQColourWheelControl* wheel_{nullptr};
    MvQColourGrid* grid_{nullptr};

    QPushButton* okPb_{nullptr};
    QObject* parentObject_;
    QColor currentColour_;

    AlphaWidget* alphaW_{nullptr};
    RGBWidget* rgb_{nullptr};
    HSLWidget* hsl_{nullptr};
    HCLuvWidget* hclUv_{nullptr};
    GreyScaleWidget* greyW_{nullptr};

    MvQColourTextControl* textControl_{nullptr};
    ColourTextWidget* text_{nullptr};

    // int wheelTabIndex_{0};
    int rgbTabIndex_{0};
    int hslTabIndex_{0};
    int hclUvTabIndex_{0};
    int greyTabIndex_{0};
    // int textTabIndex_{0};
};

class MvQColourCombo : public QComboBox, public PaletteScanner
{
    Q_OBJECT

public:
    MvQColourCombo(QWidget* parent = nullptr);
    ~MvQColourCombo() {}

    QString currentValue() { return QString(); }
    void addValue(QString) {}

    // void refresh(const std::vector<std::string>&);

public slots:
    void changeSelection(QString);
    // void slotHelperEditConfirmed();

protected slots:
    void slotCurrentChanged(int);
    void slotCopyColour();
    void slotPasteColour();

signals:
    void colourSelected(QString);

protected:
    void next(const std::string&, QColor, bool);
    void addColour(QColor, QString, QString, bool setCurrent = false);
    void setCustomColour(QColor, QString, QString, bool setCurrent = false);
    // void updateHelper();
    void setColour(int, QColor);

    QPixmap pix_;
    bool broadcastChange_;
    int customColIndex_;
};

class MvQColourWidget : public QWidget
{
    Q_OBJECT

public:
    MvQColourWidget(QWidget* parent=nullptr);
    ~MvQColourWidget() {}

    void initColour(QString);
    void start() {}
    bool dialog() { return false; }
    QWidget* widget() { return selector_; }
    QString currentValue() const;

signals:
    void valueChanged(QString);

protected slots:
    void slotSelectorEdited(QColor);
    void slotComboEdited(QString);

private:
    MvQColourCombo* colCb_;
    MvQColourSelectionWidget* selector_;
    QString oriName_;
};
