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

#include <limits.h>

#include "NameLineLayer.h"
#include "XMLSciFigs.h"
#include "GraphContent.h"
#include "PointProperties.h"
#include "NameLineLayerProperties.h"
#include "AxisWindow.h"
#include "GraphContentLayerFactory.h"
#include "LayerPainterRequest.h"
#include "LayerLocker.h"
#include "SciFigsGlobal.h"

namespace SciFigs {

const QString NameLineLayer::xmlNameLineLayerTag="NameLineLayer";

REGISTER_GRAPHCONTENTLAYER(NameLineLayer, "NameLineLayer" )
// Compatibility
SYNONYM_GRAPHCONTENTLAYER(XYNamePlot, "NameLineLayer" )

/*!
  \class NameLineLayer NameLineLayer.h
  \brief A NameLineLayer is a layer to plot lines with symbols and names

*/

NameLineLayer::NameLineLayer(AxisWindow * parent) :
    LineLayer(parent)
{
  TRACE;
  _showNames=true;
}

NameLineLayer::~NameLineLayer()
{
  TRACE;
}


Legend NameLineLayer::legend() const
{
  TRACE;
  int n=count();
  Legend leg(n);
  for(int i=0;i < n;i++ ) {
    const AbstractNameLine& l=* line(i);
    leg.setText(i, QString::number(i));
    leg.setTextColor(i, l.textColor());
    leg.setPen(i, l.pen());
    leg.setSymbol(i, l.symbol());
  }
  return leg;
}

void NameLineLayer::setLegend(Legend legend)
{
  TRACE;
  LayerLocker ll(this);
  int n=count();
  for(int i=0; i< n; i++ ) {
    AbstractNameLine * l=line(i);
    l->setTextColor(legend.textColor(i) );
    l->setPen(legend.pen(i) );
    l->setSymbol(legend.symbol(i) );
  }
  deepUpdate();
}

inline void NameLineLayer::drawLabel(QPainter& p, const QPainterPath& name, const Angle& angle,
                                     const QRectF& r, int baseLine) const
{
  TRACE;
  p.save();
  if(fabs(angle.radians())>1e-15) {
    p.rotate(angle.degrees());
    QPointF c=r.center();
    Point2D pt(c.x(), c.y());
    pt.rotate(angle);
    p.translate(pt.x()-c.x()+r.left(), pt.y()-c.y()+r.top()+baseLine);
    p.drawPath(name);
  } else {
    p.translate(r.left(), r.top()+baseLine);
    p.drawPath(name);
  }
  p.restore();
}

void NameLineLayer::paintText(const LayerPainterRequest& lp, double dotpercm)
{
  QMutexLocker ml(&_textPathsMutex);
  _textPaths.clear();
  if( !_showNames) return ;

  const GraphContentOptions& gc=lp.options();
  double coef=dotpercm/SciFigsGlobal::screenResolution();
  QFont scaledFont=_textFont;
  if(coef!=1.0 && gc.printBitmap()) {
    Font::scale(scaledFont, coef);
  }
  QList<AbstractLine *>::const_iterator it;
  for(it=begin() ;it!=end();++it) {
    AbstractNameLine * line=static_cast<AbstractNameLine *>( *it);
    int countPoints=line->count();
    if(countPoints && line->isVisible()) {
      if(line->isSelected()) { // set selection mark
        scaledFont.setBold(true);
      }
      for(int j=0;j < countPoints;j++ ) {
        QString name=line->name(j);
        QPainterPath p;
        p.addText(0, 0, scaledFont, name);
        _textPaths.append(p);
      }
      if(line->isSelected()) { // set selection mark
        scaledFont.setBold(false);
      }
    }
  }
}

void NameLineLayer::paintData(const LayerPainterRequest& lp, QPainter& p, double dotpercm) const
{
  TRACE;
  // Draw points
  LineLayer::paintData(lp, p, dotpercm);
  if( !_showNames) return ;
  const GraphContentOptions& gc=lp.options();
  QRectF r;
  QList<AbstractLine *>::const_iterator it;
  QMutexLocker ml(&_textPathsMutex);
  QList<QPainterPath>::const_iterator itText=_textPaths.begin();
  p.setRenderHints(QPainter::Antialiasing, true);
  p.setPen(Qt::NoPen);
  for(it=begin() ;it!=end();++it) {
    AbstractNameLine * line=static_cast<AbstractNameLine *>( *it);
    if(lp.terminated()) return;
    int countPoints=line->count();
    if(countPoints && line->isVisible()) {
      Symbol sym=line->symbol();
      int symSize=(int) floor(0.05*sym.size()*dotpercm+0.5);
      if(line->isSelected()) { // set selection mark
        symSize *= 2;
        p.setBrush(Qt::red);
      } else {
        p.setBrush(line->textColor());
      }
      double ddx=line->textDx() * dotpercm;
      double ddy=-line->textDy() * dotpercm;
      int dx=(int) round(ddx);
      int dy=(int) round(ddy);
      Angle angle;
      angle.setDegrees(-line->textOrientation());
      // According to position of label the offset refers to a different side of the label rectangle
      if(ddx > 0 && ddy > -ddx && ddy < ddx) { // Label on right side
         for(int j=0;j < countPoints;j++ ) {
          ASSERT(itText!=_textPaths.end());
          Point p1=line->point(j, pointOptions());
          int orx=gc.xr2s(p1.x());
          if(dx<symSize) orx+=symSize; else orx+=dx;
          int ory=gc.yr2s(p1.y()) + dy;
          r=itText->boundingRect();
          int baseLine=(int) round(-r.top());
          r.moveLeft(orx);
          r.moveTop(ory - r.height()/2);
          drawLabel(p, *itText, angle, r, baseLine);
          itText++;
        }
      } else if(ddx < 0 && ddy > ddx && ddy < -ddx) { // Label on left side
        for(int j=0;j < countPoints;j++ ) {
          ASSERT(itText!=_textPaths.end());
          Point p1=line->point(j, pointOptions());
          int orx=gc.xr2s(p1.x());
          if(-dx<symSize) orx-=symSize; else orx+=dx;
          int ory=gc.yr2s(p1.y()) + dy;
          r=itText->boundingRect();
          int baseLine=(int) round(-r.top());
          r.moveRight(orx);
          r.moveTop(ory - r.height()/2);
          drawLabel(p, *itText, angle, r, baseLine);
          itText++;
        }
      } else if(ddy < 0) { // Label on top side
        for(int j=0;j < countPoints;j++ ) {
          ASSERT(itText!=_textPaths.end());
          Point p1=line->point(j, pointOptions());
          int orx=gc.xr2s(p1.x()) + dx;
          int ory=gc.yr2s(p1.y());
          if(-dy<symSize) ory-=symSize; else ory+=dy;
          r=itText->boundingRect();
          int baseLine=(int) round(-r.top());
          r.moveLeft(orx - r.width()/2);
          r.moveBottom(ory);
          drawLabel(p, *itText, angle, r, baseLine);
          itText++;
        }
      } else if(ddy > 0) { // Label on bottom side
        for(int j=0;j < countPoints;j++ ) {
          ASSERT(itText!=_textPaths.end());
          Point p1=line->point(j, pointOptions());
          int orx=gc.xr2s(p1.x()) + dx;
          int ory=gc.yr2s(p1.y());
          if(dy<symSize) ory+=symSize; else ory+=dy;
          r=itText->boundingRect();
          int baseLine=(int) round(-r.top());
          r.moveLeft(orx - r.width()/2);
          r.moveTop(ory);
          drawLabel(p, *itText, angle, r, baseLine);
          itText++;
        }
      } else { // dx and dy==0
        for(int j=0;j < countPoints;j++ ) {
          ASSERT(itText!=_textPaths.end());
          Point p1=line->point(j, pointOptions());
          int orx=gc.xr2s(p1.x());
          int ory=gc.yr2s(p1.y());
          r=itText->boundingRect();
          int baseLine=(int) round(-r.top());
          r.moveCenter(QPoint( orx, ory) );
          drawLabel(p, *itText, angle, r, baseLine);
          itText++;
        }
      }
    }
  }
  p.setRenderHints(QPainter::Antialiasing, false);
}

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

/*!
  Setup property editor
*/
void NameLineLayer::addProperties(PropertyProxy * pp)
{
  TRACE;
  LineLayer::addProperties(pp);
  if(pp->setCurrentTab(_tab)) {
    pp->addReference(this);
    NameLineLayerProperties * w=static_cast<NameLineLayerProperties *>(pp->currentTabWidget());
    w->addLayer(this);
  } else {
    NameLineLayerProperties * w=new NameLineLayerProperties;
    pp->addTab(_tab, tr("Name Plot"), w, this);
    w->addLayer(this);
  }
}

/*!
  Clean property editor
*/
void NameLineLayer::removeProperties(PropertyProxy * pp)
{
  TRACE;
  LineLayer::removeProperties(pp);
  if(pp->setCurrentTab(_tab)) {
    NameLineLayerProperties * w=static_cast<NameLineLayerProperties *>(pp->currentTabWidget());
    w->removeLayer(this);
    pp->removeTab(_tab, this);
  }
}

void NameLineLayer::properties(PropertyWidget * w) const
{
  TRACE;
  if(w->id()==_tab) {
    w->setValue(NameLineLayerProperties::ShowNames, isShowNames());
    w->setValue(NameLineLayerProperties::Font, fontString());
  } else {
    LineLayer::properties(w);
  }
}

void NameLineLayer::setProperty(uint wid, int pid, QVariant val)
{
  TRACE;
  if(wid==_tab) {
    switch(pid) {
    case NameLineLayerProperties::ShowNames:
      setShowNames(val.toBool());
      break;
    case NameLineLayerProperties::Font:
      setFontString(val.toString());
      break;
    default:
      break;
    }
  } else {
    LineLayer::setProperty(wid, pid, val);
  }
  graphContent()->deepUpdate();
}

} // namespace SciFigs
