
#include "adsplotwidget.h"

#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsItemGroup>
#include <QGraphicsTextItem>
#include <QGraphicsLineItem>

#include <QMouseEvent>

#include <QVector>

#include <math.h>


double AdsPlotSamples::valueAtOffset(double x) const
{
  if (!values.size())
    return 0;

  double r=fabs(xmax-xmin);
  double n=r/values.size();

  double pos=n<0.00001 ? 0 : x/n;
  int p=int(pos);

  //return p;

  if (p<0)
    return values[0].y();
  if (p>=values.size())
    return values[values.size()-1].y();
  return values[p].y();
}

class GraphicScale: public QGraphicsItemGroup
{
public:
  GraphicScale()
  {
    lblItem=new QGraphicsTextItem(this);
    lblItem->setVisible(false);
    unitItem=new QGraphicsTextItem(this);
    unitItem->setVisible(false);

    autoStep=true;
    min=-1000;
    max=1000;
    vx=2000;
    vy=2000;
    step=0;
    orientation=false;
  }

  void setVerticalOrientation(bool vertical=true)
  {
    orientation=vertical;
  }

  void setViewRange(int x, int y)
  {
    vx=x;
    vy=y;
  }

  void setRange(double min_, double max_)
  {
    min=min_;
    max=max_;
  }

  void setSteps(double step_)
  {
     step=step_;
     autoStep=false;
  }

  void setLabel(const QString& label)
  {
    lblItem->setPlainText(label);
  }

  void setUnitsName(const QString& unitsName)
  {
    unitItem->setPlainText(unitsName);
  }

  void clear()
  {

  }

  void recalculate()
  {
    clear();
  }

protected:
  QGraphicsTextItem* lblItem;
  QGraphicsTextItem* unitItem;

  bool orientation;
  bool autoStep;
  double min,max,step;
  int vx, vy;
};


struct ScaleCx
{
  bool init;
  double min;
  double max;
  double sx;

  ScaleCx():
      init(false),
      min(0),
      max(0),
      sx(1)
  {
  }

  void reset()
  {
    init=false;
  }

  void set(double x)
  {
    if (!init)
    {
      init=true;
      min=x;
      max=x;
    }
    else
    {
      if (x<min)
        min=x;
      else if (x>max)
        max=x;
    }
  }

  double span() const { return (max-min) < 0 ? -(max-min) : (max-min); }
};

struct ScaleSet4
{
  ScaleCx s[4];

  ScaleSet4()
  {
    for(int i=0; i<4; ++i)
      s[i].reset();
  }
  void reset()
  {
    for(int i=0; i<4; ++i)
      s[i].reset();
  }
  ScaleCx* get(int n)
  {
    if (n<0 || n>3)
      return &s[0];
    return &s[n];
  }
};

struct SampleCx
{
  const AdsPlotSamples* data;
  ScaleCx* scale;
  Qt::GlobalColor color;

  QGraphicsItemGroup* grp;
};

class AdsPlotWidget::Private
{
public:
  QGraphicsView* gv;
  QGraphicsScene* gs;

  GraphicScale* rightScale;
  GraphicScale* leftScale;
  GraphicScale* bottomScale;

  QVector<SampleCx> scx;
  ScaleSet4 lcx;

  ScaleCx x_scale;

  bool rx_set;
  double rx_min;
  double rx_max;

  struct Cursor {
    QGraphicsItemGroup* grp;
    QGraphicsLineItem* wx;
    //QGraphicsLineItem* wy;
    double pos_x, pos_y;
    int p_x, p_y;

    Cursor():
        wx(0), pos_x(0), pos_y(0), p_x(0), p_y(0)
    {
    }

  };
  Cursor crs[2];
  unsigned int crs_selector;

  Private(QGraphicsView* view):
      gv(view),
      gs(new QGraphicsScene)
  {
    for(int i=0; i<2; ++i)
    {
      static const Qt::GlobalColor c[2]={Qt::black, Qt::darkGray};
      static const Qt::PenStyle s[2]={Qt::DashLine, Qt::DashLine};

      QPen pen(c[i]);
      pen.setStyle(s[i]);
      //pen.color().setAlpha(127);

      crs[i].wx=gs->addLine(0, 0, 0, 0, pen);
      //crs[i].wy=gs->addLine(0, 0, 0, 0, pen);
      crs[i].grp=gs->createItemGroup(QList<QGraphicsItem*>()<<crs[i].wx); //<<crs[i].wy);
      crs[i].grp->setVisible(false);
    }
    crs_selector=0;

    rx_set=false;

    gv->setScene(gs);
  }
  void clear()
  {
    for(int i=0; i<scx.size(); ++i)
    {
      if (scx[i].grp)
      {
        //gs->destroyItemGroup(scx[i].grp);
        gs->removeItem(scx[i].grp);
        delete scx[i].grp;
        scx[i].grp=0;
      }
    }

    scx.clear();

    lcx.reset();

    gs->setSceneRect(gv->rect());

  }

  void syncPosX(double x, unsigned int c)
  {
    setCursorX(x, c);
  }

  double setCursorX(double x, unsigned int c=0)
  {
    x=x*x_scale.sx;
    return setCursor(int(x), crs[c].p_y, c);
  }

  double setCursor(int x, int y, unsigned int c=0)
  {
    if (c>1)
      return 0;

    crs[c].p_x=x;
    crs[c].p_y=y;


    QPointF p=gv->mapToScene(x, y);
    x=p.x();
    y=p.y();


    if (!gv->isVisible())
      return 0;

    if (x<0)
      x=0;
    else if (x>=gs->width()-1)
      x=gs->width()-1;
    if (y<0)
      y=0;
    else if (y>=gs->height()-1)
      y=gs->height()-1;

    crs[c].pos_x=x;
    crs[c].pos_y=y;


    if (!gv->isVisible())
      return 0;

    crs[c].wx->setLine(x, 0, x, gs->height()-1);
    //crs[c].wy->setLine(0, y, gs->width()-1, y);
    crs[c].grp->setVisible(true);

    return (x/x_scale.sx);
  }

  void recalculate()
  {
    x_scale.reset();

    double h=gv->height();
    double w=gv->width();

    h=h-4;
    w=w-4;

    lcx.reset();

    gs->setSceneRect(gv->rect());

    if (rx_set)
    {
      x_scale.set(rx_min);
      x_scale.set(rx_max);
    }

    for(int i=0; i<scx.size(); ++i)
    {
      SampleCx& cx=scx[i];

      if (cx.grp)
      {
        gs->removeItem(scx[i].grp);
        delete cx.grp;
        cx.grp=0;
        //gs->destroyItemGroup(cx.grp);

        //gs->clear();
      }
      cx.grp=new QGraphicsItemGroup; //gs->createItemGroup(QList<QGraphicsItem*>());
      gs->addItem(cx.grp);
      //cx.grp->setPos(2, 2);

      for(int n=0; n<cx.data->samples(); ++n)
      {
        double x2=cx.data->x(n);
        double y2=cx.data->y(n);

        cx.scale->set(y2);
        x_scale.set(x2);
      }
    }
    double sx=x_scale.span();
    double fx=1;
    if (w!=0 && sx!=0)
      fx=w/sx;
    x_scale.sx=fx;

    for(int i=0; i<scx.size(); ++i)
    {
      SampleCx& cx=scx[i];


      double x1, y1;
      double sy=cx.scale->span();

      double fy=1;

      if (h!=0 && sy!=0)
        fy=h/sy;

      //Add limits:
      x1=0;
      y1=0;
      if (0) //rx_set)
      {
        x1=0;
        y1=0;
        QGraphicsLineItem* L=new QGraphicsLineItem(x1, y1, x1, y1, cx.grp);
        L->setPen(QPen(cx.color));

        x1=x_scale.max-x_scale.min;
        L=new QGraphicsLineItem(x1, y1, x1, y1, cx.grp);
        L->setPen(QPen(cx.color));
      }

      QPen lPen(cx.color);

      for(int n=0; n<cx.data->samples(); ++n)
      {

        double x2=cx.data->x(n);
        double y2=cx.data->y(n);

        y2-=cx.scale->min;
        y2=sy-y2;

        x2-=x_scale.min;

        x2*=fx;
        y2*=fy;

        if (n==0)
        {
          lPen.setStyle(Qt::DotLine);
          QGraphicsLineItem* L=new QGraphicsLineItem(x1, y2, x2, y2, cx.grp);
          L->setPen(lPen); //QPen(cx.color));
        }
        else
        {
          if (n==1)
            lPen.setStyle(Qt::SolidLine);

          //QGraphicsLineItem* L=new QGraphicsLineItem(x1, y1, x2, y2, cx.grp);
          //L->setPen(QPen(cx.color));
          QGraphicsLineItem* L=new QGraphicsLineItem(x1, y1, x2, y1, cx.grp);
          L->setPen(lPen); //QPen(cx.color));

          L=new QGraphicsLineItem(x2, y1, x2, y2, cx.grp);
          L->setPen(lPen); //QPen(cx.color));
        }
        x1=x2;
        y1=y2;
      }

      {
        double x2=x_scale.max-x_scale.min;
        QGraphicsLineItem* L=new QGraphicsLineItem(x1, y1, x2, y1, cx.grp);
        lPen.setStyle(Qt::DotLine);
        L->setPen(lPen);
      }

      if (cx.scale->min<=0 && cx.scale->max>=0)
      {
        x1=0;
        double x2=(x_scale.max-x_scale.min)*fx;
        double y1=(sy-(0-cx.scale->min))*fy;
        QGraphicsLineItem* L=new QGraphicsLineItem(x1, y1, x2, y1, cx.grp);
        QPen pen(cx.color);
        pen.setStyle(Qt::DashLine);
        L->setPen(pen);
      }
    }

    gv->fitInView(gs->sceneRect());
  }
};


AdsPlotWidget::AdsPlotWidget(QWidget *parent):
    QGraphicsView(parent),
    p_(*new Private(this))
{
  wSet=0;

}

void AdsPlotWidget::setGroup(AdsPlotWidgetSet * s)
{
  wSet=s;
}

void AdsPlotWidget::mouseMoveEvent(QMouseEvent *event)
{
  unsigned int crs=wSet ? wSet->syncGetCursor() : p_.crs_selector;
  double x=p_.setCursor(event->x(), event->y(), crs);
  if (wSet)
    wSet->syncPosX(this, x, crs);
  QGraphicsView::mouseMoveEvent(event);
}

void AdsPlotWidget::mousePressEvent(QMouseEvent *event)
{
  p_.crs_selector=event->button()==Qt::LeftButton ? 0 : 1;
  double x=p_.setCursor(event->x(), event->y(), p_.crs_selector);

  if (wSet)
  {
    wSet->syncSelectCursor(p_.crs_selector);
    wSet->syncPosX(this,x,  p_.crs_selector);
  }

  QGraphicsView::mousePressEvent(event);
}

void AdsPlotWidget::resizeEvent(QResizeEvent *evn)
{
  //recalculate();
  QGraphicsView::resizeEvent(evn);
}

AdsPlotWidget::~AdsPlotWidget()
{

}

void AdsPlotWidget::zoomIn()
{
  p_.gv->scale(2, 1);
}

void AdsPlotWidget::zoomOut()
{
  p_.gv->scale(0.5, 1);
}

void AdsPlotWidget::clear()
{
  p_.clear();
}

void AdsPlotWidget::syncPosX(double x, unsigned int crs)
{
  p_.syncPosX(x, crs);
}

void AdsPlotWidget::setRangeX(double min, double max)
{
  p_.rx_max=max;
  p_.rx_min=min;
  p_.rx_set=true;
}

void AdsPlotWidget::update()
{
  recalculate();
}

void AdsPlotWidget::addSamples(const AdsPlotSamples& item, unsigned int scale, Qt::GlobalColor color)
{
  SampleCx tmp;
  tmp.data=&item;
  tmp.color=color;
  tmp.grp=0;

  tmp.scale=p_.lcx.get(scale);

  p_.scx.push_back(tmp);

}

void AdsPlotWidget::recalculate()
{
  p_.recalculate();
}


class AdsPlotWidgetSet::Private
{
public:
  QVector<AdsPlotWidget*> pw;

  unsigned int crs;
};

AdsPlotWidgetSet::AdsPlotWidgetSet(QObject *parent):
    QObject(parent),
    p_(*new Private)
{
  p_.crs=0;
}


AdsPlotWidgetSet::~AdsPlotWidgetSet()
{
}

void AdsPlotWidgetSet::clear()
{
  AdsPlotWidget* p=0;
  foreach(p, p_.pw)
    p->clear();
}

void AdsPlotWidgetSet::update()
{
  AdsPlotWidget* p=0;
  foreach(p, p_.pw)
    p->update();
}

void AdsPlotWidgetSet::addWidget(AdsPlotWidget *w)
{
  if (!w)
    return;

  for(int i=0; i<p_.pw.size(); ++i)
    if (p_.pw[i]==w)
      return;

  p_.pw.push_back(w);
  w->setGroup(this);
}

bool AdsPlotWidgetSet::removeWidget(AdsPlotWidget *w)
{
  for(int i=0; i<p_.pw.size(); ++i)
    if (p_.pw[i]==w)
    {
      p_.pw.remove(i);
      return true;
    }

  return false;
}

AdsPlotWidget *AdsPlotWidgetSet::plot(int n)
{
  if (p_.pw.isEmpty())
    return 0;

  if (n>=0 && n<p_.pw.size())
    return p_.pw[n];
  return 0;
}

void AdsPlotWidgetSet::setRangeX(double min, double max)
{
  for(int i=0; i<p_.pw.size(); ++i)
  {
    p_.pw[i]->setRangeX(min, max);
  }
}

void AdsPlotWidgetSet::syncSelectCursor(unsigned int crs)
{
  p_.crs=crs;
}
unsigned int AdsPlotWidgetSet::syncGetCursor() const
{
  return p_.crs;
}

void AdsPlotWidgetSet::syncPosX(AdsPlotWidget *from, double x, unsigned int crs)
{
  for(int i=0; i<p_.pw.size(); ++i)
    if (p_.pw[i]!=from)
      p_.pw[i]->syncPosX(x, crs);
  Q_EMIT cursorMoved(x, crs);
}

void AdsPlotWidgetSet::zoomIn()
{
  for(int i=0; i<p_.pw.size(); ++i)
    p_.pw[i]->zoomIn();
}

void AdsPlotWidgetSet::zoomOut()
{
  for(int i=0; i<p_.pw.size(); ++i)
    p_.pw[i]->zoomOut();
}

