/***************************************************************************
**
**  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-10-29
**  Copyright: 2007-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "SlopeEstimator.h"
#include "GraphContentLayerFactory.h"
#include "PlotLine2D.h"
#include "AxisWindow.h"
#include "GraphicObject.h"
#include "SciFigsGlobal.h"
#include "LayerPainterRequest.h"
#include "LayerLocker.h"

namespace SciFigs {

/*!
  \class SlopeEstimator SlopeEstimator.h
  \brief Brief description of class still missing

  Full description of class still missing
*/

const QString SlopeEstimator::xmlSlopeEstimatorTag="SlopeEstimator";

REGISTER_GRAPHCONTENTLAYER(SlopeEstimator, "SlopeEstimator")

SlopeEstimator::SlopeEstimator(AxisWindow * parent)
  : LineLayer(parent)
{
  TRACE;
  PlotLine2D * ref=new PlotLine2D;
  ref->setPen(Pen( Qt::red, 0.6) );
  ref->setSymbol(Symbol( Symbol::Circle, 1.2, Pen(Qt::red, 0.0),
                             Brush(Qt::red, Qt::SolidPattern) ));
  setReferenceLine(ref);

  _format=DegreesFromX;
}

QString SlopeEstimator::formatString() const
{
  TRACE;
  switch(_format) {
  case DegreesFromX: return "DegreesFromX";
  case DegreesFromY: return "DegreesFromY";
  case RatioYOverX: return "RatioYOverX";
  case RatioXOverY: return "RatioXOverY";
  }
  return QString();
}

void SlopeEstimator::setFormat(QString f)
{
  TRACE;
  if(f.isEmpty()) return;
  QChar axis=f[f.count()-1];
  if(f[0]=='D') {
    if(axis=='X') _format=DegreesFromX; else _format=DegreesFromY;
  } else {
    if(axis=='X') _format=RatioYOverX; else _format=RatioXOverY;
  }
}

void SlopeEstimator::paintText(const LayerPainterRequest& lp, double dotpercm)
{
  TRACE;
  QMutexLocker ml(&_textPathsMutex);
  _textPaths.clear();
  const GraphContentOptions& gc=lp.options();
  int h=lp.size().height();
  double coef=dotpercm/SciFigsGlobal::screenResolution();
  QFont scaledFont=_textFont;
  if(coef!=1.0 && gc.printBitmap()) {
    Font::scale(scaledFont, coef);
  }

  double value;
  char numberType;
  int numberPrecision;
  SAFE_UNINITIALIZED(value, 0);
  SAFE_UNINITIALIZED(numberType, 'f');
  SAFE_UNINITIALIZED(numberPrecision, 0);
  switch (_format) {
  case DegreesFromX:
    numberType='f';
    numberPrecision=1;
    break;
  case DegreesFromY:
    numberType='f';
    numberPrecision=1;
    break;
  case RatioYOverX:
      {
        Axis * a=graph()->yAxis();
        numberType=a->numberType();
        numberPrecision=a->numberPrecision();
      }
    break;
  case RatioXOverY:
    {
      Axis * a=graph()->xAxis();
      numberType=a->numberType();
      numberPrecision=a->numberPrecision();
    }
    break;
  }

  QList<AbstractLine *>::const_iterator it;
  for(it=begin() ;it!=end();++it) {
    PlotLine2D * line=static_cast<PlotLine2D *>( *it);
    int countPoints=line->count();
    if(countPoints && line->isVisible()) {
      int i=0;
      Point p1;
      while(i<countPoints) {
        if(line->isValid(i)) {
          p1=line->point(i, pointOptions());
          break;
        }
        i++;
      }
      if(i>=countPoints) continue ;
      QPoint pscreen1, pscreen2;
      for(i++;i < countPoints;i++ ) {
        if(!line->isValid(i)) continue;
        Point p2=line->point(i, pointOptions());
        // Calculate display angle
        if(gc.r2s(p1, p2, pscreen1, pscreen2) ) {
          gc.checkOrigin(pscreen1, pscreen2, h);
          Angle angle(pscreen2.x()-pscreen1.x(), pscreen2.y()-pscreen1.y());
          angle.initRadians();
          angle.initDegrees();
          // Calculate number to plot
          switch (_format) {
          case DegreesFromX:
            value=p1.azimuthTo(p2);
            break;
          case DegreesFromY:
            value=0.5*M_PI-p1.azimuthTo(p2);
            break;
          case RatioYOverX:
              value=(p2.y()-p1.y())/(p2.x()-p1.x());
            break;
          case RatioXOverY:
            value=(p2.x()-p1.x())/(p2.y()-p1.y());
            break;
          }
          QPainterPath p;
          p.addText(0, 0, scaledFont, Number::toString(fabs(value), numberType, numberPrecision) );
          _textPaths.append(p);
        }
        p1=p2;
      }
    }
  }
}

void SlopeEstimator::paintData(const LayerPainterRequest& lp, QPainter& p, double dotpercm) const
{
  TRACE;
  // Draw classical lines
  LineLayer::paintData(lp, p, dotpercm);
  const GraphContentOptions& gc=lp.options();
  int h=lp.size().height();
  p.setRenderHints(QPainter::Antialiasing, true);
  p.setPen(Qt::NoPen);
  QRectF r;
  QList<AbstractLine *>::const_iterator it;
  QMutexLocker ml(&_textPathsMutex);
  QList<QPainterPath>::const_iterator itText=_textPaths.begin();
  p.setPen(Qt::NoPen);
  for(it=begin() ;it!=end();++it) {
    if(lp.terminated()) return;
    PlotLine2D * line=static_cast<PlotLine2D *>( *it);
    int countPoints=line->count();
    p.setBrush(line->symbol().pen().color());
    if(countPoints && line->isVisible()) {
      int i=0;
      Point p1;
      while(i<countPoints) {
        if(line->isValid(i)) {
          p1=line->point(i, pointOptions());
          break;
        }
        i++;
      }
      if(i>=countPoints) continue ;
      QPoint pscreen1, pscreen2;
      for(i++;i < countPoints;i++ ) {
        if(!line->isValid(i)) continue;
        Point p2=line->point(i, pointOptions());
        // Calculate display angle
        if(gc.r2s(p1, p2, pscreen1, pscreen2) ) {
          gc.checkOrigin(pscreen1, pscreen2, h);
          Angle angle(pscreen2.x()-pscreen1.x(), pscreen2.y()-pscreen1.y());
          angle.initRadians();
          angle.initDegrees(); // Required by QPainter
          QPoint pMidScreen=(pscreen2+pscreen1)*0.5;
          r=itText->boundingRect();
          int originalTop=(int) r.top();
          r.moveCenter(pMidScreen);
          p.save();
          p.rotate(angle.degrees());
          QPointF c(( r.left() + r.right()) * 0.5, r.bottom());
          Point2D pt(c.x(), c.y());
          pt.rotate(angle);
          p.translate(pt.x() - c.x() + r.left(), pt.y() - c.y() + r.top());
          p.setOpacity(0.7);
          p.fillRect( -4, originalTop-4, (int)r.width()+8, (int)r.height()+8, Qt::white);
          p.setOpacity(1);
          p.drawPath(*itText);
          itText++;
          p.restore();
        }
        p1=p2;
      }
    }
  }
  p.setRenderHints(QPainter::Antialiasing, false);
}

} // namespace SciFigs
