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

#ifndef AXIS_H
#define AXIS_H

#include <QGpGuiTools.h>
#include <QGpCoreMath.h>

#include "SciFigsDLLExport.h"
#include "SciFigsGlobal.h"
#include "Font.h"

namespace SciFigs {

class AxisGeneralProperties;
class AxisFormatProperties;
class GraphicObjectProperties;
class AxisWindow;
class GraphContent;

class SCIFIGS_EXPORT Axis : public QWidget, public PropertyItem, public XMLClass
{
  Q_OBJECT
  // Overload to mark the beginning of properties to save to XML
  Q_PROPERTY(QString objectName READ objectName WRITE setObjectName STORED true)
  // Compatibility
  Q_PROPERTY(QString name READ dummyPropertyString WRITE setObjectName STORED false)
  Q_PROPERTY(bool autoLabel READ dummyPropertyDouble WRITE setAutoTicks STORED false)
  Q_PROPERTY(double label READ dummyPropertyDouble WRITE setMajorTicks STORED false)
  Q_PROPERTY(double tick READ dummyPropertyDouble WRITE setMinorTicks STORED false)
  Q_PROPERTY(QString position READ dummyPropertyString WRITE setPositionString STORED false)
  Q_PROPERTY(QString numType READ dummyPropertyString WRITE setNumberType STORED false)
  Q_PROPERTY(int numPrec READ dummyPropertyInt WRITE setCurrentNumberPrecision STORED false)
  Q_PROPERTY(int inversedNumPrec READ dummyPropertyInt WRITE setCurrentInversedNumberPrecision STORED false)
  Q_PROPERTY(bool autoPrec READ dummyPropertyInt WRITE setAutoPrecision STORED false)
  Q_PROPERTY(bool zoomBlocked READ dummyPropertyInt WRITE setZoomBlocked STORED false)
  Q_PROPERTY(QString printScaleType READ dummyPropertyString WRITE setSizeType STORED false)
  Q_PROPERTY(double printLengthInfo READ dummyPropertyDouble WRITE setSizeInfo STORED false)
  Q_PROPERTY(double printLineWeight READ dummyPropertyDouble WRITE setLineWeight STORED false)
  Q_PROPERTY(double printTickLength READ dummyPropertyDouble WRITE setTickSize STORED false)

  Q_PROPERTY(QString scaleType READ scaleTypeString WRITE setScaleType SCRIPTABLE true)
  Q_PROPERTY(bool reversedScale READ reversedScale WRITE setReversedScale SCRIPTABLE true)
  Q_PROPERTY(double min READ minimum WRITE setMinimum SCRIPTABLE true)
  Q_PROPERTY(double max READ maximum WRITE setMaximum SCRIPTABLE true)
  Q_PROPERTY(bool autoTicks READ autoTicks WRITE setAutoTicks SCRIPTABLE true)
  Q_PROPERTY(double majorTicks READ majorTicks WRITE setMajorTicks SCRIPTABLE true)
  Q_PROPERTY(double minorTicks READ minorTicks WRITE setMinorTicks SCRIPTABLE true)
  Q_PROPERTY(QString stringLabels READ stringLabels WRITE setStringLabels SCRIPTABLE true)
  Q_PROPERTY(bool showLabels READ showLabels WRITE setShowLabels SCRIPTABLE true)
  Q_PROPERTY(bool zoomEnabled READ zoomEnabled WRITE setZoomEnabled SCRIPTABLE true)
  Q_PROPERTY(QString numberType READ numberTypeString WRITE setNumberType SCRIPTABLE true)
  Q_PROPERTY(int numberPrecision READ currentNumberPrecision WRITE setCurrentNumberPrecision SCRIPTABLE true)
  Q_PROPERTY(double unitFactor READ unitFactor WRITE setUnitFactor SCRIPTABLE true)
  Q_PROPERTY(bool autoPrecision READ autoPrecision WRITE setAutoPrecision SCRIPTABLE true)
  Q_PROPERTY(int inversedNumberPrecision READ currentInversedNumberPrecision WRITE setCurrentInversedNumberPrecision SCRIPTABLE true)
  Q_PROPERTY(QString orientation READ orientationString WRITE setOrientationString SCRIPTABLE true)
  Q_PROPERTY(QString title READ currentTitle WRITE setCurrentTitle SCRIPTABLE true)
  Q_PROPERTY(QString inversedTitle READ currentInversedTitle WRITE setCurrentInversedTitle SCRIPTABLE true)
  Q_PROPERTY(QString font READ fontString WRITE setFontString SCRIPTABLE true)
  Q_PROPERTY(QString sizeType READ sizeTypeString WRITE setSizeType SCRIPTABLE true)
  Q_PROPERTY(double sizeInfo READ sizeInfo WRITE setSizeInfo SCRIPTABLE true)
  Q_PROPERTY(double lineWeight READ lineWeight WRITE setLineWeight SCRIPTABLE true)
  Q_PROPERTY(double tickSize READ tickSize WRITE setTickSize SCRIPTABLE true)
  Q_PROPERTY(bool visible READ isEnabled WRITE setEnabled SCRIPTABLE true)
  Q_PROPERTY(QString timeReference READ timeReferenceString WRITE setTimeReference SCRIPTABLE true)
  Q_ENUMS (PrintScaleType)
  DUMMY_PROPERTIES
public:
  Axis(QWidget *parent=nullptr);
  ~Axis();

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

  void operator=(const Axis& o);
  void setGraphContent(GraphContent * gc);

  enum SizeType {TotalSize=0, AxisSize, Scaled};
  enum Orientation {North=0, South, East, West};

  double minimum() const {return constScale().globalMinimum();}
  double maximum() const {return constScale().globalMaximum();}
  void setRange(double min, double max);
  void setMinimum(double m) {scale().setGlobalMinimum(m);}
  void setMaximum(double m) {scale().setGlobalMaximum(m);}
  double visibleMinimum() const {return constScale().minimum();}
  double visibleMaximum() const {return constScale().maximum();}
  Rect boundingRect() const;

  void addStringLabel(QString lab, bool updadeScale=true);
  void removeStringLabels();
  QString stringLabels() const;
  void setStringLabels(QString s);

  double majorTicks() const {return constScale().majorTicks();}
  void setMajorTicks(double m) {scale().setMajorTicks(m);}
  double minorTicks() const {return constScale().minorTicks();}
  void setMinorTicks(double m) {scale().setMinorTicks(m);}
  bool showLabels() const {return _showLabels;}
  void setShowLabels(bool m) {_showLabels=m;}
  bool autoTicks() const {return _autoTicks;}
  void setAutoTicks(bool m);

  bool zoomEnabled() const {return _zoomEnabled;}
  void setZoomEnabled(bool m) {_zoomEnabled=m;}

  const DateTime& timeReference() const {return constScale().timeReference();}
  QString timeReferenceString() const {return timeReference().toString();}
  void setTimeReference(const DateTime& t) {scale().setTimeReference(t);}
  void setTimeReference(const QString& t);
  DateTime time(double val) const {return constScale().timeReference().shifted(val);}

  char numberType() const {return _numberType;}
  void setNumberType(char t) {_numberType=t;}
  QString numberTypeString() const;
  void setNumberType(QString t);

  int numberPrecision() const {return _numberPrecision;}
  void setNumberPrecision(int p) {_numberPrecision=p;}
  void setNumberPrecisionInversedScale(int np) {_numberPrecisionInversedScale=np;}
  int numberPrecisionInversedScale() const {return _numberPrecisionInversedScale;}
  int currentNumberPrecision() const;
  void setCurrentNumberPrecision(int p);
  int currentInversedNumberPrecision() const;
  void setCurrentInversedNumberPrecision(int p);

  void setUnitFactor(double s) {_unitFactor=s;}
  double unitFactor() const {return _unitFactor;}

  bool autoPrecision() const {return _autoPrecision;}
  void setAutoPrecision(bool p);

  void setScaleType(Scale::Type st);
  Scale::Type scaleType() const {return constScale().type();}
  QString scaleTypeString() const;
  void setScaleType(QString t);
  bool reversedScale() const {return constScale().isReversed();}
  void setReversedScale(bool b) {scale().setReversed(b);}
  bool isEffectivelyReversed() const {return constScale().isEffectivelyReversed();}

  const QString& title() const {return _title;}
  void setTitle(QString s) {_title=s;}
  void setTitleInversedScale(QString s) {_titleInversedScale=s;}
  const QString& titleInversedScale() const {return _titleInversedScale;}
  QString currentTitle() const;
  void setCurrentTitle(QString s);
  QString currentInversedTitle() const;
  void setCurrentInversedTitle(QString s);

  Orientation orientation() const {return _orientation;}
  void setOrientationBlocked(bool b) {_orientationBlocked=b;}
  void setOrientation(Orientation o);
  QString orientationString() const;
  void setOrientationString(QString o);

  QString fontString() const {return _font.toString();}
  void setFontString(QString fs) {_font.fromString(fs);}

  SizeType sizeType() const {return _sizeType;}
  void setSizeType(SizeType st) {_sizeType=st;}
  QString sizeTypeString() const;
  void setSizeType(QString st);

  double sizeInfo() const {return _sizeInfo;}
  void setSizeInfo(double s) {if(s>0.0) _sizeInfo=s;}
  double lineWeight() const {return _lineWeight;}
  void setLineWeight(double val) {_lineWeight=val;}
  double tickSize() const {return _tickSize;}
  void setTickSize(double val) {_tickSize=val;}

  int lineCount() const {return constScale().lineCount();}
  void setCurrentLine(int line);
  int currentLine() const;

  void setVisibleRange(double val1, double val2);
  void setVisibleRange(bool zoomed);
  inline bool isZoomed() const;

  int lastMousePosition() const {return _lastMousePosition;}
  bool isMouseTrack() const {return _mouseTrack;}
  // Print the axis
  void setPrintScale(int w, int h);
  void paint(QPainter& p, double dotpercm, int w, int h, bool mask);
  int printThickness(int length, double dotpercm, QPainter * p=nullptr);

  double toDouble(const QString& text, bool& ok) const;

  void addProperties(PropertyProxy * pp);
  void removeProperties(PropertyProxy * pp);
  virtual void properties(PropertyWidget * w) const;
  virtual void setProperty(uint wid, int pid, QVariant val);

  void setFrequency();
protected:
  virtual void mouseDoubleClickEvent(QMouseEvent * e);
  virtual void mousePressEvent(QMouseEvent * e);
  virtual void paintEvent(QPaintEvent * e);
  virtual void resizeEvent(QResizeEvent * e);

  virtual void xml_writeProperties(XML_WRITEPROPERTIES_ARGS) const {qobject_writeProperties(this, this, s, context);}
  virtual XMLMember xml_member(XML_MEMBER_ARGS) {return qobject_member(this, tag, attributes, context);}
  virtual bool xml_setProperty(XML_SETPROPERTY_ARGS) {return qobject_setProperty(this, memberID, tag, attributes, content, context);}
private:
  Scale& scale();
  const Scale& constScale() const;

  void setPositionString(QString p);
  void setZoomBlocked(bool m) {setZoomEnabled(!m);}

  void setScaleTransformation(int w, int h);
  void calculatePrecicion();

  void paintHorizontal(QPainter& p, double dotpercm, int w, int h, bool mask);
  void paintVertical(QPainter& p, double dotpercm, int w, int h, bool mask);
  // Used by paint() in Horizontal and Vertical axis
  inline void setPenFont(QPainter& p, bool mask, double dotpercm) const;
  QString label(int index, int numberPrecision, double unitFactor) const;
  QRectF textBoundingRect(QPainter * p, const QString& str, int length=INT_MAX, int flags=Qt::AlignLeft | Qt::AlignTop) const;

  // Graph content managed by this axis
  GraphContent * _content;

  Orientation _orientation;
  QVector<QString> * _stringLabels;
  char _numberType;
  int _numberPrecision;
  int _numberPrecisionInversedScale;
  double _unitFactor;
  Font _font;
  // Title
  QString _title;
  QString _titleInversedScale;

  // Print properties
  SizeType _sizeType;
  double _sizeInfo;
  // Line weight for printing (in mm)
  double _lineWeight;
  // Tick length for printing (in mm)
  double _tickSize;

  // Last mouse position for mouse tracking
  int _lastMousePosition;
  // Boolean values
  uint _autoPrecision:1;
  uint _showLabels:1;
  uint _zoomEnabled:1;
  uint _autoTicks:1;
  uint _mouseTrack:1;
  uint _orientationBlocked:1;

  QAction * _propertiesAction;

  static uint _tabScale, _tabFormat;
signals:
  void rangeChanged();
  void sizeChanged();
  void lengthChanged();
  void positionChanged();
  void orientationChanged();
  void contentChanged();
public slots:
  void setMouseTrack(bool t);
  void trackMouse (QPoint mouseP);
  void showProperties();
};

inline bool Axis::isZoomed() const
{
  TRACE;
  const Scale& s=constScale();
  return s.globalMinimum()<s.minimum() ||
         s.maximum()<s.globalMaximum();
}

} // namespace SciFigs

#endif // AXIS_H
