#include "xlruchartsform.h"
#include "ui_xlruchartsform.h"

#include <QChart>
#include <QLineSeries>
#include <QChartView>

#include <QGraphicsScene>
#include <QGraphicsLayout>
#include <QValueAxis>
#include <QDateTimeAxis>
#include <QDateTime>
#include <QTimerEvent>
#include <QToolTip>
#include <QMessageBox>
#include <QSpinBox>
#include <QtCharts/QLegendMarker>

#include <QDebug>

using namespace QtCharts;


class MyChartView: public QChartView
{
    //Q_OBJECT

public:
   void mousePressEvent(QMouseEvent* event) override
   {
#if 0
       QChart* _chart=chart();

       //qreal x = (event->pos()).x();
       //qreal y = (event->pos()).y();

       //qreal xVal = _chart->mapToValue(event->pos()).x();
       qreal yVal = _chart->mapToValue(event->pos()).y();

       if (true) //xVal <= maxX && xVal >= minX && yVal <= maxY && yVal >= minY)
       {
           QToolTip::showText(mapToGlobal(event->pos()), QString("%1").arg(yVal));
       }
#endif
       QChartView::mousePressEvent(event);
   }
};

class Sensor
{
public:
    QString name;
    QLineSeries* series;
    MyChartView* view;
    QValueAxis* axis;

    Sensor():
        series(0),
        view(0),
        axis(0)
    {
    }
};

class XlruChartsForm::Implementation
{
public:

   QtCharts::QChart* ch;
   QVector<Sensor> sensors;
   QVector<QLineSeries*> s;

   MyChartView* mainView;
   QDateTimeAxis* commonX;
   QValueAxis* temperatureY;
   QValueAxis* currentY;
   QValueAxis* voltageY;

   QVector<MyChartView*> cViews;

   int t_id;

   QDateTime timeStart;
   qint64 timeStartMs;

   QDateTime timeNow;
   qint64 timeNowMs;
   qint64 lastSampleMs;
   bool tgtAlive;

   QMessageBox alertBox;

   int max_temperature;
   int max_temp_period;
   bool max_temp_restart;

   Implementation():
       alertBox(QMessageBox::Warning, "WARNING", "Overtemperature")
   {
       alertBox.setWindowFlags(Qt::WindowStaysOnTopHint);
       alertBox.setModal( false );

       tgtAlive=false;
       lastSampleMs=0;
       max_temperature=0;
       max_temp_period=0;
       max_temp_restart=false;
   }

   QChart* newCharts()
   {
       QChart* c=new QChart;
       c->setAnimationOptions(QChart::NoAnimation);

       return c;
   }
   QLineSeries* newSeries(const QString& name, QChart* c, QValueAxis* axis)
   {
       QLineSeries* tmp=new QLineSeries;
       s.append(tmp);
       c->addSeries(tmp);
       tmp->attachAxis(commonX);
       tmp->attachAxis(axis);
       //c->setAxisX(commonX, tmp);
       //c->setAxisY(axis, tmp);
       tmp->setName(name);
       return tmp;
   }
};

XlruChartsForm::XlruChartsForm(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::XlruChartsForm),
    p_(*new XlruChartsForm::Implementation),
    m_tooltip(0)
{
    ui->setupUi(this);

    ui->tbEnable->setChecked(true);

    p_.mainView=new MyChartView;
    p_.mainView->setOptimizationFlag(QGraphicsView::DontSavePainterState, true);
    p_.mainView->setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing, true);
    p_.mainView->setRenderHint(QPainter::Antialiasing, false);
    p_.mainView->setRenderHint(QPainter::TextAntialiasing, false);
    p_.mainView->setRenderHint(QPainter::SmoothPixmapTransform, false);
    p_.mainView->setRubberBand( QChartView::HorizontalRubberBand );

    p_.mainView->chart()->layout()->setContentsMargins(0, 0, 0, 0);
    p_.mainView->chart()->setBackgroundRoundness(0);
    p_.mainView->chart()->setMargins(QMargins(0, 0, 0, 0));

    //p_.mainView->setToolTip("Monitoring Temperature/Current/Voltage");

    //p_.mainView->chart()->setMinimumSize(50, 50);

    p_.ch=p_.mainView->chart();

    //theChart=this->chart();
    //if (commonX==0)
    {
        p_.commonX= new QDateTimeAxis;
        p_.commonX->setFormat("h:mm:ss");
        p_.commonX->setTickCount(0);
        p_.commonX->setGridLineVisible(false);

        p_.timeNow=QDateTime::currentDateTime();
        p_.timeNowMs=p_.timeNow.toMSecsSinceEpoch();

        p_.timeStart=p_.timeNow;
        p_.timeStartMs=p_.timeNowMs;

        p_.commonX->setMin(p_.timeNow);
        p_.commonX->setMax(p_.timeNow.addSecs(60));

        p_.temperatureY=new QValueAxis;
        p_.temperatureY->setGridLineVisible(false);
        p_.temperatureY->setRange(10, 80);
        p_.temperatureY->setVisible(true);
        p_.temperatureY->setTitleText("Temp(C)");
        p_.temperatureY->setTitleVisible(true);

        p_.currentY=new QValueAxis;
        p_.currentY->setGridLineVisible(false);
        p_.currentY->setRange(0, 10);
        p_.currentY->setVisible(true);
        p_.currentY->setTitleText("Curr(A)");
        p_.currentY->setTitleVisible(true);

        p_.voltageY=new QValueAxis;
        p_.voltageY->setGridLineVisible(false);
        p_.voltageY->setRange(-20, 20);
        p_.voltageY->setVisible(false);
        p_.voltageY->setTitleText("Volt(V)");
        p_.voltageY->setTitleVisible(true);

        p_.mainView->chart()->addAxis(p_.commonX, Qt::AlignBottom);
        p_.mainView->chart()->addAxis(p_.temperatureY, Qt::AlignRight);
        p_.mainView->chart()->addAxis(p_.currentY, Qt::AlignRight);
        p_.mainView->chart()->addAxis(p_.voltageY, Qt::AlignRight);

    }

    p_.ch->setMargins(QMargins(2, 2, 2, 2));

    ui->verticalLayout->addWidget(p_.mainView);


    this->temperatureAxis=p_.temperatureY;
    this->currentAxis=p_.currentY;
    this->voltageAxis=p_.voltageY;

    p_.t_id=this->startTimer(2000);
}

void XlruChartsForm::timerEvent(QTimerEvent * e)
{
    if (e->timerId()==p_.t_id)
    {
        p_.timeNow=QDateTime::currentDateTime();
        p_.timeNowMs=p_.timeNow.toMSecsSinceEpoch();
#if 0
        int i=0;
        foreach (QLineSeries* s, p_.s)
        {
            int r=qrand();

            addTemperatureSample(i, (r*(float(300)/RAND_MAX)-150));
            ++i;
        }
#endif

#if 0
        for(unsigned int ii=6; ii<6+4; ++ii)
        {
            addSample(ii, 10*ii);
        }
#endif

        if ((p_.timeNowMs-p_.lastSampleMs)>1000*10) //Dead?
        {
            p_.tgtAlive=false;
        }
        else
        {
            p_.commonX->setMax(p_.timeNow);
            Qt::CheckState cs=ui->cbAutoPurge->checkState();
            switch(cs)
            {
            default:
            case Qt::Unchecked:
                break;
            case Qt::PartiallyChecked:
                {
                    qint64 delta=p_.timeNowMs-p_.timeStartMs;
                    if (delta>(1000*60*60))
                    {
                        p_.timeStart=p_.timeNow.addMSecs(-60*30);
                        p_.timeStartMs=p_.timeStart.toMSecsSinceEpoch();
                        p_.commonX->setMin(p_.timeStart);
                    }
                }
                break;
            case Qt::Checked:
                {
                    if (!p_.tgtAlive)
                    {
                        p_.timeStart=p_.timeNow;
                        p_.timeStartMs=p_.timeStart.toMSecsSinceEpoch();
                        p_.commonX->setMin(p_.timeStart);
                    }
                }
                break;
            }
            p_.tgtAlive=true;
            if (p_.max_temp_restart)
            {
                ui->lblMaxTemp->setText(QString("%1").arg(p_.max_temp_period));
                p_.max_temp_period=-1000;

            }
            p_.max_temp_restart=false;
        }

    }
}

int XlruChartsForm::addSensor(const QString& name, QtCharts::QValueAxis* a, int id)
{
    if (id==-1)
    {
        id=p_.s.size();
    }
    if (id>=p_.s.size())
    {
       p_.sensors.resize(id+1);
    }
    else
    {
        //Already exist!!
        return -1;
    }

    QLineSeries* s=p_.newSeries(name, p_.ch, a);
    Sensor sensor;
    sensor.series=s;
    sensor.view=p_.mainView;
    sensor.name=name;
    sensor.axis=a;
    p_.sensors[id]=sensor;
    return id;
}

int XlruChartsForm::addTemperatureSensor(const QString& name, int id)
{
    return addSensor(name, p_.temperatureY, id);
}

int XlruChartsForm::addCurrentSensor(const QString& name, int id)
{
    return addSensor(name, p_.currentY, id);
}

int XlruChartsForm::addVoltageSensor(const QString& name, int id)
{
    return addSensor(name, p_.voltageY, id);
}

#if 0
void XlruChartsForm::addSampleAxis(int id, float value, QtCharts::QValueAxis *a)
{
    if ((id>=0) && (id<p_.s.size()))
    {
        QLineSeries* s=p_.sensors[id].series;
        if (s==0)
            return;

        s->append(p_.timeNowMs, value);
        if (a->min()>value)
        {
            a->setMin(value);
        }
        else if (a->max()<value)
        {
            a->setMax(value);
        }
    }
}
#endif

void XlruChartsForm::addSample(int id, float value)
{
    if (!ui->tbEnable->isChecked())
        return;

    if ((id>=0) && (id<p_.sensors.size()))
    {
        QLineSeries* s=p_.sensors[id].series;
        if (s==0)
            return;

        p_.lastSampleMs=p_.timeNowMs;

        s->append(p_.timeNowMs, value);
        QtCharts::QValueAxis *a=p_.sensors[id].axis;
        if (a!=0)
        {
            if (a->min()>value)
            {
                a->setMin(value);
            }
            else if (a->max()<value)
            {
                a->setMax(value);
            }
            if (a==temperatureAxis)
            {
                if (value>=ui->sbTempAlarm->value())
                {
                    if (ui->cbAlarms->isChecked())
                    {
                        QApplication::beep();
                        p_.alertBox.setText(QString("%1 Overtemperature = %2").arg(p_.sensors[id].name).arg(value));
                        p_.alertBox.show();
                        p_.alertBox.raise();
                    }
                }
                if (value>p_.max_temperature)
                    p_.max_temperature=value;
                if (value>p_.max_temp_period)
                {
                    p_.max_temp_period=value;
                    p_.max_temp_restart=true;
                }
            }
        }
    }
}

void XlruChartsForm::addCurrentSample(int id, float value)
{
    addSample(id, value); //, p_.currentY);
    //p_.currentY->setVisible(true);
}

void XlruChartsForm::addTemperatureSample(int id, float value)
{
    addSample(id, value); //, p_.temperatureY);
}

void XlruChartsForm::addVoltageSample(int id, float value)
{
    addSample(id, value); //, p_.currentY);
}

XlruChartsForm::~XlruChartsForm()
{
    delete &p_;
    delete ui;
}

void XlruChartsForm::on_toolButton_clicked()
{
	//Purge
    auto ts=p_.timeNow.addSecs(-60*10);
    p_.timeStart=ts;
    p_.timeStartMs=ts.toMSecsSinceEpoch();
    p_.commonX->setMin(ts);
}

void XlruChartsForm::on_tbClear_clicked()
{
    foreach (QXYSeries* s, p_.s)
    {
        s->clear();
    }
    p_.commonX->setMin(p_.commonX->max());

}

void XlruChartsForm::connectMarkers()
{
    // Connect all markers to handler
    foreach (QLegendMarker* marker, p_.ch->legend()->markers())
    {
        // Disconnect possible existing connection to avoid multiple connections
        QObject::disconnect(marker, SIGNAL(clicked()), this, SLOT(handleMarkerClicked()));
        QObject::connect(marker, SIGNAL(clicked()), this, SLOT(handleMarkerClicked()));
    }
}

void XlruChartsForm::disconnectMarkers()
{
    foreach (QLegendMarker* marker, p_.ch->legend()->markers()) {
        QObject::disconnect(marker, SIGNAL(clicked()), this, SLOT(handleMarkerClicked()));
    }
}

void XlruChartsForm::handleMarkerClicked()
{
    QLegendMarker* marker = qobject_cast<QLegendMarker*> (sender());
    Q_ASSERT(marker);

    switch (marker->type())

    {
        case QLegendMarker::LegendMarkerTypeXY:
        {

        // Toggle visibility of series
        marker->series()->setVisible(!marker->series()->isVisible());

        // Turn legend marker back to visible, since hiding series also hides the marker
        // and we don't want it to happen now.
        marker->setVisible(true);

        // Dim the marker, if series is not visible
        qreal alpha = 1.0;

        if (!marker->series()->isVisible())
        {
            alpha = 0.5;
        }

        QColor color;
        QBrush brush = marker->labelBrush();
        color = brush.color();
        color.setAlphaF(alpha);
        brush.setColor(color);
        marker->setLabelBrush(brush);

        brush = marker->brush();
        color = brush.color();
        color.setAlphaF(alpha);
        brush.setColor(color);
        marker->setBrush(brush);

        QPen pen = marker->pen();
        color = pen.color();
        color.setAlphaF(alpha);
        pen.setColor(color);
        marker->setPen(pen);

        break;
        }
    default:
        {
        qDebug() << "Unknown marker type";
        break;
        }
    }
}

#define USE_CALLOUT 1

void XlruChartsForm::connectTooltip()
{
    // Connect all series to handler
    foreach (Sensor s, p_.sensors)
    {
        connect(s.series, &QLineSeries::clicked, this, &XlruChartsForm::keepCallout);
        connect(s.series, &QLineSeries::hovered, this, &XlruChartsForm::tooltip);
    }
}


void XlruChartsForm::keepCallout()
{
#if USE_CALLOUT==1
    QAbstractSeries *series = qobject_cast<QAbstractSeries *>(sender());
    m_callouts.append(m_tooltip);

    m_tooltip = new Callout(p_.ch, series);
    m_tooltip->hide();
#endif
}

void XlruChartsForm::tooltip(QPointF point, bool state)
{
#if USE_CALLOUT==0
    if (!state)
        return;

    QAbstractSeries *series = qobject_cast<QAbstractSeries *>(sender());

    QString msg=QString("%1=%2 ").arg(series->name()).arg(point.y());

    QPointF anchor = series->chart()->mapToPosition(point, series);

    QToolTip::showText(mapToGlobal(QPoint(anchor.x(), anchor.y())), msg, this);

#else
    QAbstractSeries *series = qobject_cast<QAbstractSeries *>(sender());
    if (m_tooltip == 0){
        m_tooltip = new Callout(p_.ch, series);
    }
    if (state)
    {
        m_tooltip->setSeries(series);

        //qreal yVal = p_.ch->mapToValue(point, series).y();

        m_tooltip->setText(QString("%1\n Y: %2 ").arg(series->name()).arg(point.y()));
        m_tooltip->setAnchor(point);
        m_tooltip->setZValue(11);
        m_tooltip->updateGeometry();
        m_tooltip->show();
    } else {
        m_tooltip->hide();
    }
#endif
}
