#include "qadsplot.h"

#include <QMenu>
#include <QAction>

#define NUM_OF_G  8

class QAdsPlot::ads_implementation_t
{
public:
  QAdsPlot* master;

  bool realtime_mode;
  bool show_scales_labels;

  double time_range;
  double time_last;
  bool acq_started;
  double last_update;

  ads_implementation_t()
  {
    realtime_mode=false;
    time_range=10; //seconds
    time_last=0;
    acq_started=false;
  }
  QVector<QCPGraph*> g;
  QVector<QCPGraph*> gf;

  QCPItemStraightLine* cursors[2];

  void clearData()
  {
    for(int i=0; i<g.size(); ++i)
    {
      g[i]->clearData();
      g[i]->setVisible(true);
      gf[i]->clearData();
      gf[i]->setVisible(false);
      //g[i]->rescaleValueAxis();
      g[i]->rescaleAxes();
      gf[i]->rescaleAxes();
    }
  }
  QCPGraph* findPfx(const QCPGraph* p)
  {
    for(int i=0; i<g.size(); ++i)
    {
      if (g[i]==p)
        return gf[i];
    }
    return 0;
  }
};

QAdsPlot::QAdsPlot(QWidget *parent) :
  QFastCustomPlot(parent),
  p_(*new ads_implementation_t)
{
  initialize(0, parent);
}

QAdsPlot::QAdsPlot(QAdsPlot &master, QWidget *parent) :
  QFastCustomPlot(parent),
  p_(*new ads_implementation_t)
{
  initialize(&master, parent);
}

void QAdsPlot::initialize(QAdsPlot *master, QWidget */*parent*/)
{
  p_.master=master;

  p_.realtime_mode=false;
  p_.show_scales_labels=false;

  customPlot=this;
  customPlot->setAntialiasedElement(QCP::aeAll, false);
  customPlot->setNotAntialiasedElements(QCP::aeAll);
  QFont font;
  font.setStyleStrategy(QFont::NoAntialias);
  customPlot->xAxis->setTickLabelFont(font);
  customPlot->yAxis->setTickLabelFont(font);
  customPlot->yAxis2->setTickLabelFont(font);
  customPlot->legend->setFont(font);
  customPlot->setPlottingHint(QCP::phForceRepaint, false);
  //customPlot->setPlottingHint(QCP::phFastPolylines, true);
  {
    const QColor colors[]={ Qt::blue, Qt::red, Qt::green, Qt::magenta};
    QCPAxis* a[]={customPlot->yAxis, customPlot->yAxis2};

    for(int i=0; i<NUM_OF_G; ++i)
    {
      int ai=i % ((sizeof(a))/sizeof(a[0]));
      int ci=i % ((sizeof(colors))/sizeof(colors[0]));

      p_.g.append(customPlot->addGraph(0, a[ai])); // blue line
      p_.g[i]->setPen(QPen(colors[ci]));
      p_.g[i]->setLineStyle(QCPGraph::lsStepRight);
      p_.g[i]->setAntialiased(false);
    }
    for(int i=0; i<NUM_OF_G; ++i)
    {
      int ai=i % ((sizeof(a))/sizeof(a[0]));
      int ci=i % ((sizeof(colors))/sizeof(colors[0]));

      p_.gf.append(customPlot->addGraph(0, a[ai])); // blue line
      QPen fPen(colors[ci]);
      fPen.setStyle(Qt::DashLine);
      p_.gf[i]->setPen(fPen);
      p_.gf[i]->setSelectable(false);
      //p_.g[i]->setLineStyle(QCPGraph::lsStepRight);

      //customPlot->graph(0)->setPen(QPen(Qt::blue));
      //customPlot->graph(0)->setLineStyle(QCPGraph::lsStepRight);
      //customPlot->addGraph(0, customPlot->yAxis2); // red line
      //customPlot->graph(1)->setPen(QPen(Qt::red));
      //customPlot->graph(1)->setLineStyle(QCPGraph::lsStepRight);
    }
  }

  for(int i=0; i<2; ++i)
  {
    p_.cursors[i]=new QCPItemStraightLine(customPlot);
    //p_.cursors[1]=new QCPItemLine(customPlot);
    p_.cursors[i]->point1->setCoords(0, 10);
    p_.cursors[i]->point2->setCoords(0, -10);
    p_.cursors[i]->setSelectable(true);
  }
  p_.cursors[0]->setPen(QPen(Qt::DashLine));
  p_.cursors[1]->setPen(QPen(Qt::DashDotLine));

  customPlot->xAxis->setTickLabelType(QCPAxis::ltNumber);
  customPlot->xAxis->setAutoTickStep(true);
  customPlot->xAxis->setAutoSubTicks(true);
  //customPlot->xAxis->setTickStep(2);

  if (p_.master)
  {
    customPlot->xAxis->setTickLabels(false);
  }

  customPlot->setContextMenuPolicy(Qt::CustomContextMenu);
  connect(customPlot, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequest(QPoint)));

  connect(customPlot, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(cpMouseWheel(QWheelEvent*)));

  connect(customPlot, SIGNAL(axisDoubleClick(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*)), this, SLOT(cpAxisDoubleClick(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*)));

  connect(customPlot, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(cpMousePress(QMouseEvent*)));
}

void QAdsPlot::clearData()
{
  p_.clearData();
}

bool QAdsPlot::showScalesLabels() const
{
  return p_.show_scales_labels;
}

void QAdsPlot::setShowScalesLabels(bool show)
{
  p_.show_scales_labels=show;
  customPlot->yAxis->setTickLabels(show);
  customPlot->yAxis2->setTickLabels(show);
}

bool QAdsPlot::realtimeMode() const
{
  return p_.realtime_mode;
}

void QAdsPlot::setRealtimeMode(bool enable)
{
  //if (p_.realtime_mode==enable)
  //  return;

  if (!p_.realtime_mode)
  {
    //customPlot->graph(0)->clearData();
    //customPlot->graph(1)->clearData();
    p_.clearData();

    p_.acq_started=false;
  }
  this->setFastRealTimeMode(enable);

  p_.realtime_mode=enable;
  bool fast_update=!enable;

  customPlot->xAxis->setGrid(fast_update);
  customPlot->xAxis->setTickLabels(fast_update); // && (p_.master==0));
  customPlot->xAxis->setTicks(fast_update);

  customPlot->yAxis->setTicks(fast_update);
  customPlot->yAxis->setGrid(fast_update);
  customPlot->yAxis->setTickLabels(fast_update && p_.show_scales_labels);
  customPlot->yAxis2->setTicks(fast_update);
  customPlot->yAxis2->setGrid(fast_update);
  customPlot->yAxis2->setTickLabels(fast_update && p_.show_scales_labels);

  customPlot->yAxis2->setVisible(true);
  customPlot->yAxis->setVisible(true);
  customPlot->xAxis2->setVisible(true);
  customPlot->xAxis->setVisible(true);

  customPlot->xAxis2->setGrid(false);
  customPlot->xAxis2->setTickLabels(false);
  customPlot->xAxis2->setTicks(false);

  p_.cursors[0]->setVisible(!enable);
  p_.cursors[1]->setVisible(!enable);

#if 1
  customPlot->blockSignals(enable);
  customPlot->xAxis->blockSignals(enable);
  customPlot->yAxis->blockSignals(enable);
  customPlot->yAxis2->blockSignals(enable);
#endif
  if (enable)
  {

    customPlot->setInteractions(0);//QCustomPlot::iRangeDrag | QCustomPlot::iRangeZoom);
    //customPlot->setRangeDrag(Qt::Horizontal);
    //customPlot->setRangeZoom(Qt::Horizontal);
  }
  else
  {
    customPlot->setInteractions(
                  QCustomPlot::iRangeDrag | QCustomPlot::iRangeZoom | QCustomPlot::iSelectAxes | QCustomPlot::iMultiSelect |
                  QCustomPlot::iSelectLegend | QCustomPlot::iSelectPlottables | QCustomPlot::iSelectTitle | QCustomPlot::iSelectItems);
    customPlot->setRangeDrag(Qt::Horizontal|Qt::Vertical);
    customPlot->setRangeZoom(Qt::Horizontal|Qt::Vertical);
  }
  customPlot->replot();
}

void QAdsPlot::cpMousePress(QMouseEvent* e)
{
  if (p_.realtime_mode)
    return;

  double x=customPlot->xAxis->pixelToCoord(e->pos().x());
  int i= (e->button() & Qt::RightButton) ? 1 : 0;
  setCursor(i, x);
}

void QAdsPlot::setCursor(int c, double x)
{
  if (x!=p_.cursors[c]->point1->coords().x())
  {
    p_.cursors[c]->point1->setCoords(x,10);
    p_.cursors[c]->point2->setCoords(x,-10);
    customPlot->replot();
    emit cursorMoved(c, x);
  }
}

void QAdsPlot::cpMouseWheel(QWheelEvent* /*e*/)
{
  if (p_.realtime_mode)
    return;

  Qt::Orientations o=0;
  QCPAxis* y=customPlot->yAxis;

  if (customPlot->yAxis2->selected().testFlag(QCPAxis::spAxis))
  {
    o|=customPlot->yAxis2->orientation();
    y=customPlot->yAxis2;
  }
  else if (customPlot->yAxis->selected().testFlag(QCPAxis::spAxis))
  {
    o|=customPlot->yAxis->orientation();
    y=customPlot->yAxis;
  }

  if (customPlot->xAxis->selected().testFlag(QCPAxis::spAxis))
  {
    o|=customPlot->xAxis->orientation();
  }

  customPlot->setRangeZoomAxes(customPlot->xAxis, y);
  customPlot->setRangeZoom(o);
}

void QAdsPlot::adsAddData(int plot, double key, double value)
{
  if (!p_.acq_started)
  {
    p_.acq_started=true;
    p_.time_last=key;
    //return;
  }
  if (plot>=p_.g.size())
    return;

  key=key-p_.time_last;
  p_.g[plot]->addData(key, value);
  if (!p_.realtime_mode)
    return;

  if (key-p_.time_last > p_.time_range)
  {
    p_.g[plot]->removeDataBefore(key-p_.time_range);
  }
  p_.g[plot]->rescaleValueAxis(true);
  if (key-p_.last_update > 0.25)
  {
    p_.last_update=key;
    //customPlot->graph(0)->rescaleValueAxis(true);
    //customPlot->graph(1)->rescaleValueAxis(true);
    customPlot->xAxis->setRange(key+0.25, p_.time_range, Qt::AlignRight);
    customPlot->replot();
  }
}

void QAdsPlot::contextMenuRequest(QPoint pos)
{
  QMenu *menu = new QMenu(this);
  menu->setAttribute(Qt::WA_DeleteOnClose);

  QList<QCPGraph*> g=customPlot->selectedGraphs();
  if (g.size()==1)
  {
    QAction* a0=menu->addAction("Add derivate"); //, this, SLOT(addRandomGraph()));
    QAction* a1=menu->addAction("Remove derivate");
    a1->setEnabled(false);
    QCPGraph* fx=p_.findPfx(g[0]);
    if (fx && fx->visible())
    {
      a1->setEnabled(true);
    }
    QAction* x=menu->exec(customPlot->mapToGlobal(pos));
    if (x==a0)
    {
      QCPGraph* fx=p_.findPfx(g[0]);
      if (!fx)
        return;
      fx->clearData();
      const QCPDataMap& d0=*g[0]->data();
      QVector<double> k;
      QVector<double> v;
      QCPDataMap::const_iterator i=d0.constBegin();
      if (i==d0.end())
        return;
      double previous=i.value().value;
      for(++i; i!=d0.end(); ++i)
      {
        k.append(i.key());
        double x=i.value().value-previous;
        previous=i.value().value;
        v.append(x);
      }
      fx->setData(k, v);
      fx->rescaleValueAxis(true);

      fx->setVisible(true);
      QPen pen(Qt::DashLine);
      pen.setColor(g[0]->pen().color());
      fx->setPen(pen);
      customPlot->replot();
    }
    else if (x==a1)
    {
      fx->clearData();
      fx->setVisible(false);
      customPlot->replot();
    }
  }
  else if (g.size()==2)
  {
    QAction* a0=menu->addAction("Add Delta+"); //, this, SLOT(addRandomGraph()));
    QAction* a1=menu->addAction("Add Delta-"); //, this, SLOT(addRandomGraph()));
    QAction* a2=menu->addAction("Remove delta");

    a2->setEnabled(false);
    QCPGraph* fx=p_.findPfx(g[0]);
    if (fx && fx->visible())
    {
      a2->setEnabled(true);
    }

    QAction* m=menu->exec(customPlot->mapToGlobal(pos));

    if (!fx)
      return;
    if (m==a2)
    {
      fx->clearData();
      fx->setVisible(false);
      customPlot->replot();
    }
    else if (m==a0 || m==a1)
    {
      fx->clearData();
      const QCPDataMap& d0=*g[0]->data();
      const QCPDataMap& d1=*g[1]->data();
      QVector<double> k;
      QVector<double> v;
      QCPDataMap::const_iterator i0=d0.constBegin();
      QCPDataMap::const_iterator i1=d1.constBegin();
      if (i0==d0.end())
        return;
      for(; i0!=d0.end() && i1!=d1.end() ; ++i0, ++i1)
      {
        k.append(i0.key());
        double x=(m==a0) ? (i0.value().value-i1.value().value) : (i1.value().value-i0.value().value);
        v.append(x);
      }
      fx->setData(k, v);
      fx->rescaleValueAxis(true);

      fx->setVisible(true);
      customPlot->replot();
    }
  }
}

void QAdsPlot::cpAxisDoubleClick(QCPAxis* a,QCPAxis::SelectablePart,QMouseEvent*)
{
  if (a==customPlot->xAxis)
  {
    customPlot->rescaleAxes();
  }
}



//The Widget
#include <QVBoxLayout>
#include <QGridLayout>
#include <QCheckBox>
#include <QToolButton>
#include <QSpacerItem>
#include <QDoubleSpinBox>
#include <QComboBox>
#include <QTreeView>
#include <QStandardItemModel>
#include <QStandardItemModel>

#include <QLabel>
#include <float.h>


struct Channel {
  int plot;
  int graph;
  double v;

  QColor color;

  QCheckBox* wCheck;
  QCheckBox* wPane;
  QToolButton* wSelect;
  QLabel* wName;
  QDoubleSpinBox* wValue;
  void* userData;
};

class QAdsPlotWidget::implementation_t
{
public:

  QVBoxLayout* mainLy;
  QVector<QAdsPlot*> plots;
  QVector<Channel*> channels;

  QGridLayout* gridLy;
  bool real_time;

  double time_range;
  double time_last;
  double last_key;
  bool acq_started;
  double last_update;
};

QAdsPlotWidget::QAdsPlotWidget(QWidget */*parent*/):
  p_(*new QAdsPlotWidget::implementation_t)
{
  QColor dc[]={Qt::cyan, Qt::blue, Qt::red, Qt::darkBlue, Qt::darkRed, Qt::darkGreen, Qt::black, Qt::darkGray};

  p_.plots.append(new QAdsPlot(this));
  p_.plots.append(new QAdsPlot(*p_.plots[0], this));

  p_.mainLy=new QVBoxLayout(this);

  p_.gridLy=new QGridLayout;
  p_.mainLy->addLayout(p_.gridLy);
  int gp=0;
  for(int i=0; i<8; ++i)
  {
    int row=i % 4;
    int colum=i/4;

    gp=colum*5;
    Channel* c=new Channel;
    p_.channels.append(c);
    c->color=dc[i % ((sizeof(dc))/sizeof(dc[0]))];
    c->plot=0; //i>>2;
    c->graph=i; // % 4;
    c->wCheck=new QCheckBox(QString("%1:").arg(i));
    c->wCheck->setLayoutDirection(Qt::RightToLeft);
    c->wCheck->setTristate(true);
    c->wCheck->setCheckState(Qt::Checked);
    connect(c->wCheck, SIGNAL(stateChanged(int)), SLOT(dcbStateChange(int)));
    p_.gridLy->addWidget(c->wCheck, row, gp++);

    c->wPane=new QCheckBox(",P2:");
    c->wPane->setLayoutDirection(Qt::RightToLeft);
    connect(c->wPane, SIGNAL(stateChanged(int)), SLOT(dcbPaneChange(int)));
    p_.gridLy->addWidget(c->wPane, row, gp++);

    c->wSelect=new QToolButton;
    c->wSelect->setText(">");
    p_.gridLy->addWidget(c->wSelect, row, gp++);

    c->wName=new QLabel("<none>");
    c->wName->setStyleSheet(QString("color: rgb(%1,%2,%3)").arg(c->color.red()).arg(c->color.green()).arg(c->color.blue()));
    p_.gridLy->addWidget(c->wName, row, gp++);

    c->wValue=new QDoubleSpinBox;
    c->wValue->setSpecialValueText("?????");
    c->wValue->setMaximum(DBL_MAX);
    c->wValue->setMinimum(-DBL_MAX);
    c->wValue->setValue(-DBL_MAX);
    c->wValue->setReadOnly(true);
    c->wValue->setDecimals(4);
    c->wValue->setButtonSymbols(QAbstractSpinBox::NoButtons);
    c->userData=0;
    p_.gridLy->addWidget(c->wValue, row, gp++);

    //p_.plots[c->plot]->graph(c->graph)->setPen(c->color);
    //p_.plots[c->plot]->graph(c->graph)->setValueAxis(p_.plots[c->plot]->yAxis);
    p_.plots[0]->graph(c->graph)->setPen(c->color);
    p_.plots[0]->graph(c->graph)->setValueAxis(p_.plots[0]->yAxis);
    p_.plots[1]->graph(c->graph)->setPen(c->color);
    p_.plots[1]->graph(c->graph)->setValueAxis(p_.plots[1]->yAxis);

    p_.plots[0]->graph(c->graph)->setVisible(true);
    p_.plots[1]->graph(c->graph)->setVisible(false);
  }
  p_.gridLy->addItem(new QSpacerItem(4, 2, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, gp, 1, 1);
  //p_.gridLy->addItem(new QSpacerItem(1,1), 0, --gp);
  p_.gridLy->setMargin(2);
  p_.gridLy->setSpacing(4);

  p_.mainLy->addWidget(p_.plots[1]);
  p_.mainLy->addWidget(p_.plots[0]);
  p_.mainLy->setContentsMargins(2, 2, 2, 2);
  p_.mainLy->setSpacing(1);
  for(int i=0; i<2; ++i)
  {
    connect(p_.plots[i]->xAxis, SIGNAL(rangeChanged(QCPRange)), SLOT(xAxisRangeChanged(QCPRange)));
    connect(p_.plots[i]->yAxis, SIGNAL(rangeChanged(QCPRange)), SLOT(yAxisRangeChanged(QCPRange)));
    connect(p_.plots[i]->yAxis2, SIGNAL(rangeChanged(QCPRange)), SLOT(yAxisRangeChanged(QCPRange)));
    connect(p_.plots[i], SIGNAL(cursorMoved(int,double)), SLOT(setCursor(int,double)));
    connect(p_.plots[i], SIGNAL(mouseMove(QMouseEvent*)), SLOT(cpMouseMove(QMouseEvent*)));
    //p_.plots[i]->xAxis->setNumberFormat("gb");
    p_.plots[i]->yAxis->setNumberFormat("gb");
    p_.plots[i]->yAxis2->setNumberFormat("gb");
    p_.plots[i]->yAxis->setNumberPrecision(4);
    p_.plots[i]->yAxis2->setNumberPrecision(4);
    p_.plots[i]->setAutoMargin(false);
    p_.plots[i]->setShowScalesLabels(false);
    p_.plots[i]->setMargin(8, 8, 4, p_.plots[i]->marginBottom());
  }
  p_.plots[1]->setVisible(false);

  p_.real_time=false;
  p_.acq_started=false;
  p_.time_range=10; //seconds
}

QToolButton* QAdsPlotWidget::channelSelctor(int channel)
{
  return p_.channels[channel]->wSelect;
}

void QAdsPlotWidget::setSelectionModel(QStandardItemModel* /*m*/)
{
}

void QAdsPlotWidget::setChannelName(int channel, const QString &name, void *usrdata)
{
  p_.channels[channel]->wName->setText(name);
  p_.channels[channel]->userData=usrdata;
}

void* QAdsPlotWidget::channelUserData(int channel) const
{
  return  p_.channels[channel]->userData;
}

void QAdsPlotWidget::setChannelEnabled(int channel, bool enabled)
{
  p_.channels[channel]->wCheck->setEnabled(enabled);
}

void QAdsPlotWidget::dcbStateChange(int n)
{
  QObject* s=sender();
  foreach(Channel* c, p_.channels)
  {
    if (s==c->wCheck)
    {
      p_.plots[c->plot]->graph(c->graph)->setVisible(n!=Qt::Unchecked);
      p_.plots[c->plot]->graph(c->graph)->setValueAxis(n==Qt::PartiallyChecked ? p_.plots[c->plot]->yAxis2 : p_.plots[c->plot]->yAxis);
      /*
      p_.plots[c->plot]->graph(c->graph)->rescaleAxes();
      foreach(Channel* cc, p_.channels)
        if (cc!=c && cc->plot==c->plot)
          p_.plots[cc->plot]->graph(cc->graph)->rescaleAxes(true);
      */
      p_.plots[c->plot]->rescaleAxes();
      p_.plots[c->plot]->replot();
      break;
    }
  }
}

void QAdsPlotWidget::dcbPaneChange(int n)
{
  QObject* s=sender();
  foreach(Channel* c, p_.channels)
  {
    if (s==c->wPane)
    {
      int old_pane=c->plot;
      int new_pane=n!=Qt::Unchecked ? 1 : 0;
      if (new_pane==old_pane)
        break;
      QCPDataMap* d=const_cast<QCPDataMap*>(p_.plots[c->plot]->graph(c->graph)->data());
      p_.plots[new_pane]->graph(c->graph)->setData(d, true);
      p_.plots[new_pane]->graph(c->graph)->setVisible(true);
      p_.plots[c->plot]->graph(c->graph)->clearData();
      p_.plots[c->plot]->graph(c->graph)->setVisible(false);
      c->plot=new_pane;
      p_.plots[new_pane]->graph(c->graph)->rescaleValueAxis();
      //p_.plots[old_pane]->graph(c->graph)->rescaleValueAxis();
      unsigned int panes_used=1<<new_pane;
      foreach(Channel* cc, p_.channels)
      {
        if (panes_used & (1<<cc->plot))
        //if (cc!=c)
          p_.plots[cc->plot]->graph(cc->graph)->rescaleValueAxis(true);
        else
          p_.plots[cc->plot]->graph(cc->graph)->rescaleValueAxis();
        panes_used|=1<<cc->plot;
      }
      if (panes_used & 1)
      {
        p_.plots[0]->setVisible(true);
      }
      else
        p_.plots[0]->setVisible(false);
      if (panes_used & 2)
      {
        p_.plots[1]->setVisible(true);
      }
      else
        p_.plots[1]->setVisible(false);

      //p_.plots[0]->rescaleAxes();
      //p_.plots[1]->rescaleAxes();

      p_.plots[0]->replot();
      p_.plots[1]->replot();

      break;
    }
  }
}

QAdsPlot& QAdsPlotWidget::plot(int n)
{
  return *p_.plots[n];
}

void QAdsPlotWidget::clear()
{
  foreach(QAdsPlot* p, p_.plots)
  {
    p->clearData();
  }
}

void QAdsPlotWidget::rescaleAll()
{
  foreach(QAdsPlot* p, p_.plots)
  {
    if (p->isVisible())
    {
      p->rescaleAxes();
      break;
    }
  }
}

void QAdsPlotWidget::setRealtimeMode(bool enable)
{
  foreach(QAdsPlot* p, p_.plots)
  {
    p->setRealtimeMode(enable);
    //p->rescaleAxes();
  }
  if (enable==false)
  {
    foreach(QAdsPlot* p, p_.plots)
    {
      p->rescaleAxes();
      p->replot();
      //break;
    }
  }
  p_.acq_started=false;
  p_.real_time=enable;
}

void QAdsPlotWidget::sync(QCPAxis* axis)
{
  if (!axis)
    return;
  QCPRange r=axis->range();
  foreach(QAdsPlot* p, p_.plots)
  {
    if (p->xAxis==axis)
      continue;
    bool blocked=p->xAxis->signalsBlocked();
    p->xAxis->blockSignals(true);
    p->xAxis->setRange(r);
    p->xAxis->blockSignals(blocked);
    p->replot();
  }
}

void QAdsPlotWidget::addData(int channel, double key, double value)
{
  if (p_.real_time)
  {
    if (!p_.acq_started)
    {
      p_.acq_started=true;
      p_.time_last=key;
      p_.last_update=0;
    }
    key=key-p_.time_last;
  }

  Channel& c=*p_.channels[channel];
  //p_.plots[c.plot]->adsAddData(c.graph, key, value);
  QCPGraph* g=p_.plots[c.plot]->graph(c.graph);
  if (!g)
    return;
  g->addData(key, value);

  p_.last_key=key;

  if (!p_.real_time)
    return;

  //double delta=key-p_.time_last;
  if (key > p_.time_range)
  {
    g->removeDataBefore(key-p_.time_range);
  }
  g->rescaleValueAxis();

  p_.last_update=key;

  return;

  //if (key-p_.last_update > 0.25)
  {
    p_.last_update=key;
    //customPlot->graph(0)->rescaleValueAxis(true);
    //customPlot->graph(1)->rescaleValueAxis(true);
    p_.plots[c.plot]->xAxis->setRange(key/*+0.25*/, p_.time_range, Qt::AlignRight); //Qt::AlignRight);
    p_.plots[c.plot]->replot();
    int other_plot=c.plot==0 ? 1 : 0;
    p_.plots[other_plot]->xAxis->setRange(key/*+0.25*/, p_.time_range, Qt::AlignLeft); //Qt::AlignRight);
    p_.plots[other_plot]->replot();

  }
}

void QAdsPlotWidget::updateData()
{
  double key=p_.last_key;

  for(int i=0; i<2; ++i)
  {
    QAdsPlot* g=p_.plots[i];
    g->xAxis->setRange(key/*+0.25*/, p_.time_range, Qt::AlignRight); //Qt::AlignRight);
    g->replot();
  }
}

void QAdsPlotWidget::setCursor(int c, double x)
{
  QAdsPlot* s=qobject_cast<QAdsPlot*>(sender());
  foreach(QAdsPlot* p, p_.plots)
  {

    if (p==s)
      continue;
    p->setCursor(c, x);
  }
}

void QAdsPlotWidget::cpMouseMove(QMouseEvent* e)
{
  QCustomPlot* cp=qobject_cast<QCustomPlot*>(sender());
  if (!cp)
    return;

  double x=cp->xAxis->pixelToCoord(e->pos().x());
  foreach(Channel* c, p_.channels)
  {
    double y=DBL_MIN;
    QCPGraph* g=p_.plots[c->plot]->graph(c->graph);
    if (!g)
      continue;
    const QCPDataMap* d=g->data();
    if (!d)
      continue;
    //double px=0;
    for(QCPDataMap::const_iterator i=d->constBegin(); i!=d->end(); ++i)
    {
      double k=i.value().key;
      if (k>=x)
      {
        y=i.value().value;
        break;
      }
      //px=k;
    }
    c->wValue->setValue(y);
  }
}

void QAdsPlotWidget::xAxisRangeChanged(QCPRange)
{
  if (p_.real_time)
    return;

  QCPAxis* axis=qobject_cast<QCPAxis*>(sender());
  if (axis)
    sync(axis);
}

void QAdsPlotWidget::yAxisRangeChanged(QCPRange)
{
  if (p_.real_time)
    return;
}

//
#include <QTreeWidget>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QModelIndex>

QTreeInputDialog::QTreeInputDialog(const QString &name, QWidget *parent):
  QDialog(parent)
{
  parent_selectable=false;
  this->setWindowTitle(name);
  this->w=new QTreeView;
  w->setHeaderHidden(true);
  w->setEditTriggers(QAbstractItemView::NoEditTriggers);
  this->setLayout(new QVBoxLayout);
  this->layout()->addWidget(w);
  this->bbox=new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
  this->layout()->addWidget(bbox);
  bbox->button(QDialogButtonBox::Ok)->setEnabled(false);

  connect(w, SIGNAL(clicked(QModelIndex)), SLOT(itemClicked()));
  connect(w, SIGNAL(doubleClicked(QModelIndex)), SLOT(itemDoubleClicked()));
  connect(bbox, SIGNAL(accepted()), SLOT(accept()));
  connect(bbox, SIGNAL(rejected()), SLOT(reject()));
}

void QTreeInputDialog::setParentSelectable(bool enable)
{
  parent_selectable=enable;
}

void QTreeInputDialog::itemClicked()
{
  QModelIndex i=w->currentIndex();
  QModelIndex ix=w->model()->index(0, 0, i);
  //if (i.child(0, 0).isValid() && !parent_selectable)
  if (ix.isValid() && !parent_selectable)
    bbox->button(QDialogButtonBox::Ok)->setEnabled(false);
  else
    bbox->button(QDialogButtonBox::Ok)->setEnabled(true);
}

void QTreeInputDialog::itemDoubleClicked()
{
  QModelIndex i=w->currentIndex();
  QModelIndex ix=w->model()->index(0, 0, i);
  //if (i.index(0, 0).isValid() && !parent_selectable)
  if (ix.isValid() && !parent_selectable)
    return;
  else
    accept();
}

void QTreeInputDialog::setModel(QStandardItemModel* model)
{
  this->w->setModel(model);
}

//
void QFastCustomPlot::paintEvent(QPaintEvent *event)
{
  Q_UNUSED(event);
  if (!mFastRealTimeMode)
  {
    QPainter painter(this);
    painter.drawPixmap(0, 0, mPaintBuffer);
    return;
  }
  replotUnderEvent();
}

void QFastCustomPlot::fastDraw(QCPPainter *painter)
{
  QCPLayer* ly=this->layer("main");
  QList<QCPLayerable*> layerChildren = ly->children();
  for (int k=0; k < layerChildren.size(); ++k)
  {
    QCPLayerable *child = layerChildren.at(k);
    if (child->visible())
    {
      painter->save();
      painter->setClipRect(child->clipRect().translated(0, -1));
      child->applyDefaultAntialiasingHint(painter);
      child->draw(painter);
      painter->restore();
    }
  }

}

void QFastCustomPlot::replotUnderEvent()
{
  QCPPainter painter;
  painter.begin(this);
  painter.setRenderHints(QPainter::HighQualityAntialiasing|QPainter::TextAntialiasing|QPainter::Antialiasing|QPainter::SmoothPixmapTransform, false);

  painter.fillRect(this->rect(), Qt::white); //mBackgroundBrush.color());

  //if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
  //  painter.fillRect(mViewport, mBackgroundBrush);
  fastDraw(&painter);
  painter.end();

}

void QFastCustomPlot::replot()
{
  if (!mFastRealTimeMode)
  {
    QCustomPlot::replot();
    return;
  }

  //if (mReplotting) // incase signals loop back to replot slot
  //  return;
  //mReplotting = true;

  update();
  //mReplotting = false;

#if 0
  //emit beforeReplot();

  mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
  QCPPainter painter;
  painter.begin(&mPaintBuffer);
  if (painter.isActive())
  {
    //painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
    painter.setRenderHints(QPainter::HighQualityAntialiasing|QPainter::TextAntialiasing|QPainter::Antialiasing|QPainter::SmoothPixmapTransform, false);

    if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
      painter.fillRect(mViewport, mBackgroundBrush);
    draw(&painter);
    painter.end();
    if (mPlottingHints.testFlag(QCP::phForceRepaint))
      repaint();
    else
      update();
  } else // might happen if QCustomPlot has width or height zero
    qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer";

  //emit afterReplot();

  mReplotting = false;
#endif
}
