//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2016 Richard S. Smith and collaborators.
//
// If you use MorphoGraphX in your work, please cite:
//   http://dx.doi.org/10.7554/eLife.05864
//
// MorphoGraphX is free software, and is licensed under under the terms of the 
// GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
// 
#ifndef MISC_H
#define MISC_H
/**
 * \file Misc.hpp
 *
 * Misc. definitions and utilities
 */
#include <Config.hpp>
#include <GL.hpp>

#include <Color.hpp>
#include <EdgeData.hpp>
#include <MGXViewer/qglviewer.h>
#include <Vector.hpp>
#include <VertexData.hpp>
#include <VVGraph.hpp>

#include <QDir>
#include <QList>
#include <QtCore>

namespace mgx 
{
  static const QString UM(QString::fromWCharArray(L"\xb5m"));             // um
  static const QString UM2(QString::fromWCharArray(L"\xb5m\xb2"));        // um^2
  static const QString UM3(QString::fromWCharArray(L"\xb5m\xb3"));        // um^3
  static const QString UM_1(QString::fromWCharArray(L"\xb5m\x207B\xb9")); // um^-1
  static const QString UM_2(QString::fromWCharArray(L"\xb5m\x207B\xb2")); // um^-2
  
  #ifdef _MSC_VER
  template class mgx_EXPORT Vertex<VertexData>;
  template class mgx_EXPORT Edge<EdgeData>;
  template class mgx_EXPORT VVGraph<VertexData, EdgeData>;
  #endif
  
  // Type of the VV graph
  typedef VVGraph<VertexData, EdgeData> vvGraph;
  
  // Type of a vertex
  typedef vvGraph::vertex_t vertex;
  
  // Type of an edge
  typedef vvGraph::edge_t edge;
  
  typedef Vector<2, int> Point2i;
  typedef Vector<3, GLuint> Point3GLui;
  typedef Vector<4, GLuint> Point4GLui;
  typedef Vector<3, GLubyte> Point3GLub;
  typedef Vector<4, GLubyte> Point4GLub;
  typedef Color<float> Colorf;
  typedef Vector<3, float> Point3f;
  
  /// Returns resource directory
  mgx_EXPORT QDir resourceDir();
  /// Returns user processes directory
  mgx_EXPORT QList<QDir> userProcessDirs();
  /// Returns processes directory
  mgx_EXPORT QList<QDir> processDirs();
  /// Returns the libraries directory
  mgx_EXPORT QDir libDir();
  /// Returns the includes directory
  mgx_EXPORT QDir includeDir();
  /// Returns the documentation directory
  mgx_EXPORT QDir docDir();
  
  /// Map unique colors to indices
  inline Point3GLub vMapColor(uint u)
  {
    u++;
    return (Point3GLub(u / (256 * 256), u / 256 % 256, u % 256));
  }
  
  /// Map unique colors to indices
  inline uint vMapColor(Point3GLub& p) {
    return ((int(p.x()) * 256 * 256 + int(p.y()) * 256 + int(p.z())) - 1);
  }
  
  /// Create representation of a string that can be written in a single line, without spaces
  inline QString shield(QString s)
  {
    s.replace("\\", "\\\\");
    s.replace(" ", "\\s");
    s.replace("\n", "\\n");
    s.replace("\t", "\\t");
    s.replace("\"", "\\\"");
    s.replace("\'", "\\\'");
    return s;
  }
  
  /// Retrieve a string that has been shielded with shield
  inline QString unshield(QString s)
  {
    s.replace("\\\'", "\'");
    s.replace("\\\"", "\"");
    s.replace("\\s", " ");
    s.replace("\\n", "\n");
    s.replace("\\t", "\t");
    s.replace("\\\\", "\\");
    return s;
  }
  
  /// Shield a string to send it to the python interpreter
  inline QString shield_python(QString s)
  {
    s.replace("\\", "\\\\");
    s.replace("\n", "\\n");
    s.replace("\t", "\\t");
    s.replace("\"", "\\\"");
    s.replace("\'", "\\\'");
    return s;
  }
  
  /// Retrieve a string that is retrieved from the python representation
  inline QString unshield_python(QString s)
  {
    s.replace("\\\'", "\'");
    s.replace("\\\"", "\"");
    s.replace("\\n", "\n");
    s.replace("\\t", "\t");
    s.replace("\\\\", "\\");
    return s;
  }

  // Maybe make a template?
  /// Convert a string to Point3d
  inline Point3d stringToPoint3d(const QString &s)
  {
    QStringList sl = s.split(" ");
    size_t sz = sl.size();
    if(sz > 3)
      sz = 3;
    Point3d result;
    for(size_t i = 0; i < sz; ++i)
      result[i] = sl[i].toDouble();
    return result; 
  }
  inline Point4d stringToPoint4d(const QString &s)
  {
    QStringList sl = s.split(" ");
    size_t sz = sl.size();
    if(sz > 4)
      sz = 4;
    Point4d result;
    for(size_t i = 0; i < sz; ++i)
      result[i] = sl[i].toDouble();
    return result; 
  }
  inline Point2f stringToPoint2f(const QString &s)
  {
    QStringList sl = s.split(" ");
    size_t sz = sl.size();
    if(sz > 2)
      sz = 2;
    Point2f result;
    for(size_t i = 0; i < sz; ++i)
      result[i] = sl[i].toFloat();
    return result; 
  }
  inline Point2i stringToPoint2i(const QString &s)
  {
    QStringList sl = s.split(" ");
    size_t sz = sl.size();
    if(sz > 2)
      sz = 2;
    Point2i result;
    for(size_t i = 0; i < sz; ++i)
      result[i] = sl[i].toInt();
    return result; 
  }
  inline Point2u stringToPoint2u(const QString &s)
  {
    QStringList sl = s.split(" ");
    size_t sz = sl.size();
    if(sz > 2)
      sz = 2;
    Point2u result;
    for(size_t i = 0; i < sz; ++i)
      result[i] = sl[i].toUInt();
    return result; 
  }
  inline Point2d stringToPoint2d(const QString &s)
  {
    QStringList sl = s.split(" ");
    size_t sz = sl.size();
    if(sz > 2)
      sz = 2;
    Point2d result;
    for(size_t i = 0; i < sz; ++i)
      result[i] = sl[i].toDouble();
    return result; 
  }
  inline Point3u stringToPoint3u(const QString &s)
  {
    QStringList sl = s.split(" ");
    size_t sz = sl.size();
    if(sz > 3)
      sz = 3;
    Point3u result;
    for(size_t i = 0; i < sz; ++i)
      result[i] = sl[i].toUInt();
    return result; 
  }

  inline bool isGuiThread()
  {
    if(QThread::currentThread() == QCoreApplication::instance()->thread())
      return true;
    return false;
  }

  class Guard
  {
  public:
    Guard(bool &lock) : _lock(lock)
    {
      if(not _lock) {
        _owner = true;
        _lock = true;
      } else {
        _owner = false;
      }
    }
    ~Guard()
    {
      if(_owner)
        _lock = false;
    }
    bool owner()
    {
      return _owner;
    }

  private:
    bool _owner;
    bool &_lock;
  };
}
#endif
