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

#include <QtGui>
#include "LineEditor.h"
#include "LineLayer.h"
#include "GraphContent.h"
#include "LayerLocker.h"

namespace SciFigs {

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

  Full description of class still missing
*/

/*!
  Description of constructor still missing
*/
LineEditor::LineEditor(LineLayer * layer)
{
  TRACE;
  _layer=layer;
  _lineIndex=-1;
  _original=0;
  _deformation=0;
}

/*!
  Restore orignal line if current point not accepted by user
*/
LineEditor::~LineEditor()
{
  TRACE;
  // Editor is still active...
  if(_lineIndex>=0) restoreOriginal();
  delete [] _original;
  delete [] _deformation;
}

/*!
  Return true if this object can be deleted
*/
bool LineEditor::mousePressEvent(QMouseEvent * e)
{
  TRACE;
  if(_lineIndex<0) { // The first click which generated this object
    pointAt(e->pos());
    if(_lineIndex<0) return true;
    _deformZ=e->modifiers() & Qt::ShiftModifier;
    _deformationWidth=0.2;
    _dx=0.0;
    _dy=0.0;
    saveOriginal();
  } else {
    if(e->buttons() & Qt::LeftButton) {
      // By removing the current index, the curve cannot be modified any more
      // The destructor won't restore the original curve
      _lineIndex=-1;
      return true;
    }
  }
  return false;
}

void LineEditor::mouseMoveEvent (const QPoint& pt)
{
  TRACE;
  if(_lineIndex<0) return;
  Point2D p=_layer->graphContent()->options().s2r(pt);
  _dx=_original[_pointIndex].x() - p.x();
  _dy=_original[_pointIndex].y() - p.y();
  deformLine();
}

void LineEditor::wheelEvent (QWheelEvent * e)
{
  TRACE;
  int d=e->delta()/120;
  if(e->modifiers() & Qt::ShiftModifier) {
    _pointIndex +=d;
    if(_pointIndex>=_count) _pointIndex=_count-1;
    else if(_pointIndex<0) _pointIndex=0;
  } else {
    _deformationWidth *= pow(1.2, d);
    if(_deformationWidth<0.2) _deformationWidth=0.2;
  }
  setDeformation();
  deformLine();
}

void LineEditor::deformLine()
{
  TRACE;
  LayerLocker ll(_layer);
  AbstractLine& line=*_layer->line(_lineIndex);
  if(_deformZ) {
    for(int i=0; i<_count;i++) {
      Point& p=_original[i];
      if(_deformation[i]>0.0) {
        line.setZ(i, p.z()-_dy*_deformation[i], _layer->pointOptions());
      } else {
        line.setZ(i, p.z(), _layer->pointOptions());
      }
    }
  } else {
    for(int i=0; i<_count;i++) {
      Point& p=_original[i];
      if(_deformation[i]>0.0) {
        line.setX(i, p.x()-_dx*_deformation[i]);
        line.setY(i, p.y()-_dy*_deformation[i], _layer->pointOptions());
      } else {
        line.setX(i, p.x());
        line.setY(i, p.y(), _layer->pointOptions());
      }
    }
  }
}

void LineEditor::pointAt(const QPoint& pScreen)
{
  TRACE;
  Point p=_layer->graphContent()->options().s2r(pScreen);
  // Calculate the equivalent in real coordinates of a 3 pixel rectangle in the neighbourhood of p
  Point pPrec=_layer->graphContent()->options().s2r(pScreen+QPoint(3,3));
  double precisionX=fabs(pPrec.x()-p.x());
  double precisionY=fabs(pPrec.y()-p.y());

  int nLine=_layer->count();
  for(int iLine=0; iLine<nLine; iLine++) {
    const AbstractLine& line=*_layer->line(iLine);
    if(line.isEditable()) {
      int nPoint=line.count();
      for(int iPoint=0; iPoint<nPoint; iPoint++) {
        if(p.isHit(line.point(iPoint, _layer->pointOptions()), precisionX, precisionY)) {
          _lineIndex=iLine;
          _pointIndex=iPoint;
          //printf("Edit point %i in line %i\n",iPoint,iLine);
          return;
        }
      }
    }
  }
  _lineIndex=-1;
}

void LineEditor::saveOriginal()
{
  TRACE;
  const AbstractLine& line=*_layer->line(_lineIndex);
  _count=line.count();
  _original=new Point[_count];
  for(int i=0; i<_count; i++) {
    _original[i]=line.point(i, _layer->pointOptions());
  }
  _deformation=new double[_count];
  setDeformation();
}

void LineEditor::restoreOriginal()
{
  TRACE;
  LayerLocker ll(_layer);
  AbstractLine& line=*_layer->line(_lineIndex);
  for(int i=0; i<_count;i++) {
    Point& p=_original[i];
    line.setX(i, p.x());
    line.setY(i, p.y(), _layer->pointOptions());
    line.setZ(i, p.z(), _layer->pointOptions());
  }
}

void LineEditor::setDeformation()
{
  TRACE;
  double factor=1.0/_deformationWidth;
  for(int i=0; i<_count;i++) {
    double x=(double)(i-_pointIndex) * factor;
    x*=x;
    if(x<25) {
      _deformation[i]=exp( -x);
    } else {
      _deformation[i]=0.0;
    }
  }
}

} // namespace SciFigs
