/***************************************************************************
**
**  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: 2005-11-08
**  Copyright: 2005-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "ColorMapProperties.h"
#include "XMLSciFigs.h"
#include "XYValuePlot.h"
#include "GraphContent.h"
#include "GraphContentLayerFactory.h"
#include "AxisWindow.h"
#include "LayerPainterRequest.h"
#include "LayerLocker.h"

namespace SciFigs {

/*!
  \class XYValuePlot XYValuePlot.h
  \brief A XYValuePlot is a layer to draw dots of color (static)
 
  The difference with XYColorPlot is that color can be directly set through a color palette. A color is not
  directly affected to points but a value, the color is calculated from the value and the palette.
*/

const QString XYValuePlot::xmlXYValuePlotTag="XYValuePlot";

REGISTER_GRAPHCONTENTLAYER(XYValuePlot, "XYValuePlot")

XYValuePlot::XYValuePlot(AxisWindow * parent) :
    XYPlot(parent)
{
  TRACE;
  _values=0;
  _colorMap.generateColorScale(20, ColorPalette::Hsv);
}

XYValuePlot::~XYValuePlot()
{
  TRACE;
  delete _values;
}

/*!
  Set the palette. Safe for painting threads.
*/
void XYValuePlot::setColorMap(const ColorMap& map)
{
  TRACE;
  // Avoid recurrent loop
  static bool locked=false;
  if(!locked) {
    locked=true;
    LayerLocker ll(this);
    _colorMap=map;
    // This layer takes generally time to repaint (useless to repaint only this layer) 
    graphContent()->deepUpdate();
    emit colorMapChanged(map);
    locked=false;
  }
}

/*!
  Set a linear scale on palette. Safe for painting threads. For other modification of the palette
  see setColorMap().
*/
void XYValuePlot::setLinearColorMap(double minVal, double maxVal)
{
  TRACE;
  LayerLocker ll(this);
  _colorMap.setVLinear(minVal, maxVal);
  emit colorMapChanged(colorMap());
}

/*!
  Set a log scale on palette. Safe for painting threads. For other modification of the palette
  see setColorMap().
*/
void XYValuePlot::setLogColorMap(double minVal, double maxVal)
{
  TRACE;
  LayerLocker ll(this);
  _colorMap.setVLog(minVal, maxVal);
  emit colorMapChanged(colorMap());
}

/*!
  Set the pointer to value Data
*/
void XYValuePlot::setValues(QVector<double> * val)
{
  TRACE;
  LayerLocker ll(this);
  if(_values) delete _values;
  _values=val;
}

void XYValuePlot::paintData(const LayerPainterRequest& lp, QPainter& p, double dotpercm) const
{
  TRACE;
  // Make a copy of current palette (mandatory for multi-thread, palette are implicitly shared
  // objects so deep copy probably is not occuring right now, only upon user request for modification)
  const ColorMap map=_colorMap;
  const GraphContentOptions& gc=lp.options();
  p.setRenderHints(QPainter::Antialiasing, true);
  int count;
  double pointSize, halfPointSize;
  getPaintParam(count, pointSize, halfPointSize, dotpercm);
  if(count>_values->count()) count=_values->count();
  QColor col;
  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()) {
        guiColor(map.color(_values->at(i)), col);
        p.setBrush(col);
        p.setPen(col);
        Symbol::paint(_symbol, p, gc.r2sF(x, y), pointSize, halfPointSize);
      }
    }
  }
}

uint XYValuePlot::_tabColorMap=PropertyProxy::uniqueId();

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

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

void XYValuePlot::properties(PropertyWidget * w) const
{
  if(w->id()==_tabColorMap) {
    w->setValue(ColorMapProperties::Map, QVariant::fromValue(colorMap()));
  } else {
    XYPlot::properties(w);
  }
}

void XYValuePlot::setProperty(uint wid, int pid, QVariant val)
{
  if(wid==_tabColorMap) {
    switch(pid) {
    case ColorMapProperties::Map:
      setColorMap(val.value<ColorMap>());
      break;
    default:
      break;
    }
  } else {
    XYPlot::setProperty(wid, pid, val);
  }
}

/*!
  Get min and maximum of values (usually to adjust the palette).
  It is the responsability of the caller to initialize min and max to std::numeric_limits<double>::infinity() and -std::numeric_limits<double>::infinity() respectively
*/
void XYValuePlot::valueRange(double& min, double& max) const
{
  TRACE;
  if(_values->isEmpty()) return;
  double val=_values->at(0);
  if(val<min) min=val;
  if(val>max) max=val;
  for(int i=_values->count()-1;i>0;i--) {
    val=_values->at(i);
    if(val<min) min=val;
    else if(val>max) max=val;
  }
}

void XYValuePlot::xml_writeChildren(XML_WRITECHILDREN_ARGS) const
{
  TRACE;
  XMLSciFigs * scifigsContext=static_cast<XMLSciFigs *>(context);
  if(scifigsContext->data()) {
    _colorMap.xml_save(s, context);
  }
}

XMLMember XYValuePlot::xml_member(XML_MEMBER_ARGS)
{
  TRACE;
  XMLSciFigs * scifigsContext=static_cast<XMLSciFigs *>(context);
  if(scifigsContext->data()) {
    if(tag=="ColorMap" ||
       tag=="ColorPalette") {   // Kept for compatibility
      return XMLMember(new ColorMap, true);
    }
  }
  return XYPlot::xml_member(tag, attributes, context);
}

void XYValuePlot::xml_polishChild(XML_POLISHCHILD_ARGS)
{
  Q_UNUSED(context)
  if(child->xml_tagName()=="ColorMap") {
    ColorMap * pal=static_cast<ColorMap *>(child);
    setColorMap( *pal);
  }
}

void XYValuePlot::xml_writeBinaryData(XML_WRITEBINARYDATA_ARGS) const
{
  TRACE;
  XYPlot::xml_writeBinaryData(s, context);
  QVector<double>::iterator it;
  QVector<double>::iterator itEnd;

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

bool XYValuePlot::xml_setBinaryData(XML_SETBINARYDATA_ARGS)
{
  TRACE;
  int n;
  if(XYPlot::xml_setBinaryData(s, context) && (n=count())>0) {
    QVector<double>::iterator it;
    QVector<double>::iterator itEnd;

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


} // namespace SciFigs
