/***************************************************************************
**
**  This file is part of SciFigs.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This file is distributed in the hope that it will be useful, but WITHOUT
**  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
**  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2007-08-23
**  Copyright: 2007-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "XUniqueYColorLines.h"
#include "XYColorLines.h"
#include "XMLSciFigs.h"
#include "XYColorLinesProperties.h"
#include "AxisWindow.h"
#include "GraphContentLayerFactory.h"
#include "LayerPainterRequest.h"
#include "LayerLocker.h"

namespace SciFigs {

/*!
  \class XUniqueYColorLines XUniqueYcolorLines.h
  \brief A layer optimized for huge number of curves having all the same X sampling
*/

const QString XUniqueYColorLines::xmlXUniqueYColorLinesTag="XUniqueYColorLines";

REGISTER_GRAPHCONTENTLAYER(XUniqueYColorLines, "XUniqueYColorLines")

/*!
  Description of constructor still missing
*/
XUniqueYColorLines::XUniqueYColorLines(AxisWindow * parent) :
    GraphContentLayer(parent)
{
  TRACE;
  _lineWeight=0.1;
  _propertiesTab=0;
  _symbol=Symbol::NoSymbol;
  _data=new XUniqueYColorData;
  _dataOwner=true;
}

/*!
  Description of destructor still missing
*/
XUniqueYColorLines::~XUniqueYColorLines()
{
  TRACE;
  clear();
  if(_dataOwner) {
    delete _data;
  }
}

void XUniqueYColorLines::setData(XUniqueYColorData * d)
{
  if(_dataOwner) {
    delete _data;
  }
  _data=d;
  _dataOwner=false;
}

void XUniqueYColorLines::clear()
{
  TRACE;
  LayerLocker ll(this);
}

void XUniqueYColorLines::setSymbol(Symbol::Type s)
{
  TRACE;
  LayerLocker ll(this);
  _symbol=s;
}

/*!
  Setup property editor
*/
void XUniqueYColorLines::addProperties(PropertyProxy * pp)
{
  TRACE;
  if(pp->setCurrentTab(XYColorLines::_tab)) {
    pp->addReference(this);
  } else {
    XYColorLinesProperties * w=new XYColorLinesProperties;
    pp->addTab(XYColorLines::_tab, tr("Color Lines"), w, this);
  }
}

/*!
  Clean property editor
*/
void XUniqueYColorLines::removeProperties(PropertyProxy * pp)
{
  TRACE;
  pp->removeTab(XYColorLines::_tab, this);
}

void XUniqueYColorLines::properties(PropertyWidget * w) const
{
  TRACE;
  w->setValue(XYColorLinesProperties::LineWeight, lineWeightMM());
}

void XUniqueYColorLines::setProperty(uint, int pid, QVariant val)
{
  TRACE;
  switch(pid) {
  case XYColorLinesProperties::LineWeight:
    setLineWeight(val.toDouble()*0.1);
    graphContent()->deepUpdate();
    break;
  default:
    break;
  }
}

/*!
  This is the unique function you have to call to populate this layer with lines.
*/
void XUniqueYColorLines::setPointCount(int nCurves, int nX)
{
  TRACE;
  LayerLocker ll(this);
  _data->setPointCount(nCurves, nX);
}

void XUniqueYColorLines::paintData(const LayerPainterRequest& lp, QPainter& p, double dotpercm) const
{
  TRACE;
  if(_data->curveCount()==0 || _data->xCount()==0) return;

  const GraphContentOptions& gc=lp.options();
  int h=lp.size().height();
  int * screenX=new int[_data->xCount()];
  for(int i=0; i<_data->xCount(); i++) {
    screenX[i]=gc.xr2s(_data->x()[i]);
  }
  double * y=_data->y();
  QColor col(Qt::black);
  p.setBrush(col);
  p.setPen(col);
  if(_symbol!=Symbol::NoSymbol) {
    double pointSize=_lineWeight*dotpercm;
    double halfPointSize=0.5*pointSize;
    for(int i=0; i<_data->curveCount(); i++) {
      if(lp.terminated()) {
        delete [] screenX;
        return;
      }
      if(_data->colors()) {
        guiColor(_data->colors()[i], col);
        p.setBrush(col);
        p.setPen(col);
      }
      for(int j=0; j<_data->xCount(); j++) {
        double xr=_data->x()[j];
        if(xr>gc.xVisMin() && xr<gc.xVisMax()) {
          double yr=y[j];
          if(yr>gc.yVisMin() && yr<gc.yVisMax()) {
            Symbol::paint(_symbol, p, QPointF(screenX[j], gc.yr2s(y[j])), pointSize, halfPointSize);
          }
        }
      }
      y+=_data->xCount();
    }
  } else {
    Point2D * realPoints=new Point2D[_data->xCount()];
    for(int i=0; i<_data->xCount(); i++) {
      realPoints[i].setX(_data->x()[i]);
    }
    QPoint pscreen1, pscreen2;
    int lineWeight=static_cast<int>(round(_lineWeight*dotpercm));
    if(lineWeight<1) lineWeight=1;
    QPen currentPen(Qt::black, lineWeight, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin);
    for(int i=0; i<_data->curveCount(); i++) {
      if(lp.terminated()) {
        delete [] realPoints;
        delete [] screenX;
        return;
      }
      QPolygon ap(_data->xCount());
      if(_data->colors()) {
        guiColor(_data->colors()[i], col);
        currentPen.setColor(col);
      }
      p.setPen(currentPen);
      for(int j=0; j<_data->xCount(); j++) {
        realPoints[j].setY(y[j]);
      }
      y+=_data->xCount();
      Point2D * p1=realPoints, *p2;
      pscreen2.setX(screenX[0]);
      int subCurveCount=0;
      for(int j=1; j<_data->xCount(); j++) {
        p2=p1+1;
        pscreen1.setX(screenX[j-1]);
        pscreen2.setX(screenX[j]);
        if(gc.yr2s( *p1, *p2, pscreen1, pscreen2) ) {
          gc.checkOrigin(pscreen1, pscreen2, h);
          if(subCurveCount==0) {
            ap.setPoint(0, pscreen1);
            subCurveCount=1;
          } else {
            if(ap.point(subCurveCount - 1)!=pscreen1) {
              p.drawPolyline (ap.data(), subCurveCount);
              ap.setPoint(0, pscreen1);
              subCurveCount=1;
            }
          }
          ap.setPoint(subCurveCount, pscreen2);
          subCurveCount++;
        } else if(subCurveCount>1) {
          p.drawPolyline (ap.data(), subCurveCount);
          subCurveCount=0;
        }
        p1=p2;
      }
      if(subCurveCount>1) {
        p.drawPolyline (ap.data(), subCurveCount);
      }
    }
    delete [] realPoints;
  }
  delete [] screenX;
}

/*!
  When the user select curve using the mouse pointer
*/
bool XUniqueYColorLines::trackRectangle(int id, double rx1, double ry1, double rx2, double ry2, Qt::KeyboardModifiers)
{
  TRACE;
  // Select the points inside the rectangle and call dataSelected(QVector * indexList)
  // This trackRectangle function can be redefined in sub-classes
  QVector<int> * indexList=_data->inside(Rect(rx1, ry1, rx2, ry2));
  if(!indexList->isEmpty()) {
    emit dataSelected(id, indexList);
  }
  delete indexList;
  return true;
}

void XUniqueYColorLines::setColor(int index, const Color& col)
{
  TRACE;
  LayerLocker ll(this);
  _data->setColor(index, col);
}

void XUniqueYColorLines::setColors(Color * col)
{
  TRACE;
  LayerLocker ll(this);
  _data->setColors(col);
}

void XUniqueYColorLines::xml_writeProperties(XML_WRITEPROPERTIES_ARGS) const
{
  TRACE;
  GraphContentLayer::xml_writeProperties(s, context);
  XMLSciFigs * scifigsContext=static_cast<XMLSciFigs *>(context);
  if(scifigsContext->data()) writeBinaryData(s, context);
}

void XUniqueYColorLines::xml_writeBinaryData(XML_WRITEBINARYDATA_ARGS) const
{
  TRACE;
  Q_UNUSED(context)
  _data->toStream(s);
}

bool XUniqueYColorLines::xml_setBinaryData(XML_SETBINARYDATA_ARGS)
{
  TRACE;
  Q_UNUSED(context)
  XMLSciFigs * scifigsContext=static_cast<XMLSciFigs *>(context);
  Version v(scifigsContext->currentFileVersion());
  return _data->fromStream(s, &v);
}

} // namespace SciFigs
