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

#include "GraphicSheet.h"
#include "GraphContentLayer.h"
#include "GraphContent.h"
#include "GraphicObject.h"
#include "AxisWindow.h"
#include "LayerMouseTracking.h"
#include "LayerPainterRequest.h"
#include "XMLSciFigs.h"

namespace SciFigs {

/*!
  \class GraphContentLayer GraphContentLayer.h
  \brief The base class for all layers of the plot content of a AxisWindow (GraphContent).

*/

const QString GraphContentLayer::xmlGraphContentLayerTag="GraphContentLayer";

/*!
  If \a parent is not null the layer is automatically added to the stack of layers.
  You
*/
GraphContentLayer::GraphContentLayer(AxisWindow * parent) :
    QObject(parent ? parent->graphContent() : nullptr), _mutex(Mutex::Recursive)
{
  TRACE;
  _opacity=1.0;
  _referenceCount=1; // By default one reference from the internal code
  _isEditable=true;
  _isSelected=true;
  if(parent) {
    parent->graphContent()->appendLayer(this);
  }
}

/*!
  Set parent has no effect if the layer has already a parent (i.e. already inserted in a layer stack).
  This function is mainly called by the xml restore where layer objects are created without parent.

  If you re-implement this function you must explicitely call it from your contructor otherwise your
  function won't be executed when the layer is created with a non null parent (normal way).
*/
void GraphContentLayer::setParent(GraphContent * parent)
{
  if(!QObject::parent()) QObject::setParent(parent);
}

void GraphContentLayer::lockDelayPainting()
{
  if(_mutex.tryLock()) return;
  // Else probably locked by a painting thread, these low priority threads can be terminated
  // to allow this lock
  graphContent()->delayPainting();
  while(!_mutex.tryLock(10)) {}
}

void GraphContentLayer::deepUpdate()
{
  TRACE;
  graphContent()->deepUpdate(graphContent()->indexOf(this));
}

void GraphContentLayer::beginMouseTracking(const LayerMouseTracking& mt)
{
  TRACE;
  graphContent()->beginMouseTracking(mt);
}

void GraphContentLayer::endMouseTracking(int id)
{
  TRACE;
  graphContent()->endMouseTracking(this, id);
}

bool GraphContentLayer::isMouseTracking(int id) const
{
  TRACE;
  return graphContent()->isMouseTracking(this, id);
}

void GraphContentLayer::setOpacity(qreal o)
{
  TRACE;
  _opacity=o;
  if(_opacity>1.0) _opacity=1.0;
  else if(_opacity<0.0) _opacity=0.0;
}

QString GraphContentLayer::trackingActionCtrlHelp()
{
  return tr(" Hold CTRL to deactive temporarily this mode.");
}

/*!

*/
QAction * GraphContentLayer::addTrackingAction(QString text, int id, QString statusTip)
{
  TRACE;
  GraphContent * gc=graphContent();
  QAction * a;
  QList<QAction *> al=gc->actions();
  /*for(QList<QAction *>::iterator it=al.begin(); it!=al.end();++it) {
    if((*it)->data().toInt()==id) return; // already added
  }*/
  if(!al.last()->objectName().startsWith("layerTracking")) {
    a=new QAction(gc);
    a->setObjectName("LayerTrackingSeparator");
    a->setSeparator(true);
    gc->QWidget::addAction(a);
  }

  a=new QAction(text, this);
  a->setObjectName(QString("layerTracking")+objectName()+QString::number(id));
  a->setCheckable(true);
  a->setChecked(false);
  a->setData(id);
  if(!statusTip.isEmpty()) {
    a->setStatusTip(statusTip+trackingActionCtrlHelp());
  }
  connect(a, SIGNAL(toggled(bool)), this, SLOT(toggleTrackingAction(bool)));
  gc->QWidget::addAction(a);
  return a;
}

/*!
  Convenient function to switch on/off a tracking action
*/
void GraphContentLayer::setTrackingAction(int id, bool checked)
{
  TRACE;
  GraphContent * gc=graphContent();
  QList<QAction *> alist=gc->actions();
  for(QList<QAction *>::iterator it=alist.begin(); it!= alist.end(); it++) {
    QAction * a=*it;
    if(a->data().toInt()==id) {
      a->setChecked(checked);
      return;
    }
  }
}

/*!

*/
void GraphContentLayer::toggleTrackingAction(bool checked, int id)
{
  TRACE;
  if(id==-1) {
    QAction * a=qobject_cast<QAction *>(sender());
    if(!a) return;
    id=a->data().toInt();
  }
  if(checked) {
    if(!isMouseTracking(id)) {
      LayerMouseTracking mt(this);
      mt.setId(id);
      mt.setIdleCursor(QPixmap(":selectcursor.png"), 4, 4);
      mt.setRectangle(true);
      mt.showRectangle();
      beginMouseTracking(mt);
    }
  } else {
    if(isMouseTracking(id)) {
      endMouseTracking(id);
    }
  }
}

void GraphContentLayer::paint(const LayerPainterRequest& lp, QPainter & p, double dotpercm)
{
  TRACE;
  if(opacity()==1.0) {
    lock();
    paintData(lp, p, dotpercm);
    unlock();
  } else if(opacity() > 0.0) {
    p.save();
    p.setOpacity(opacity());
    // Opacity must be applied on the whole layer image not on individual painting operations
    // So we first draw to a temporary pixmap with full opacity and then draw this pixmap with the
    // appropriate opacity.
    //QPixmap * pm=new QPixmap(w,h);
    //QPainter ppx;
    //ppx.begin(pm);
    //ppx.fillRect(0,0,w,h,Qt::white);
    lock();
    paintData(lp, p, dotpercm);
    unlock();
    //ppx.end();
    //p.drawPixmap(0,0,*pm);
    //delete pm;
    p.restore();
  }
}

#ifdef GRAPHCONTENTLAYER_TESTING
void GraphContentLayer::paintData(const LayerPainterRequest& lp, QPainter& p, double) const
{
  TRACE;
  const GraphContentOptions& gc=lp.options();
  double val;
  int w=lp.size().width();
  int h=lp.size().height();
  int y, x;
  // Y rule
  for(val=-10; val <= 110; val += 10) {
    if(lp.terminated()) return;
    y=gc.yr2s(val);
    p.drawLine(0, y, 100, y);
    p.drawLine(w, y, w - 100, y);
  }
  for(val=-10; val <= 110; val += 2) {
    if(lp.terminated()) return;
    y=gc.yr2s(val);
    p.drawLine(0, y, 50, y);
    p.drawLine(w, y, w - 50, y);
  }
  // X rule
  for(val=-10; val <= 110; val += 10) {
    if(lp.terminated()) return;
    x=gc.xr2s(val);
    p.drawLine(x, h, x, h - 100);
    p.drawLine(x, 0, x, 100);
  }
  for(val=-10; val <= 110; val += 2) {
    if(lp.terminated()) return;
    x=gc.xr2s(val);
    p.drawLine(x, h, x, h - 50);
    p.drawLine(x, 0, x, 50);
  }
}
#endif

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

XMLMember GraphContentLayer::xml_member(XML_MEMBER_ARGS)
{
  TRACE;
  XMLSciFigs * scifigsContext=static_cast<XMLSciFigs *>(context);
  if(scifigsContext->makeUp()) {
    return qobject_member(this, tag, attributes, context);
  }
  return XMLMember(XMLMember::Unknown);
}

QString GraphContentLayer::coordinateTipInfo(const Point2D&, const Point2D&) const
{
  return QString();
}

} // namespace SciFigs
