/* Distributed under the Apache License, Version 2.0.
See accompanying NOTICE file for details.*/
#include "QwtPulsePlot.h"
#include <QMutex>
#include <QVector>
#include <deque>

#include <qwt_plot.h>
#include <qwt_plot_grid.h>
#include <qwt_plot_curve.h>
#include <qwt_point_data.h>
#include <qwt_plot_canvas.h>
#include <qwt_text.h>

class QwtPulsePlot::Data
{
public:
  QwtPlot*                Plot;
  QwtPlotCurve*           Curve;
  QwtPlotGrid*            Grid;
  std::deque<double>      Times;
  std::deque<double>      Values;
  double                  MaxY;
  double                  MinY;
  size_t                  MaxSize;
  QMutex                  Mutex;
};

QwtPulsePlot::QwtPulsePlot(QWidget* parent, size_t max_points)
{
  m_Data = new QwtPulsePlot::Data();
  m_Data->MinY = std::numeric_limits<double>::max();
  m_Data->MaxY = -std::numeric_limits<double>::max();
  m_Data->MaxSize = max_points;

  m_Data->Plot = new QwtPlot(parent);
  m_Data->Plot->setMaximumHeight(parent->height());
  m_Data->Curve = new QwtPlotCurve();
  m_Data->Curve->setRenderHint(QwtPlotItem::RenderAntialiased);
  m_Data->Curve->setPen(Qt::blue, 2);
  m_Data->Curve->attach(m_Data->Plot);

  m_Data->Grid = new QwtPlotGrid();
  m_Data->Grid->enableX(false);
  m_Data->Grid->enableY(false);
  m_Data->Grid->attach(m_Data->Plot);
}

QwtPulsePlot::~QwtPulsePlot()
{
  delete m_Data->Plot;
  delete m_Data;
}

void QwtPulsePlot::Clear()
{
  QVector<QPointF> points;
  m_Data->Curve->setSamples(points);
  m_Data->Plot->replot();
  m_Data->Times.clear();
  m_Data->Values.clear();
}

QwtPlot& QwtPulsePlot::GetPlot() { return *m_Data->Plot; }
QwtPlotCurve& QwtPulsePlot::GetCurve() { return *m_Data->Curve; }
QwtPlotGrid& QwtPulsePlot::GetGrid() { return *m_Data->Grid; }


void QwtPulsePlot::SetDataRange(double min, double max)
{
  m_Data->MinY = min;
  m_Data->MaxY = max;
  m_Data->Plot->setAxisScale(m_Data->Plot->yLeft, min, max);
}

void QwtPulsePlot::Append(double time, double value)
{
  m_Data->Mutex.lock();
  m_Data->Times.push_back(time);
  m_Data->Values.push_back(value);

  double t = time;
  if (m_Data->Values.size() < m_Data->MaxSize)
  {
    for (size_t i = m_Data->Values.size(); i <= m_Data->MaxSize; i++)
    {
      t -= 1./50.;
      m_Data->Values.push_back(value);
      m_Data->Times.push_front(t);
    }
  }

  m_Data->Values.pop_front();
  m_Data->Times.pop_front();
  m_Data->Mutex.unlock();
}

void QwtPulsePlot::UpdateUI(bool pad)
{
  m_Data->Mutex.lock();
  size_t size = m_Data->Values.size();
  if (size > 2)
  {
    QVector<QPointF> points;
  
    double v;
  
    for (size_t i = 0; i < size; i++)
    {
      v = m_Data->Values[i];
      if (v > m_Data->MaxY)
        m_Data->MaxY = v;
      if (v < m_Data->MinY)
       m_Data->MinY = v;
      points.append(QPointF(m_Data->Times[i], v));
    }
    if (m_Data->MinY == m_Data->MaxY)
    {
      m_Data->MinY -= 0.5;
      m_Data->MaxY += 0.5;
    }
    if(pad)
      m_Data->Plot->setAxisScale(QwtPlot::yLeft, m_Data->MinY - (m_Data->MinY*0.05), m_Data->MaxY + (m_Data->MaxY*0.05));
    else
      m_Data->Plot->setAxisScale(QwtPlot::yLeft, m_Data->MinY, m_Data->MaxY);
    m_Data->Plot->setAxisScale(QwtPlot::xBottom, m_Data->Times[0], m_Data->Times[size - 1]);
    m_Data->Curve->setSamples(points);
    m_Data->Plot->setMaximumHeight(m_Data->Plot->parentWidget()->height());
    m_Data->Plot->replot();
    m_Data->Plot->setVisible(true);
  }
  m_Data->Mutex.unlock();
}

