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

#ifndef GLOBAL_H
#define GLOBAL_H

#include <QtCore>

#include "QGpCoreToolsDLLExport.h"
#include "Color.h"

#ifdef HAS_OPEN_BLAS
#  ifdef HAS_OPEN_BLAS_UBUNTU
#     include <cblas-openblas.h>
#   else
#     include <openblas/cblas.h>
#  endif
#endif

// endl is ambiguous with Qt 5.15 and clang++ on MacOS Catalina.
// Qt::endl is now mandatory but...
// For Qt 5.11: QT_NAMESPACE is not defined, hence endl is not inside Qt namespace.
// Not checked for other releases
#if(QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
namespace Qt {
inline QTextStream &endl(QTextStream &s) {s << ::endl; return s;}
inline QTextStream &flush(QTextStream &s) {s << ::flush; return s;}
}
#endif

namespace QGpCoreTools {

  // Redefine my own assert (assert even if QT_NO_DEBUG is defined)
  #if !defined(ASSERT)
  #ifndef NO_ASSERT
  #  define ASSERT(cond) do {if(!(cond))qt_assert(#cond,__FILE__,__LINE__);} while(0)
  #else
  #  define ASSERT(cond)
  #endif
  #endif

  QGPCORETOOLS_EXPORT void skipOpenBlasMultiThreading();

  // With gcc 4.1.2, I have a lot of unecessary warnings with -Wuninitialized
  // Maybe gcc will do a better job in the future
  #define SAFE_UNINITIALIZED(var,value) var=value;

  template<typename T>
  inline void minMax(T v1, T v2, T v3, T& min, T& max)
  {
    if(v1<v2) {
      if(v2<v3) {
        min=v1;
        max=v3;
      } else if(v1<v3){
        min=v1;
        max=v2;
      } else {
        min=v3;
        max=v2;
      }
    } else {
      if(v1<v3) {
        min=v2;
        max=v3;
      } else if(v2<v3){
        min=v2;
        max=v1;
      } else {
        min=v3;
        max=v1;
      }
    }
  }

  // Usefull for compatibility with old names of properties in XML files
  #define DUMMY_PROPERTIES \
  public: \
    void setDummyProperty(const QString&) {} \
    void setDummyProperty(double) {} \
    void setDummyProperty(int) {} \
    QString dummyPropertyString() const {return QString();} \
    double dummyPropertyDouble() const {return 0.0;} \
    int dummyPropertyInt() const {return 0;}

  enum AxisType {XAxis, YAxis, ZAxis};
  Q_DECLARE_FLAGS(AxisTypes, AxisType)

  QGPCORETOOLS_EXPORT QString encodeToHtml(QString str);

  template<typename T>
  void unique(QList<T>& list)
  {
    if(!list.isEmpty()) {
      QList<T> newList;
      newList.append(list.first());
      int n=list.count();
      for(int i=1; i<n; i++) {
        if(!(list.at(i)==newList.last())) {
          newList.append(list.at(i));
        }
      }
      list=newList;
    }
  }

  template<typename T>
  void uniqueDeleteAll(QList<T>& list)
  {
    if(!list.isEmpty()) {
      QList<T> newList;
      newList.append(list.first());
      int n=list.count();
      for(int i=1; i<n; i++) {
        if(list.at(i)==newList.last()) {
          delete list.at(i);
        } else {
          newList.append(list.at(i));
        }
      }
      list=newList;
    }
  }

  template<typename T, typename Equal>
  void unique(QList<T>& list, Equal equal)
  {
    if(!list.isEmpty()) {
      QList<T> newList;
      newList.append(list.first());
      int n=list.count();
      for(int i=1; i<n; i++) {
        if(!equal(list.at(i), newList.last())) {
          newList.append(list.at(i));
        }
      }
      list=newList;
    }
  }

  template<typename T, typename Equal>
  void uniqueDeleteAll(QList<T>& list, Equal equal)
  {
    if(!list.isEmpty()) {
      QList<T> newList;
      newList.append(list.first());
      int n=list.count();
      for(int i=1; i<n; i++) {
        if(equal(list.at(i), newList.last())) {
          delete list.at(i);
        } else {
          newList.append(list.at(i));
        }
      }
      list=newList;
    }
  }

  template<typename T>
  void unique(QVector<T>& vector)
  {
    if(!vector.isEmpty()) {
      QVector<T> newVector;
      newVector.append(vector.first());
      int n=vector.count();
      for(int i=1; i<n; i++) {
        if(!(vector.at(i)==newVector.last())) {
          newVector.append(vector.at(i));
        }
      }
      vector=newVector;
    }
  }

  template<typename T>
  void uniqueDeleteAll(QVector<T>& vector)
  {
    if(!vector.isEmpty()) {
      QVector<T> newVector;
      newVector.append(vector.first());
      int n=vector.count();
      for(int i=1; i<n; i++) {
        if(vector.at(i)==newVector.last()) {
          delete vector.at(i);
        } else {
          newVector.append(vector.at(i));
        }
      }
      vector=newVector;
    }
  }

  template<typename T, typename Equal>
  void unique(QVector<T>& vector, Equal equal)
  {
    if(!vector.isEmpty()) {
      QVector<T> newVector;
      newVector.append(vector.first());
      int n=vector.count();
      for(int i=1; i<n; i++) {
        if(!equal(vector.at(i), newVector.last())) {
          newVector.append(vector.at(i));
        }
      }
      vector=newVector;
    }
  }

  template<typename T, typename Equal>
  void uniqueDeleteAll(QVector<T>& vector, Equal equal)
  {
    if(!vector.isEmpty()) {
      QVector<T> newVector;
      newVector.append(vector.first());
      int n=vector.count();
      for(int i=1; i<n; i++) {
        if(equal(vector.at(i), newVector.last())) {
          delete vector.at(i);
        } else {
          newVector.append(vector.at(i));
        }
      }
      vector=newVector;
    }
  }

  inline bool lessThanLocalAwareCompareString(const QString& s1, const QString& s2)
  {
    return QString::localeAwareCompare(s1, s2)<0;
  }

  // Return value of registerComparators is ignored because if the operators
  // are not available at compile time an error is issued.
  #define REGISTER_METATYPE(type) \
  class type##InitMetaType \
  { \
  public: \
    type##InitMetaType(); \
  }; \
  type##InitMetaType::type##InitMetaType() \
  { \
    qRegisterMetaType<type>(# type); \
    QMetaType::registerComparators<type>(); \
  } \
  static type##InitMetaType instance##type##InitMetaType;

} // namespace QGpCoreTools

#endif // GLOBAL_H
