/***************************************************************************
**
**  This file is part of GeopsyGui.
**
**  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: 2002-08-30
**  Copyright: 2002-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#ifndef SIGNALLAYER_H
#define SIGNALLAYER_H

#include <GeopsyCore.h>
#include <SciFigs.h>

#include "GeopsyGuiDLLExport.h"

namespace GeopsyGui {

class SignalInt;
class SubPoolWindow;

class GEOPSYGUI_EXPORT SignalLayer: public GridPlot
{
  Q_OBJECT
  // Compatibility
  Q_PROPERTY(bool smoothGrid READ dummyPropertyInt WRITE setSmooth STORED false)
  Q_PROPERTY(QString norm READ dummyPropertyString WRITE setNormalize STORED false)
  Q_PROPERTY(double normValue READ dummyPropertyDouble WRITE setNormalizeValue STORED false)
  Q_PROPERTY(bool useReceiverCoord READ dummyPropertyInt WRITE setReceiverYAxis STORED false)

  Q_PROPERTY(bool variableArea READ variableArea WRITE setVariableArea)
  Q_PROPERTY(bool wiggleTrace READ wiggleTrace WRITE setWiggleTrace)
  Q_PROPERTY(bool grid READ grid WRITE setGrid)
  Q_PROPERTY(double normalizeValue READ normalizeValue WRITE setNormalizeValue)
  Q_PROPERTY(double clipValue READ clipValue WRITE setClipValue)
  Q_PROPERTY(double clipPerc READ clipPerc WRITE setClipPerc)
  Q_PROPERTY(double overlap READ overlap WRITE setOverlap)
  Q_PROPERTY(QString clip READ clipString WRITE setClip)
  Q_PROPERTY(QString normalize READ normalizeString WRITE setNormalize)
  Q_PROPERTY(QString offset READ offsetString WRITE setOffset)
  Q_PROPERTY(QString yAxis READ yAxisString WRITE setYAxis)
  Q_PROPERTY(QString timeScale READ timeScaleString WRITE setTimeScale)
  Q_PROPERTY(QString timeRange READ timeRangeString WRITE setTimeRange)
  Q_PROPERTY(QString aroundPickName READ aroundPickName WRITE setAroundPickName)
  Q_PROPERTY(double beforePickDelay READ beforePickDelay WRITE setBeforePickDelay)
  Q_PROPERTY(double afterPickDelay READ afterPickDelay WRITE setAfterPickDelay)
  Q_PROPERTY(double gapHeight READ gapHeight WRITE setGapHeight)
public:
  SignalLayer(AxisWindow * parent=nullptr);
  ~SignalLayer ();

  virtual const QString& xml_tagName() const {return xmlSignalLayerTag;}
  static const QString xmlSignalLayerTag;

  void subPoolUpdate(SubSignalPool * subPool=nullptr);
  void signalsUpdate();

  int baseLineCount() const;
  void minMaxY (double& min, double& max) const;
  int signalAt (int yPos, double& baseDistance) const;
  void sigYBoundaries (int index, double overlap, QRect& bound) const;

  virtual Rect boundingRect() const;

  const SubSignalPool * subPool() const {return _subPool;}

  enum Normalize {NormalizeAll, NormalizeOne,
                  NormalizeVisibleAll, NormalizeVisibleOne,
                  NormalizeValue};
  enum Offset {NoOffset, GlobalOffset, VisibleOffset};
  enum Clip {NoClip, ClipOverlap, ClipValue, ClipPercentage};
  enum YAxis{ViewerIndex, Receiver, SignalName, Overlay};
  enum TimeRange {AvailableRange, AroundPickRange, CustomRange};
  enum TimeScale {AbsoluteTime, RelativeToStartTime};

  bool variableArea() const {return _variableArea;}
  void setVariableArea(bool b) {_variableArea=b;}

  bool wiggleTrace() const {return _wiggleTrace;}
  void setWiggleTrace(bool b) {_wiggleTrace=b;}

  double gapHeight() const {return _gapHeight;}
  void setGapHeight(double h) {_gapHeight=h;}

  bool grid() const {return _gridPlot;}
  void setGrid (bool b);

  double normalizeValue() const {return _normalizeValue;}
  void setNormalizeValue(double v) {_normalizeValue=v;}

  double clipValue() const {return _clipValue;}
  void setClipValue(double v) {_clipValue=v;}

  double clipPerc() const {return _clipPerc;}
  void setClipPerc(double v) {_clipPerc=v;}

  double overlap() const {return _overlap;}
  void setOverlap(double v) {_overlap=v; _signalOverlaps.clear();}

  Normalize normalize() const {return _normalize;}
  void setNormalize(Normalize n) {_normalize=n;}
  QString normalizeString() const;
  void setNormalize(QString n);

  Offset offset() const {return _offset;}
  void setOffset(Offset o) {_offset=o;}
  QString offsetString() const;
  void setOffset(QString n);

  Clip clip() const {return _clip;}
  void setClip (Clip c) {_clip=c;}
  QString clipString() const;
  void setClip (QString c);

  YAxis yAxis() const {return _yAxis;}
  QString yAxisString() const;
  void setYAxis(YAxis a) {_yAxis=a;}
  void setYAxis(QString a);
  // Compatibility
  void setReceiverYAxis(bool rc) {_yAxis=rc ? Receiver : ViewerIndex;}

  TimeScale timeScale() const {return _timeScale;}
  QString timeScaleString() const;
  void setTimeScale(TimeScale s);
  void setTimeScale(QString ts);

  TimeRange timeRange() const {return _timeRange;}
  QString timeRangeString() const;
  void setTimeRange(TimeRange tr) {_timeRange=tr;}
  void setTimeRange(QString tr);

  const QString& aroundPickName() const {return _aroundPickName;}
  void setAroundPickName(const QString& pn) {_aroundPickName=pn;}

  double beforePickDelay() const {return _beforePickDelay;}
  void setBeforePickDelay(double d) {_beforePickDelay=d;}

  double afterPickDelay() const {return _afterPickDelay;}
  void setAfterPickDelay(double d) {_afterPickDelay=d;}

  const TimeRangeParameters& customTimeRange() const {return _customTimeRange;}
  void setCustomTimeRange(const TimeRangeParameters& ctr) {_customTimeRange=ctr;}

  void highlightSignal(const GraphContentOptions& gc, QPainter& p, int w, int iSig, int isigmin, int isigmax) const;

  const QColor& signalColor(const Signal * sig) const;
  void setSignalColor(const Signal * sig, const QColor& c);
  void clearSignalColors();
  Legend signalLegend() const;
  void setSignalLegend(const Legend& l);

  virtual bool hasProperties() {return true;}
  virtual void addProperties(PropertyProxy * pp);
  virtual void removeProperties(PropertyProxy * pp);
  virtual void properties(PropertyWidget * w) const;
  virtual void setProperty(uint wid, int pid, QVariant val);

  void visibleIndexRange(const GraphContentOptions& gc, int& isigmin, int& isigmax) const;
  double signalY(int index) const {return _signalY[index];}
  void timeWindowY(const GraphContentOptions& gc, int iSig, double rh, int& top, int& height) const;
  void drawingAmplitude(const Signal * sig, int itmin, int itmax, double& valClip, double& affMax) const;
  double maxAmplitude(const GraphContentOptions& gc) const;
  bool visibleSamples(const GraphContentOptions& gc, const Signal * sig,
                      int& itmin, int& itmax, double& x0, double& dx) const;
  void visibleSamples(const GraphContentOptions& gc, const Signal * sig, int& itmin, int& itmax) const;
  const DateTime& timeReference(const Signal * sig) const;

  enum TrackingModes{AddGaps, RemoveGaps};
  virtual void toggleTrackingAction(bool checked, int id=-1);
  virtual bool trackRectangle(int id, double rx1, double ry1, double rx2, double ry2, Qt::KeyboardModifiers);
  QList<Signal *> hitSignals(double ry1, double ry2) const;
signals:
  void propertiesChanged();
protected:
  virtual void paintData(const LayerPainterRequest& lp, QPainter& p, double dotpercm) const;
  virtual bool wheelEvent (QWheelEvent * e);
  virtual bool keyPressEvent (QKeyEvent* e);

  virtual void xml_writeProperties(XML_WRITEPROPERTIES_ARGS) const;
  virtual void xml_writeChildren(XML_WRITECHILDREN_ARGS) const;
  virtual XMLMember xml_member(XML_MEMBER_ARGS);
  virtual bool xml_setProperty(XML_SETPROPERTY_ARGS);
  virtual void xml_polishChild(XML_POLISHCHILD_ARGS);
  virtual bool xml_polish(XML_POLISH_ARGS);

  static uint _tab;
private:
  // Pointer to graphic window's official subPool
  SubSignalPool * _subPool;
  bool _subPoolOwner;

  // Appearance options
  // ------------------
  // If true, positive variable area is filled in black
  bool _variableArea;
  // If true, a wiggle trace is displayed
  bool _wiggleTrace;
  // Pointeur to grid structure, if NULL trace plotting is used
  Grid2D<double> * _gridPlot;
  // Type of Y axis
  YAxis _yAxis;
  // Vector where to store the projections of the receiver coordinates (axis Y)
  // or the index of signals (if ViewerIndex)
  QVector<double> _signalY;
  // Height of gaps
  double _gapHeight;

  // Trace plotting options
  // ----------------------
  Offset _offset;
  Normalize _normalize;
  // User value to normalize
  double _normalizeValue;
  // Flag to select the type of clipping
  Clip _clip;
  // Value used to clip is _clip is Value
  double _clipValue;
  // Percentage used to clip is _clip is Percentage
  double _clipPerc;
  // Value of overlap between adjacent signals
  double _overlap;
  // Individual values of overlap
  QMap<const Signal *, double> _signalOverlaps;
  // Individual colors for signals
  QMap<const Signal *, QColor> _signalColors;

  // Time scale options
  // ------------------
  TimeScale _timeScale;

  // Time range options
  // ------------------
  TimeRange _timeRange;
  TimeRangeParameters _customTimeRange;
  QString _aroundPickName;
  double _beforePickDelay;
  double _afterPickDelay;

  // Updates the internal grid whenever the signals change
  void updateGrid();
  void drawSignal(const GraphContentOptions& gc, QPainter& p, const Signal * sig,
                   int iSig, int itmin, int itmax, double x0, double dx,
                   bool allSamplesVisible, double affMax, int pixelWidth,
                   bool variableArea, bool wiggleTrace) const;
  void drawGaps(const GraphContentOptions& gc, QPainter& p, const Signal * sig, int iSig) const;
private:
  void spinOverlap(int nSteps, bool individual, int y);

  static const QString signalColorTag;
  static const QString signalOverlapTag;
  static const QString indexTag;
};

/*!
  Estimate the visible signals
*/
inline void SignalLayer::visibleIndexRange(const GraphContentOptions& gc, int& isigmin, int& isigmax) const
{
  TRACE;
  int n=_subPool->count();
  switch(_yAxis) {
  case ViewerIndex:
    if(n > 1) {
      if(gc.yVisMin() > 1)
        isigmin=(int) floor(gc.yVisMin()) - 1;
      else isigmin=0;
      isigmax=(int) ceil(gc.yVisMax()) - 1;
      if(isigmax > n) isigmax=n;
    } else {
      isigmin=0;
      isigmax=n;
    }
    break;
  case Receiver: // TODO something better
    isigmin=0;
    isigmax=n;
    break;
  case SignalName:         // TODO something better
    isigmin=0;
    isigmax=n;
    break;
  case Overlay:
    isigmin=0;
    isigmax=n;
    break;
  }
}

inline void SignalLayer::visibleSamples(const GraphContentOptions& gc, const Signal * sig,
                                        int& itmin, int& itmax) const
{
  TRACE;
  double x0, dx;
  visibleSamples(gc, sig, itmin, itmax, x0, dx);
}

} // namespace GeopsyGui

#endif // SIGNALLAYER_H
