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

#ifndef STRINGSECTION_H
#define STRINGSECTION_H

#include "Trace.h"
#include "Complex.h"
#include "DateTime.h"
#include "QGpCoreToolsDLLExport.h"

namespace QGpCoreTools {

class QGPCORETOOLS_EXPORT StringSection
{
public:
  inline StringSection();
  inline StringSection(const QString& s);
  inline StringSection(const QChar * s);
  inline StringSection(const QChar * s, int len);
  inline StringSection(const QChar * s1, const QChar * s2);
  inline StringSection(const StringSection& o);
  ~StringSection() {}
  inline StringSection& operator=(const StringSection& o);

  inline bool operator==(const QChar * o) const;
  inline bool operator!=(const QChar * o) const;
  bool operator<(const QChar * o) const {return compare(o)<0;}
  bool operator<=(const QChar * o) const {return compare(o)<=0;}
  bool operator>(const QChar * o) const {return compare(o)>0;}
  bool operator>=(const QChar * o) const {return compare(o)>=0;}
  inline int compare(const QChar * o) const;

  inline bool operator==(const StringSection& o) const;
  inline bool operator!=(const StringSection& o) const;
  bool operator<(const StringSection& o) const {return compare(o)<0;}
  bool operator<=(const StringSection& o) const {return compare(o)<=0;}
  bool operator>(const StringSection& o) const {return compare(o)>0;}
  bool operator>=(const StringSection& o) const {return compare(o)>=0;}
  int compare(const StringSection &o) const;

  inline bool operator==(const QString& o) const;
  inline bool operator!=(const QString& o) const;
  bool operator<(const QString& o) const {return compare(o)<0;}
  bool operator<=(const QString& o) const {return compare(o)<=0;}
  bool operator>(const QString& o) const {return compare(o)>0;}
  bool operator>=(const QString& o) const {return compare(o)>=0;}
  inline int compare(const QString& o) const;

  QChar operator[] (int i) const {return _str[i];}
  void operator++() {_str++; if(_size>0) _size--;}
  void operator+=(int l) {_str+=l; if(_size>=l) _size-=l; else _size=0;}
  void operator--() {_str--; _size++;}

  bool isValid() const {return _str;}
  bool isEmpty() const {return _size==0;}
  const QChar * data() const {return _str;}
  const QChar * begin() const {return _str;}
  const QChar * end() const {return _str+_size;}
  int size() const {return _size;}

  void shrink(int nChar) {if(nChar>=_size) _size=0; else _size-=nChar;}
  StringSection& trimmed();
  void setInvalid() {_str=nullptr; _size=0;}
  void set(const QString& s) {_str=s.data(); _size=s.size();}
  void set(const QChar * s, int size) {_str=s; _size=size;}
  void setEnd(const QChar * e) {_size=static_cast<int>(e-_str); if(_size<0) _size=0;}
  void setSize(int size) {_size=size;}

  bool toBool(bool * ok=nullptr) const;
  int toInt(bool * ok=nullptr, int base=10) const;
  uint toUInt(bool * ok=nullptr, int base=10) const;
  qlonglong toLongLong(bool * ok=nullptr, int base=10) const;
  double toDouble(bool * ok=nullptr) const;
  inline Complex toComplex() const;
  inline Complex toComplex(bool * ok) const;
  DateTime toTime(const QString& format, bool * ok=nullptr) const;

  QString toString() const {return isValid() ? QString(_str, _size) : QString();}
  void appendTo(QString& str) const {str.insert(str.size(), _str, _size);}

  // Force conversion to a type given by a fake argument (used by XMLMap)
  bool to(bool, bool * ok=nullptr) const {return toBool(ok);}
  QString to(QString) const {return toString();}
  int to(int, bool * ok=nullptr) const {return toInt(ok);}
  uint to(uint, bool * ok=nullptr) const {return toUInt(ok);}
  qlonglong to(qlonglong, bool * ok=nullptr) const {return toLongLong(ok);}
  double to(double, bool * ok=nullptr) const {return toDouble(ok);}
  Complex to(const Complex&, bool * ok=nullptr) const {return toComplex(ok);}
  DateTime to(const DateTime&, const QString& format, bool * ok=nullptr) const {return toTime(format, ok);}

  inline StringSection left(int n) const;
  inline StringSection right(int n) const;
  inline StringSection mid(int position, int n) const;

  const QChar * find(QChar c) const;
  qint64 find(const StringSection target, bool reverse) const;
  bool contains(const StringSection target) const {return find(target, false)>-1;}
  bool contains(QChar target) const {return find(StringSection(&target, 1), false)>-1;}
  inline bool beginWith(const QString& o) const;
  inline bool endWith(const QString& o) const;
  bool beginWith(const StringSection& o) const;
  bool endWith(const StringSection& o) const;

  StringSection readLine(const QChar *& ptr) const;
  bool nextLine(const QChar *& ptr, const QString& sep=defaultSeparators) const;
  StringSection nextField(const QChar *& ptr, const QString& sep=defaultSeparators, bool skipEmpty=true) const;
  bool nextNumber(int& n, const QChar *& ptr, const QString& sep=defaultSeparators, bool skipEmpty=true) const;
  bool nextNumber(double& n, const QChar *& ptr, const QString& sep=defaultSeparators, bool skipEmpty=true) const;
  bool nextNumber(Complex& n, const QChar *& ptr, const QString& sep=defaultSeparators, bool skipEmpty=true) const;

  static int size(const QChar * str);
  static int compare(const QChar * str1, const QChar * str2, int n);
  static const QString defaultSeparators;
  static const StringSection null;
private:
  inline void skipSingleQuoted(const QChar *&ptr, const QChar *ptrE) const;
  inline void skipDoubleQuoted(const QChar *&ptr, const QChar *ptrE) const;

  const QChar * _str;
  int _size;
};

QGPCORETOOLS_EXPORT uint qHash(StringSection key);

inline StringSection::StringSection()
{
  _str=nullptr;
  _size=0;
}

inline StringSection::StringSection(const QString& s)
{
  _str=s.data();
  _size=s.size();
}

inline StringSection::StringSection(const QChar * s)
{
  _str=s;
  _size=size(s);
}

inline StringSection::StringSection(const QChar * s1, const QChar * s2)
{
  _str=s1;
  setEnd(s2);
}

inline StringSection::StringSection(const QChar * s, int len)
{
  _str=s;
  _size=len;
}

inline StringSection::StringSection(const StringSection& o)
{
  *this=o;
}

inline StringSection& StringSection::operator=(const StringSection& o)
{
  _str=o._str;
  _size=o._size;
  return *this;
}

inline int StringSection::compare(const QChar * o) const
{
  StringSection os(o);
  return compare(os);
}

inline int StringSection::compare(const QString& o) const
{
  StringSection os(o);
  return compare(os);
}

inline bool StringSection::operator==(const QChar * o) const
{
  if(_size!=size(o)) return false;
  return compare(o, _str, _size)==0;
}

inline bool StringSection::operator==(const StringSection& o) const
{
  if(_size!=o._size) return false;
  return compare(o._str, _str, _size)==0;
}

inline bool StringSection::operator==(const QString& o) const
{
  if(_size!=o.size()) return false;
  return compare(o.data(), _str, _size)==0;
}

inline bool StringSection::operator!=(const QChar * o) const
{
  if(_size!=size(o)) return true;
  return compare(o, _str, _size)!=0;
}

inline bool StringSection::operator!=(const StringSection& o) const
{
  if(_size!=o._size) return true;
  return compare(o._str, _str, _size)!=0;
}

inline bool StringSection::operator!=(const QString& o) const
{
  if(_size!=o.size()) return true;
  return compare(o.data(), _str, _size)!=0;
}

inline bool StringSection::beginWith(const StringSection& o) const
{
  if(_size < o._size) return false;
  return compare(o._str, _str, o.size())==0;
}

inline bool StringSection::endWith(const StringSection& o) const
{
  if(_size < o._size) return false;
  return compare(o._str, _str+_size-o._size, o._size)==0;
}

inline bool StringSection::beginWith(const QString& o) const
{
  if(_size < o.size()) return false;
  return compare(o.data(), _str, o.size())==0;
}

inline bool StringSection::endWith(const QString& o) const
{
  if(_size < o.size()) return false;
  return compare(o.data(), _str+_size-o.size(), o.size())==0;
}

inline StringSection StringSection::left(int n) const
{
  if(n>_size) n=_size;
  return StringSection(_str, n);
}

inline StringSection StringSection::right(int n) const
{
  if(n>_size) n=_size;
  return StringSection(_str+_size-n, n);
}

inline StringSection StringSection::mid(int position, int n) const
{
  if(position>=_size) return StringSection::null;
  int ns=_size-position;
  if(n<ns) ns=n;
  return StringSection(_str+position, ns);
}

inline Complex StringSection::toComplex() const
{
  Complex c;
  c.fromString(*this);
  return c;
}

inline Complex StringSection::toComplex(bool * ok) const
{
  Complex c;
  bool tok=c.fromString(*this);
  if(ok) *ok=tok;
  return c;
}

} // namespace QGpCoreTools

#endif // STRINGSECTION_H
