/***************************************************************************
**
**  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: 2002-05-07
**  Copyright: 2002-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "XYPlotProperties.h"
#include "XMLSciFigs.h"
#include "AxisWindow.h"
#include "XYPlot.h"
#include "GraphContentLayerFactory.h"
#include "LayerPainterRequest.h"
#include "LegendTable.h"
#include "LayerLocker.h"

namespace SciFigs {

/*!
  \class XYPlot XYPlot.h
  \brief A XYPlot is a layer to plot black dots (static)

  Basic plot of black dots defined by two pointer to QVector (_xData, _yData)
*/

const QString XYPlot::xmlXYPlotTag="XYPlot";

REGISTER_GRAPHCONTENTLAYER(XYPlot, "XYPlot")

XYPlot::XYPlot(AxisWindow * parent) :
    GraphContentLayer(parent)
{
  TRACE;
  _xData=0;
  _yData=0;
  setDotDiameter(0.1); // 0.1 cm
  _symbol=Symbol::Circle;
}

XYPlot::~XYPlot()
{
  TRACE;
  delete _xData;
  delete _yData;
}

/*!
  Set the pointer to X Data
*/
void XYPlot::setXData(QVector<double> * x)
{
  LayerLocker ll(this);
  delete _xData;
  _xData=x;
}

/*!
  Set the pointer to Y Data
*/
void XYPlot::setYData(QVector<double> * y)
{
  LayerLocker ll(this);
  delete _yData;
  _yData=y;
}

/*!
  \fn double dotDiameter() const
  Returns diameter of point on plot (in cm)
*/

/*!
  \fn void setDotDiameter (double d)
  Change diameter of point on plot (in cm)
*/

uint XYPlot::_tab=PropertyProxy::uniqueId();

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

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


void XYPlot::properties(PropertyWidget * w) const
{
  TRACE;
  w->setValue(XYPlotProperties::DotDiameter, dotDiameter()*10.0);
  w->setValue(XYPlotProperties::Symbol, LegendTable::symbolItem(_symbol));
}

void XYPlot::setProperty(uint, int pid, QVariant val)
{
  TRACE;
  switch(pid) {
  case XYPlotProperties::DotDiameter:
    setDotDiameter(val.toDouble()*0.1);
    graphContent()->deepUpdate();
    break;
  case XYPlotProperties::Symbol:
    setSymbol(LegendTable::symbolType(val.toInt()));
    graphContent()->deepUpdate();
    break;
  default:
    break;
  }
}

void XYPlot::paintData(const LayerPainterRequest& lp, QPainter& p, double dotpercm) const
{
  TRACE;
  const GraphContentOptions& gc=lp.options();
  p.setRenderHints(QPainter::Antialiasing, true);
  int count;
  double pointSize, halfPointSize;
  getPaintParam(count, pointSize, halfPointSize, dotpercm);
  for(int i=0; i<count; i++) {
    double x=(*_xData)[i];
    if(x>gc.xVisMin() && x<gc.xVisMax()) {
      double y=(*_yData)[i];
      if(y>gc.yVisMin() && y<gc.yVisMax()) {
        Symbol::paint(_symbol, p, gc.r2s(x, y), pointSize, halfPointSize);
      }
    }
  }
}

bool XYPlot::trackRectangle(int, double rx1, double ry1, double rx2, double ry2,
                            Qt::KeyboardModifiers)
{
  TRACE;
  // Select the points inside the rectangle and call trackRectangle(QVector * indexList)
  // This select function can redefined in sub-classes
  Rect r(rx1, ry1, rx2, ry2);
  QVector<int> * indexList=new QVector<int>;
  int minSize=_xData->size();
  if(minSize>_yData->size()) {
    minSize=_yData->size();
  }
  for(int i=0; i<minSize; i++) {
    Point p=Point((*_xData)[i], (*_yData)[i]);
    if(r.includes(p)) indexList->push_back(i);
  }
  if(indexList->size()>0) {
    select(indexList);
  }
  delete indexList;
  return true;
}

/*!
  Can be redefined in sub classes. This function is triggered when user wants to select points
*/
void XYPlot::select(QVector<int> *)
{
  TRACE;
  // Basic functionnality to test method
  /*fprintf(stdout, "%i selected\n", indexList->size());
  for(int i=0;i < indexList->size();i++ ) {
    int ind=( *indexList) [ i ];
    fprintf(stdout, "%g %g\n", ( *_xData) [ ind ], ( *_yData) [ ind ] );
  }*/
}

Rect XYPlot::boundingRect() const
{
  TRACE;
  int minSize=_xData->size();
  if(minSize > _yData->size()) minSize=_yData->size();
  if(minSize >= 1) {
    double x=( *_xData) [ 0 ];
    double y=( *_yData) [ 0 ];
    Rect r(x, y, x, y);
    for(int i=1;i < minSize;i++ ) r.add(( *_xData) [ i ], ( *_yData) [ i ] );
    return r;
  } else return Rect();
}

void XYPlot::swapXY()
{
  TRACE;
  QVector<double> * tmp=_xData;
  _xData=_yData;
  _yData=tmp;
  graphContent() ->deepUpdate();
}

void XYPlot::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 XYPlot::xml_writeBinaryData(XML_WRITEBINARYDATA_ARGS) const
{
  TRACE;
  Q_UNUSED(context)
  s << count();
  QVector<double>::iterator it;
  QVector<double>::iterator itEnd;

  itEnd=_xData->end();
  for(it=_xData->begin();it!=itEnd;++it) s << *it;

  itEnd=_yData->end();
  for(it=_yData->begin();it!=itEnd;++it) s << *it;
}

bool XYPlot::xml_setBinaryData(XML_SETBINARYDATA_ARGS)
{
  TRACE;
  Q_UNUSED(context)
  int n;
  s >> n;
  if(n>0) {
    QVector<double>::iterator it;
    QVector<double>::iterator itEnd;

    _xData=new QVector<double>;
    _xData->resize(n);
    itEnd=_xData->end();
    for(it=_xData->begin();it!=itEnd;++it) s >> *it;

    _yData=new QVector<double>;
    _yData->resize(n);
    itEnd=_yData->end();
    for(it=_yData->begin();it!=itEnd;++it) s >> *it;
  }
  return true;
}


} // namespace SciFigs
