#pragma once
#include "../../Utils/Functors.h"
#include "../../Utils/Utils.h"
#include <BWAPI.h>
#include <hash_map>
#include <boost/tuple/tuple.hpp>
#include <boost/utility.hpp>

const double SQRT_OF_TWO = std::sqrt(2.0);

// Useless #define, only there to mark where we use the fact that the unit pointers do not change to uniquely identify enemy units
#define INVARIANT_UNIT_POINTERS_ASSUMPTION

/**
 * @brief Extracts the class name from the type-info record, which may initially include outer classes and namespaces.
 * @param t The type-info record to extract the class name from.
 * @return The simple class name corresponding to this type-info record.
 */
inline std::string extractSimpleClassName(const type_info& t)
{
  static const std::string SeparatorString("::");
  static const std::string ClassString("class ");

  std::string fullName(t.name());
  size_t begin = fullName.find_last_of(SeparatorString);
  if (begin == std::string::npos)
  {
    if ( (begin = fullName.find_first_of(ClassString)) == std::string::npos)
      begin = 0;
    else
      begin += ClassString.length();
  }
  else
    begin += (SeparatorString.length() - 1);
  return fullName.substr(begin);
}

template<class T>
std::ostream& operator<<(std::ostream& out, const std::list<T>& lst)
{
  std::copy(lst.begin(), lst.end(), std::ostream_iterator<T>(out, "; "));
  return out;
}

/**
 * @brief Base unit wrapper class, which offers conversion operators.
 */
class UnitWrapperBase
{
public:
  struct extract_unit : std::unary_function<UnitWrapperBase,BWAPI::Unit*>
  {
    BWAPI::Unit* operator()(const UnitWrapperBase& unitWrapper) const
    {
      return unitWrapper.getUnit();
    }
  };

  UnitWrapperBase(BWAPI::Unit* unit)
    : m_unit(unit) {}
  BWAPI::Unit* getUnit() const
  {
    return m_unit;
  }
  operator BWAPI::Unit*() const 
  { 
    return m_unit;
  }
  BWAPI::Unit* operator->() const
  {
    return m_unit;
  }
  BWAPI::Unit* operator->()
  {
    return m_unit;
  }
  BWAPI::Unit& operator*() const
  {
    return *m_unit;
  }
  BWAPI::Unit& operator*()
  {
    return *m_unit;
  }
  friend bool operator< (const UnitWrapperBase& wrapper1, const UnitWrapperBase& wrapper2)
  {
    return wrapper1.m_unit < wrapper2.m_unit;
  }
protected:
  BWAPI::Unit* m_unit;
};
struct UnitWrapperBaseCompare
{
  bool operator()(const UnitWrapperBase& unit1, const  UnitWrapperBase& unit2) const
  {
    return unit1.getUnit() < unit2.getUnit();
  }
};
struct UnitWrapperBaseHashCompare : public stdext::hash_compare<UnitWrapperBase, UnitWrapperBaseCompare>
{
  size_t operator()(const UnitWrapperBase& unit) const
  {
    return size_t(unit.getUnit());
  }
  bool operator()(const UnitWrapperBase& unit1, const  UnitWrapperBase& unit2) const
  {
    return UnitWrapperBaseCompare()(unit1, unit2);
  }
};

/**
 * @brief Iterator acting either as one type of iterator or another type of iterator given the particular
 *        type it is instantiated with.
 * @tparam FirstIterator The first possible type of iterator.
 * @tparam SecondIterator The second possible type of iterator.
 */
template <class FirstIterator, class SecondIterator>
class general_iterator : public std::iterator<std::forward_iterator_tag, 
                                              typename FirstIterator::value_type,
                                              std::ptrdiff_t,
                                              typename FirstIterator::pointer,
                                              typename FirstIterator::reference>
{
  static_assert<boost::is_same<typename FirstIterator::value_type, typename SecondIterator::value_type>::value> nested_iterators_must_have_same_value_type;
  static_assert<boost::is_same<typename FirstIterator::pointer, typename SecondIterator::pointer>::value> nested_iterators_must_have_same_pointer_type;
  static_assert<boost::is_same<typename FirstIterator::reference, typename SecondIterator::reference>::value> nested_iterators_must_have_same_reference_type;
public:
  general_iterator(FirstIterator it)
    : m_firstIt(it)
    , m_useFirst(true)
  {}
  general_iterator(SecondIterator it)
    : m_secondIt(it)
    , m_useFirst(false)
  {}
  template <class OtherFirstIterator, class OtherSecondIterator>
  general_iterator(const general_iterator<OtherFirstIterator, OtherSecondIterator>& other)
    : m_firstIt(other.m_firstIt)
    , m_secondIt(other.m_secondIt)
    , m_useFirst(other.m_useFirst)
  {}
  reference operator*() const
  {
    if (m_useFirst){
      return *m_firstIt;
    }
    else {
      return *m_secondIt;
    }
  }
  general_iterator& operator++()
  {
    if (m_useFirst){
      ++m_firstIt;
    }
    else {
      ++m_secondIt;
    }
    return *this;
  }
  general_iterator operator++(int)
  {
    general_iterator copy(*this);
    ++(*this);
    return copy;
  }
  bool operator== (const general_iterator& other) const
  {
    if (m_useFirst){
      return m_firstIt == other.m_firstIt;
    }
    else {
      return m_secondIt == other.m_secondIt;
    }
  }
  bool operator!= (const general_iterator& other) const
  {
    if (m_useFirst){
      return m_firstIt != other.m_firstIt;
    }
    else {
      return m_secondIt != other.m_secondIt;
    }
  }
  general_iterator& operator=(const general_iterator& other)
  {
    m_firstIt = other.m_firstIt;
    m_secondIt = other.m_secondIt;
    m_useFirst = other.m_useFirst;
    return *this;
  }
private:
  template <class OtherFirstIterator, class OtherSecondIterator> friend class general_iterator;
  FirstIterator m_firstIt;
  SecondIterator m_secondIt;
  bool m_useFirst;
};

/**
 * @brief Iterator merging multiple sub-sequences represented by other iterators.
 * @tparam IteratorPairsTuple The type of sub-sequences, which must be of the form boost::tuple<std::pair<begin1, end1>, std::pair<begin2, end2>, ...>
 * @tparam ValueType The type of value returned by this iterator. All sub-sequences must return values convertible to this type.
 */
template <class IteratorPairsTuple, class ValueType>
class concatenate_iterators : public std::iterator<std::forward_iterator_tag, ValueType>
{
public:
  concatenate_iterators(IteratorPairsTuple iteratorPairsTuple)
    : m_iteratorPairsTuple(iteratorPairsTuple)
    , m_currentIterator(0)
  {
    ForEach<0>(*this).findNext();
  }
  value_type operator*() const
  {
    return *ForEachConst<0>(*this);
  }
  concatenate_iterators& operator++()
  {
    ++ForEach<0>(*this);
    ForEach<0>(*this).findNext();
    return *this;
  }
  concatenate_iterators operator++(int)
  {
    concatenate_iterators copy(*this);
    ++(*this);
    return copy;
  }
  bool operator== (const concatenate_iterators& other) const
  {
    return m_currentIterator == other.m_currentIterator
        && ForEachConst<0>(*this) == other;
  }
  bool operator!= (const concatenate_iterators& other) const
  {
    return m_currentIterator != other.m_currentIterator
        || ForEachConst<0>(*this) != other;
  }
  concatenate_iterators& operator=(const concatenate_iterators& other)
  {
    m_currentIterator = other.m_currentIterator;
    m_iteratorPairsTuple = other.m_iteratorPairsTuple;
    return *this;
  }
private:
  template <size_t i>
  struct ForEachConst
  {
    ForEachConst(const concatenate_iterators& it)
      : m_it(it)
    {}
    bool operator==(const concatenate_iterators& other) const
    {
      return m_it.m_iteratorPairsTuple.get<i>().first == other.m_iteratorPairsTuple.get<i>().first 
          && m_it.m_iteratorPairsTuple.get<i>().second == other.m_iteratorPairsTuple.get<i>().second
          && ForEachConst<i+1>(m_it) == other;
    }
    bool operator!=(const concatenate_iterators& other) const
    {
      return m_it.m_iteratorPairsTuple.get<i>().first != other.m_iteratorPairsTuple.get<i>().first 
          || m_it.m_iteratorPairsTuple.get<i>().second != other.m_iteratorPairsTuple.get<i>().second
          || ForEachConst<i+1>(m_it) != other;
    }
    value_type operator*() const
    {
      if (i == m_it.m_currentIterator)
        return *m_it.m_iteratorPairsTuple.get<i>().first;
      else
        return *ForEachConst<i+1>(m_it);
    }
  private:
    const concatenate_iterators& m_it;
  };
  template <>
  struct ForEachConst<boost::tuples::length<IteratorPairsTuple>::value>
  {
    ForEachConst(const concatenate_iterators&){}
    bool operator==(const concatenate_iterators&) const { return true; }
    bool operator!=(const concatenate_iterators&) const { return false; }
    value_type operator*() const { throw std::runtime_error("Internal error: invalid current iterator"); }
  };

  template <size_t i>
  struct ForEach
  {
    ForEach(concatenate_iterators& it)
      : m_it(it)
    {}
    void operator++()
    {
      if (i == m_it.m_currentIterator)
        ++m_it.m_iteratorPairsTuple.get<i>().first;
      else
        ++ForEach<i+1>(m_it);
    }
    void findNext()
    {
      if (i < m_it.m_currentIterator)
        ForEach<i+1>(m_it).findNext();
      else if (m_it.m_iteratorPairsTuple.get<i>().first == m_it.m_iteratorPairsTuple.get<i>().second)
        ForEach<i+1>(m_it).findNext();
      else
        m_it.m_currentIterator = i;
    }
  private:
    concatenate_iterators& m_it;

    //prevent the use of the assignement operator
    ForEach& operator=(const ForEach&);
  };
  template <>
  struct ForEach<boost::tuples::length<IteratorPairsTuple>::value>
  {
    ForEach(concatenate_iterators& it)
      : m_it(it)
    {}
    void operator++() { throw std::runtime_error("Internal error: invalid current iterator"); }
    void findNext() { m_it.m_currentIterator = boost::tuples::length<IteratorPairsTuple>::value; }
  private:
    concatenate_iterators& m_it;

    //prevent the use of the assignement operator
    ForEach& operator=(const ForEach&);
  };

  size_t m_currentIterator;
  IteratorPairsTuple m_iteratorPairsTuple;
};
