//
// Copyright (c) 2009 Froduald Kabanza and the Université de Sherbrooke.
// Use of this software is permitted for non-commercial research purposes, and
// it may be copied or applied only for that use. All copies must include this
// copyright message.
// 
// This is a research prototype that has not gone through intensive tests and
// is delivered as is. It may still contain bugs. Froduald Kabanza and the
// Université de Sherbrooke disclaim any responsibility for damage that may be
// caused by using it.
//
// Implementation: Simon Chamberland
// Project Manager: Froduald Kabanza
//

#ifndef _UTILS_H
#define _UTILS_H

#include "../../Utils/Functors.h"
#include "../../Utils/Utils.h"
#include <boost/functional/hash.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
//#include <boost/preprocessor/list/enum.hpp>
#include <boost/type_traits.hpp>
#include <boost/lexical_cast.hpp>
#include <vector>
#include <list>
#include <set>
#include <map>
#include <typeinfo>
#include <iostream>
#include <limits>
#undef min
#undef max

//------------------------------------------------------------------------------
// Macros
//------------------------------------------------------------------------------

#define THREAD_UNSAFE

#define TRACE_WRITE(level, options, message) \
if (level <= options.getTraceLevel()) \
  trace.write(message);

//------------------------------------------------------------------------------
// Functions
//------------------------------------------------------------------------------

template <class iterator>
std::string sequenceToString(iterator begin, iterator end)
{
  std::string s;
  for (; begin != end; ++begin)
  {
    if (!s.empty())
      s.append(" - ");
    s.append(toString(*begin));
  }
  return s;
}

template <class Iterator>
bool sequenceContainsNullValues(Iterator begin, Iterator end)
{
  for (; begin != end; ++begin)
  {
    if (*begin == NULL)
      return true;
  }
  return false;
}

std::string stringify(double x);

bool getPrimeNumber(size_t i, size_t& primeNumber);

struct HashCodeIfPointerType
{
  template <class T>
  static size_t getHashCode(const T& t)
  {
    return getHashCode(*t);
  }
};

struct HashCodeIfReferenceType
{
  template <class T>
  static size_t getHashCode(const T& t)
  {
    return t.getHashCode();
  }
};

template <class T>
inline size_t getHashCode(const T& t)
{
  return typename static_if_else<boost::is_pointer<T>::value, 
                                 HashCodeIfPointerType, 
                                 HashCodeIfReferenceType>::type::getHashCode(t);
}

template <class T>
inline size_t getHashCode(const T* const t)
{
  return getHashCode(*t);
}


template <class iterator>
size_t calculateSequenceHashCode(iterator begin, iterator end)
{
  // Could be implemented via boost::hash_range

  size_t hashCode = 0;
  size_t i = 0;
  for (; begin != end; ++begin)
  {
    size_t primeNumberI;
    getPrimeNumber(i++, primeNumberI);
    hashCode += (primeNumberI * getHashCode(*begin));
  }
  return hashCode;
}

//------------------------------------------------------------------------------
// Others
//------------------------------------------------------------------------------

template <class Enum>
struct EnumTraits
{
  // std::string description
  // std::pair<std::string, Enum> enumValues[];
};

template <class Enum>
Enum stringToEnum(const std::string& value)
{
  for (const std::pair<std::string, Enum>* p = EnumTraits<Enum>::values;
       p != EnumTraits<Enum>::values + sizeof(EnumTraits<Enum>::values) / sizeof(std::pair<std::string, Enum>);
       ++p)
  {
    if ((*p).first == value)
      return (*p).second;
  }
  throw std::runtime_error("Invalid " + EnumTraits<Enum>::description
                         + ": '" + value + "'");
}

template <class Enum>
const std::string& enumToString(Enum value)
{
  for (const std::pair<std::string, Enum>* p = EnumTraits<Enum>::values;
       p != EnumTraits<Enum>::values + sizeof(EnumTraits<Enum>::values) / sizeof(std::pair<std::string, Enum>);
       ++p)
  {
    if ((*p).second == value)
      return (*p).first;
  }
  throw std::runtime_error("Invalid " + EnumTraits<Enum>::description
                         + ": '" + boost::lexical_cast<std::string>(value) + "'");
}

class IntegerInterval
{
public:
  IntegerInterval(int start, size_t length)
    : m_start(start), m_length(length)
  {
  }

  int getStart() const
  {
    return m_start;
  }

  size_t getLength() const
  {
    return m_length;
  }

private:
  int m_start;
  size_t m_length;
};

//------------------------------------------------------------------------------
// Comparison/hashing
//------------------------------------------------------------------------------

template <class T>
class ExpHash : public boost::hash<T>
{
public:
  size_t operator()(const T* t) const
  {
    return t->getHashCode();
  }
};

template <class T>
class ExpEqual : public std::equal_to<T>
{
public:
  bool operator()(const T* t1, const T* t2) const
  {
    return t1->compareTo(*t2) == 0;
  }
};

template <class T>
class ExpCompare : public std::binary_function<const T*, const T*, bool>
{
public:
  bool operator()(const T* t1, const T* t2) const
  {
    return t1->compareTo(*t2) < 0;
  }
};

template <class T>
class PointerHash : public boost::hash<T>
{
public:
  size_t operator()(const T* t) const
  {
    return size_t(t);
  }
};

template <class T>
class PointerEqual : public std::equal_to<T>
{
public:
  bool operator()(const T* t1, const T* t2) const
  {
    return t1 == t2;
  }
};

template <class T>
class CustomHash : public boost::hash<T>
{};

template <>
class CustomHash<size_t> : public boost::hash<size_t>
{
public:
  size_t operator()(size_t n) const
  {
    size_t hashcode, add;
    hashcode = add = n;
    for (int i = 0; i < 2; ++i)
    {
      add *= add;
      hashcode += add;
    }
    return hashcode;
  }
};

#define COMPARE(e1, e2) \
  { \
    int c = funcCompare(e1, e2); \
    if (c != 0) \
      return c; \
  }

#define COMPARE_PTR(e1, e2) LESS_COMPARE(e1, e2)

#define LESS_COMPARE(e1, e2) \
  if (e1 < e2) return -1; \
  else if (e1 > e2) return 1;

// Default funcCompare methods 

struct ComparisonIfPointerType
{
  template <class T>
  static int compare(const T& e1, const T& e2)
  {
    return funcCompare(*e1, *e2);
  }
  template <class T>
  static int compare(const T* const e1, const T* const e2)
  {
    return funcCompare(*e1, *e2);
  }
};

struct ComparisonIfReferenceType
{
  template <class T>
  static int compare(const T& e1, const T& e2)
  {
    return e1.compareTo(e2);
  }
};

template <class T>
inline int funcCompare(const T& e1, const T& e2)
{
  return typename static_if_else<boost::is_pointer<T>::value, 
                                 ComparisonIfPointerType, 
                                 ComparisonIfReferenceType>::type::compare(e1, e2);
}

template <class T>
inline int funcCompare(const T* const e1, const T* const e2)
{
  return funcCompare(*e1, *e2);
}

// funcCompare specializations


template <>
inline int funcCompare<>(const std::type_info& t1, const std::type_info& t2)
{
  if (t1.before(t2))
    return -1;
  else if (t2.before(t1))
    return 1;
  else
    return 0;
}

template <>
inline int funcCompare<>(const int& e1, const int& e2)
{
  LESS_COMPARE(e1, e2);
  return 0;
}

template <>
inline int funcCompare<>(const size_t& e1, const size_t& e2)
{
  LESS_COMPARE(e1, e2);
  return 0;
}

template <>
inline int funcCompare<>(const bool& e1, const bool& e2)
{
  LESS_COMPARE(e1, e2);
  return 0;
}

template <>
inline int funcCompare<>(const double& e1, const double& e2)
{
  LESS_COMPARE(e1, e2);
  return 0;
}

template <>
inline int funcCompare<>(const float& e1, const float& e2)
{
  LESS_COMPARE(e1, e2);
  return 0;
}

template <class T, class U>
inline int funcCompare(const std::pair<T,U>& p1, const std::pair<T,U>& p2)
{
  COMPARE(p1.first, p2.first);
  COMPARE(p1.second, p2.second);
  return 0;
}

template <class Iterator>
inline int funcCompare(Iterator begin1, Iterator end1, Iterator begin2)
{
  for (; begin1 != end1; ++begin1, ++begin2)
  {
    COMPARE((*begin1), (*begin2));
  }
  return 0;
}

template <class T, class Allocator>
inline int funcCompare(const std::vector<T, Allocator>& v1, 
                       const std::vector<T, Allocator>& v2)
{
  COMPARE(v1.size(), v2.size());
  return funcCompare(v1.begin(), v1.end(), v2.begin());
}

template <class T, class Allocator>
inline int funcCompare(const std::list<T, Allocator>& v1, 
                       const std::list<T, Allocator>& v2)
{
  COMPARE(v1.size(), v2.size());
  return funcCompare(v1.begin(), v1.end(), v2.begin());
}

template <class Key, class Compare, class Allocator>
inline int funcCompare(const std::set<Key, Compare, Allocator>& s1, 
                       const std::set<Key, Compare, Allocator>& s2)
{
  COMPARE(s1.size(), s2.size());
  return funcCompare(s1.begin(), s1.end(), s2.begin());
}

template <class Key, class Compare, class Allocator>
inline int funcCompare(const std::multiset<Key, Compare, Allocator>& s1, 
                       const std::multiset<Key, Compare, Allocator>& s2)
{
  COMPARE(s1.size(), s2.size());
  return funcCompare(s1.begin(), s1.end(), s2.begin());
}

template <class Key, class T, class Compare, class Allocator>
inline int funcCompare(const std::map<Key, T, Compare, Allocator>& m1, 
                       const std::map<Key, T, Compare, Allocator>& m2)
{
  COMPARE(m1.size(), m2.size());
  return funcCompare(m1.begin(), m1.end(), m2.begin());
}

#endif
