/* gLineChart Header
 *
 * Copyright (c) 2019-2024 The OSCAR Team
 * Copyright (C) 2011-2018 Mark Watkins 
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License. See the file COPYING in the main directory of the source code
 * for more details. */

#ifndef GLINECHART_H
#define GLINECHART_H

#include <QPainter>
#include <QVector>

#include "Graphs/layer.h"
#include "SleepLib/event.h"
#include "SleepLib/day.h"
#include "Graphs/gLineOverlay.h"

#define ENABLE_ALWAYS_ON_ZERO_RED_LINE_FLOW_RATE
enum DottedLineCalc {
    DLC_Zero, DLC_Min, DLC_Mid, DLC_Perc, DLC_Max, DLC_UpperThresh, DLC_LowerThresh
};

QColor darken(QColor color, float p = 0.5);

struct DottedLine {
public:
    DottedLine() {
        code = NoChannel;
        type = Calc_Zero;
        value = 0;
        visible = false;
        available = false;
    }
    DottedLine(const DottedLine & copy) {
        code = copy.code;
        type = copy.type;
        value = copy.value;
        available = copy.available;
        visible = copy.visible;
    }
    DottedLine(ChannelID code, ChannelCalcType type, bool available = false):
        code(code), type(type), available(available) {}

    EventDataType calc(Day * day) {
        if (day == nullptr) {
            qWarning() << "DottedLine::calc called with null day object";
            return 0;
        }

        available = day->channelExists(code);
        value = day->calc(code, type);
        return value;
    }

    DottedLine& operator=(const DottedLine& other) = default;

    ChannelID code;
    ChannelCalcType type;
    EventDataType value;
    bool visible;
    bool available;
};
QDataStream & operator<<(QDataStream &, const DottedLine &);
QDataStream & operator>>(QDataStream &, DottedLine &);


/*! \class gLineChart
    \brief Draws a 2D linechart from all Session data in a day. EVL_Waveforms typed EventLists are accelerated.
    */
class gLineChart: public Layer
{
    friend class gGraphView;
  public:
    /*! \brief Creates a new 2D gLineChart Layer
        \param code  The Channel that gets drawn by this layer
        \param square_plot Whether or not to use square plots (only effective for EVL_Event typed EventList data)
        \param disable_accel Whether or not to disable acceleration for EVL_Waveform typed EventList data
        */
    gLineChart(ChannelID code, bool square_plot = false, bool disable_accel = false);
    virtual ~gLineChart();

    //! \brief The drawing code that fills the vertex buffers
    virtual void paint(QPainter &painter, gGraph &w, const QRegion &region);

    //! \brief Set Use Square plots for non EVL_Waveform data
    void SetSquarePlot(bool b) { m_square_plot = b; }

    //! \brief Returns true if using Square plots for non EVL_Waveform data
    bool GetSquarePlot() { return m_square_plot; }

    //! \brief Set this if you want this layer to draw it's empty data message
    //! They don't show anymore due to the changes in gGraphView. Should modify isEmpty if this still gets to live
    void ReportEmpty(bool b) { m_report_empty = b; }

    //! \brief Returns whether or not to show the empty text message
    bool GetReportEmpty() { return m_report_empty; }

    //! \brief Sets the ability to Disable waveform plot acceleration (which hides unseen data)
    void setDisableAccel(bool b) { m_disable_accel = b; }

    //! \brief Returns true if waveform plot acceleration is disabled
    bool disableAccel() { return m_disable_accel; }

    //! \brief Sets the Day object containing the Sessions this linechart draws from
    virtual void SetDay(Day *d);

    //! \brief Returns Minimum Y-axis value for this layer
    virtual EventDataType Miny();

    //! \brief Returns Maximum Y-axis value for this layer
    virtual EventDataType Maxy();

    //! \brief Returns Minimum Y-axis value for this layer
    virtual EventDataType actualMinY() {return m_actual_min_y;};

    //! \brief Returns Maximum Y-axis value for this layer
    virtual EventDataType actualMaxY() {return m_actual_max_y;};

    //! \brief Returns true if all subplots contain no data
    virtual bool isEmpty();

    //! \brief Add Subplot 'code'. Note the first one is added in the constructor.
    void addPlot(ChannelID code, bool square=false) { m_codes.push_back(code); m_enabled[code] = true; m_square.push_back(square); }

    //! \brief Returns true of the subplot 'code' is enabled.
    bool plotEnabled(ChannelID code) { if ((m_enabled.contains(code)) && m_enabled[code]) { return true; } else { return false; } }

    //! \brief Enable or Disable the subplot identified by code.
    void setPlotEnabled(ChannelID code, bool b) { m_enabled[code] = b; }

    QString getMetaString(qint64 time);

    void addDotLine(DottedLine dot) { m_dotlines.append(dot); }
    QVector<DottedLine> m_dotlines;
    QHash<ChannelID, bool> m_flags_enabled;
    QHash<ChannelID, QHash<quint32, bool> > m_dot_enabled;

    virtual Layer * Clone() {
        gLineChart * lc = new gLineChart(m_code);
        Layer::CloneInto(lc);
        CloneInto(lc);
        return lc;
    }

    void CloneInto(gLineChart * layer) {
        layer->m_dotlines = m_dotlines;
        layer->m_flags_enabled = m_flags_enabled;
        layer->m_dot_enabled = m_dot_enabled;
        layer->m_report_empty = m_report_empty;
        layer->m_square_plot = m_square_plot;
        layer->m_disable_accel = m_disable_accel;
        layer->subtract_offset = subtract_offset;
        layer->m_codes = m_codes;
        layer->m_threshold = m_threshold;
        layer->m_square = m_square;
        layer->m_enabled = m_enabled;
        layer->lines = lines;
        layer->lasttext = lasttext;
        layer->lasttime = lasttime;
    }

    virtual void resetGraphViewSettings();

  protected:
    //! \brief Mouse moved over this layers area (shows the hover-over tooltips here)
    virtual bool mouseMoveEvent(QMouseEvent *event, gGraph *graph);


    bool m_report_empty;
    bool m_square_plot;
    bool m_disable_accel;
    EventDataType m_actual_min_y=0,m_actual_max_y=0;

    //! \brief Used by accelerated waveform plots. Must be >= Screen Resolution (or at least graph width)
    static const int max_drawlist_size = 10000;

    //! \brief The list of screen points used for accelerated waveform plots..
    QPointF m_drawlist[max_drawlist_size];

    int subtract_offset;

    QVector<ChannelID> m_codes;
    QStringList m_threshold;
    QVector<bool> m_square;
    QHash<ChannelID, bool> m_enabled; // plot enabled
    QHash<ChannelID, gLineOverlayBar *> flags;

    QVector<QLine> lines;

    QString lasttext;
    qint64 lasttime;
};

#endif // GLINECHART_H