#include "xlruchartclass.h"

#include <QHBoxLayout>
#include <QFont>
#include <QMetaEnum>
#include <QApplication>
//#include <QMetaEnum>


using namespace QtCharts;


XlruChartClass::XlruChartClass(QWidget *parent) :
    QWidget(parent),

    report(0)
#ifdef TELEMETRY
    ,telemetry(0)
#endif
{
    init();

    SensorsTable = NULL;
}

XlruChartClass::~XlruChartClass()
{
    delete mainView;
    //delete report;
}

void XlruChartClass::setTitle(QString aTitle)
{
    title = aTitle;
}

QString XlruChartClass::getTitle()
{
    return title;
}


void XlruChartClass::init()
{
    ch = new QChart();
    mainView = new QChartView();

    ch=mainView->chart();

    m_tooltip = 0;

    mainView->setRubberBand( QChartView::HorizontalRubberBand );
    ch->setMargins(QMargins(0, 0, 0, 0));
    ch->setAnimationOptions(QChart::NoAnimation);
    ch->setMinimumSize(CHART_DIMX_DEF, CHART_DIMY_DEF);
    ch->setAcceptHoverEvents(true);
    ch->setTitle(title);

    QHBoxLayout *layout = new QHBoxLayout();

#ifdef TRACKING_VALUE
    valueToScreen = new QLabel(this);
    valueToScreen->setText("prova");
    layout->addWidget(valueToScreen);
#endif
    layout->addWidget(mainView);
    setLayout(layout);

    QFont font;
    // Customize chart title
    font.setPixelSize(FONT_DIM_DEF);

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

    timeStart=timeNow;
    timeStartMs=timeNowMs;

    commonX= new QDateTimeAxis;
    commonX->setFormat("h:mm:ss");
    commonX->setTickCount(0);
    commonX->setGridLineVisible(false);
    commonX->setMin(timeNow.addSecs(-60));
    if (UPDATE_OPTIMIZED)
        commonX->setMax(timeNow.addSecs(TIME_UPDATE_XMAX));
    else
        commonX->setMax(timeNow);

    temperatureY=new QValueAxis;
    temperatureY->setGridLineVisible(false);
    temperatureY->setVisible(true);
    temperatureY->setTitleText("Temp(C)");
    temperatureY->setTitleFont(font);
    temperatureY->setTitleVisible(true);
    temperatureY->setRange(TEMP_DEF_MIN, TEMP_DEF_MAX);

    currentY=new QValueAxis;
    currentY->setGridLineVisible(false);
    currentY->setVisible(true);
    currentY->setTitleText("Curr(A)");
    currentY->setTitleFont(font);
    currentY->setTitleVisible(true);
    currentY->setRange(CURR_DEF_MIN, CURR_DEF_MAX);

    voltageY=new QValueAxis;
    voltageY->setGridLineVisible(false);
    voltageY->setVisible(true);
    voltageY->setTitleText("Volt(V)");
    voltageY->setTitleFont(font);
    voltageY->setTitleVisible(true);
    voltageY->setRange(VOLT_DEF_MIN, VOLT_DEF_MAX);

    ch->addAxis(commonX, AXIS_ALIGNMENT_DATE);

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

    mainView->chart()->setBackgroundRoundness(0);
    mainView->chart()->setMargins(QMargins(0, 0, 0, 0));
    mainView->chart()->legend()->setVisible(true);
    mainView->chart()->legend()->setAlignment(Qt::AlignTop);

    autopurge_enabled = Qt::Unchecked;

    setPenDim(PEN_DIM_DEF);

    report_enabled = REPORT_ENABLED_DEF;

}

int XlruChartClass::findSensor(QString _label)
{
    int idx = 0;
    foreach (Sensor *s, list_sensors)
    {
        if (s->label == _label)
           return idx;

        idx++;
    }
    return -1;
}

int XlruChartClass::findSensorName(QString _name)
{
    int idx = 0;
    foreach (Sensor *s, list_sensors)
    {
        if (s->name == _name)
           return idx;

        idx++;
    }
    return -1;
}



int XlruChartClass::addSensorFromConfig(sensor_config_t *sensor_config)
{
    //int count_sensor = list_sensors.count();
    int idx = findSensorName(sensor_config->name);

    if (idx != -1)
    {
        //Already exist!!
        return -1;
    }

    if ((report_enabled) && (report!=0))
           report->addSensorCol(sensor_config->label_sensor);

    Sensor *newSensor = new Sensor();
    QLineSeries* s;

    newSensor->view=mainView;
    newSensor->name = sensor_config->name;
    newSensor->label = sensor_config->label_sensor;
    newSensor->type_serie = sensor_config->type;
    newSensor->limit = sensor_config->limit;
    newSensor->limitFlag = true;
    newSensor->fpga_ch = sensor_config->fpga_ch;

    switch (newSensor->type_serie)
    {
    case type_s_temperature:
        newSensor->axis=temperatureY;
        if (ch->axes().indexOf(temperatureY, 0)==-1)
            ch->addAxis(temperatureY, AXIS_ALIGNMENT_TEMP);
        s=addSeries(sensor_config->label_sensor, temperatureY);
        break;
    case type_s_current:
        newSensor->axis=currentY;
        if (ch->axes().indexOf(currentY, 0)==-1)
            ch->addAxis(currentY, AXIS_ALIGNMENT_CURR);
        s=addSeries(sensor_config->label_sensor, currentY);
        break;
    case type_s_voltage:
        newSensor->axis=voltageY;
        if (ch->axes().indexOf(voltageY, 0)==-1)
            ch->addAxis(voltageY, AXIS_ALIGNMENT_CURR);
        s=addSeries(sensor_config->label_sensor, voltageY);
        break;
    default:
        break;
    }

    QPen pen = s->pen();
    pen.setWidth(penDim);
    s->setPen(pen);
    s->setName(QString("%1(%2)").arg(sensor_config->label_sensor).arg(sensor_config->limit));

    newSensor->series=s;

    list_sensors.append(newSensor);

#ifdef USE_TABLE
    //add sensor table
    if (SensorsTable!=nullptr)
    {
        SensorsTable->addNewSensor(sensor_config->label_sensor);
    }
#endif

    return list_sensors.size();
}


QLineSeries* XlruChartClass::addSeries(QString _name, QValueAxis* _axis)
{
    QLineSeries* new_series=new QLineSeries;
    //list_series.append(new_series);
    ch->addSeries(new_series);
    ch->setAxisX(commonX, new_series);
    ch->setAxisY(_axis, new_series);
    new_series->setName(_name);
    return new_series;
}

void XlruChartClass::addSample(QString name_sensor , float value)
{
    int idx = findSensorName(name_sensor);

    if (idx == -1)
    {
        //sensor not exists
        return;
    }

    QLineSeries* s=list_sensors[idx]->series;
    QString sensor_label = list_sensors[idx]->label;
    if (s==0)
        return;
#ifdef TELEMETRY
    if (telemetry!=0)
        telemetry->start("add to serie");
#endif

    if (UPDATE_OPTIMIZED)
    {
        //controlla se manca meno di TIME_DELTA_XMAX secondi da xmax


        qint64 axisXmaxNum= commonX->max().toMSecsSinceEpoch();
        qint64 nowNum= timeNow.toMSecsSinceEpoch();

        qint64 delta = axisXmaxNum - nowNum;
        if (delta< TIME_DELTA_XMAX*1000) //10 seconds
        {
            QDateTime newmax = timeNow.addSecs(TIME_UPDATE_XMAX);
            commonX->setMax(newmax);
        }
    }

    lastSampleMs=timeNowMs;
    s->append(timeNowMs, value);

#ifdef TELEMETRY
    if (telemetry!=0)
        telemetry->stop();
#endif


    if ((report!=0) && (report_enabled))
    {
#ifdef TELEMETRY
        if (telemetry!=0)
            telemetry->start("update report");
#endif
        report->addValueCol(sensor_label , value);
#ifdef TELEMETRY
        if (telemetry!=0)
            telemetry->stop();
#endif
    }

#ifdef TRACKING_VALUE
    valueToScreen->setText(QString("%1 %2").arg(timeNowMs).arg(value));
#endif

#ifdef TELEMETRY
    if (telemetry!=0)
        telemetry->start("update axis");
#endif

    Sensor *se = list_sensors[idx];
    QtCharts::QValueAxis *a=se->axis;

    if (a!=0)
    {
        if (a->min()>value)
        {
            a->setMin(value-1.0);
        }
        else if (a->max()<value)
        {
            a->setMax(value+1.0);
        }
    }

    if ((se->limitFlag) && (value>=se->limit))
    {
        QApplication::beep();

        switch (se->type_serie)
        {
        case type_s_temperature:
            showWarning(QString("%1 -> Over-temperature (%2 celsius)").arg(s->name()).arg(value));
            break;
        case type_s_current:
            showWarning(QString("%1 -> Over-current (%2 ampere)").arg(s->name()).arg(value));
            break;
        case type_s_voltage:
            showWarning(QString("%1 -> Over-voltage (%2 volt)").arg(s->name()).arg(value));
            break;
        }
    }

#ifdef USE_TABLE
    //add sensor table
    if (SensorsTable!=nullptr)
    {
        SensorsTable->update_sensor(sensor_label, value);
    }
#endif

#ifdef TELEMETRY
    if (telemetry!=0)
        telemetry->stop();
#endif
}

void XlruChartClass::timerEvent()
{
    timeNow=QDateTime::currentDateTime();
    timeNowMs=timeNow.toMSecsSinceEpoch();

    if ((timeNowMs-lastSampleMs)>1000*10) //Dead?
    {
       //p_.tgtAlive=false;
    }
    else
    {
        if (! UPDATE_OPTIMIZED)
            commonX->setMax(timeNow);

        switch(autopurge_enabled)
        {
            default:
            case Qt::Unchecked:
                break;
            case Qt::PartiallyChecked:
                {
                    qint64 delta=timeNowMs-timeStartMs;
                    if (delta>(1000*60*60)) //1h
                    {
                        timeStart=timeNow.addMSecs(-60*PURGE_PARTIAL_DEF*1000);
                        timeStartMs=timeStart.toMSecsSinceEpoch();
                        commonX->setMin(timeStart);
                    }
                }
                break;
            case Qt::Checked:
                {
                    //if (!p_.tgtAlive)
                    {
                        timeStart=timeNow.addMSecs(-60*PURGE_DEF*1000);
                        timeStartMs=timeStart.toMSecsSinceEpoch();
                        commonX->setMin(timeStart);
                    }
                }
                break;
        }
    }
}

void XlruChartClass::setReport(bool enabled)
{
    report_enabled = enabled;
}


void XlruChartClass::connectMarkers()
{
    // Connect all markers to handler
    foreach (QLegendMarker* marker, 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 XlruChartClass::disconnectMarkers()
{
    foreach (QLegendMarker* marker, ch->legend()->markers()) {
        QObject::disconnect(marker, SIGNAL(clicked()), this, SLOT(handleMarkerClicked()));
    }
}

void XlruChartClass::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;
        }
    }
}

void XlruChartClass::connectTooltip()
{
    // Connect all series to handler
    foreach (Sensor *s, list_sensors)
    {
        connect(s->series, &QLineSeries::clicked, this, &XlruChartClass::keepCallout);
        connect(s->series, &QLineSeries::hovered, this, &XlruChartClass::tooltip);
    }
}


void XlruChartClass::keepCallout()
{
    QAbstractSeries *series = qobject_cast<QAbstractSeries *>(sender());
    m_callouts.append(m_tooltip);

    m_tooltip = new Callout(ch, series);
    m_tooltip->hide();
}

void XlruChartClass::tooltip(QPointF point, bool state)
{
    QAbstractSeries *series = qobject_cast<QAbstractSeries *>(sender());
    if (m_tooltip == 0)
    {
        m_tooltip = new Callout(ch, series);
    }
    if (state)
    {
        m_tooltip->setSeries(series);
        //QPointF pointFound = ch->mapToValue(point, series);

        qreal yVal = point.y();
        m_tooltip->setText(QString().sprintf("%s\n %0.2f", series->name().toStdString().c_str(), yVal));
        m_tooltip->setAnchor(point);
        m_tooltip->updateGeometry();
        m_tooltip->show();
    }
    else
    {
        m_tooltip->hide();
    }
}

void XlruChartClass::showWarning(QString _mess)
{
    alertBox.setText(_mess);
    alertBox.setIcon(QMessageBox::Warning);
    alertBox.setWindowTitle("WARNING");
    alertBox.setWindowFlags(Qt::WindowStaysOnTopHint);
    alertBox.setModal( false );
    alertBox.show();
    alertBox.raise();
}

void XlruChartClass::setPenDim(int _dim)
{
    penDim = _dim;
}

void XlruChartClass::purge()
{
    auto ts=timeNow.addSecs(-60*PURGE_DEF);
    timeStart=ts;
    timeStartMs=ts.toMSecsSinceEpoch();
    commonX->setMin(ts);
}

void XlruChartClass::clear()
{
    foreach (Sensor *s, list_sensors)
    {
        s->series->clear();
    }
    commonX->setMin(commonX->max().addSecs(-60));
}


void XlruChartClass::save_header_info()
{
    if ((report!=0) && (report_enabled))
    {
        report->saveHeader();
    }
}


void XlruChartClass::save_all_sample()
{
    if ((report!=0) && (report_enabled))
    {
        report->saveValue();
    }
}



#ifdef TELEMETRY
void XlruChartClass::setTelemetry(telemetryClass *_telemetry)
{
    telemetry = _telemetry;
}
#endif
