#pragma once
#include "../../../PerceptualState/World/SparObject.h"

class TechWrapper;
class SparObject;

class TechSet
{
private:
  struct TechWrapper
  {
    TechWrapper(SparObject* tech, unsigned int priority)
      : m_tech(tech)
      , m_priority(priority)
    {}
    SparObject* m_tech;
    unsigned int m_priority;
    //stdext::hash_map<SparObject*, TechWrapper>::iterator m_posInTechs;
    //std::multimap<unsigned int, TechWrapper*>::iterator m_posInTechsByPriority;
  };
  struct extract_tech : std::unary_function<const TechWrapper*, SparObject*>
  {
    SparObject* operator()(const TechWrapper* wrapper) const
    {
      return wrapper->m_tech;
    }
  };

public:
  TechSet() {}
  TechSet(const TechSet& other)
  {
    for (stdext::hash_map<SparObject*, TechWrapper>::const_iterator it = other.m_techs.begin(); it != other.m_techs.end(); ++it)
    {
      add(it->first, it->second.m_priority);
    }
  }

  bool add(SparObject* tech, unsigned int priority)
  {
    std::pair<stdext::hash_map<SparObject*, TechWrapper>::iterator, bool> resultInTechs = 
      m_techs.insert(std::make_pair(tech, TechWrapper(tech, priority)));
    if (resultInTechs.second)
    {
      TechWrapper& wrapper = resultInTechs.first->second;

      std::multimap<unsigned int, TechWrapper*>::iterator posInTechsByPriority = 
        m_techsByPriority.insert(std::make_pair(priority, &wrapper));

      //wrapper.m_posInTechs = resultInTechs.first;
      //wrapper.m_posInTechsByPriority = posInTechsByPriority;
    }

    return resultInTechs.second;
  }

  size_t size() const
  {
    assert(m_techs.size() == m_techsByPriority.size());
    return m_techs.size();
  }

  bool empty() const
  {
    assert(m_techs.empty() == m_techsByPriority.empty());
    return m_techs.empty();
  }

  typedef boost::transform_iterator<composite_functor<extract_tech, extract_pair_second<std::multimap<unsigned int, TechWrapper*>::const_iterator::value_type>>,
                                    std::multimap<unsigned int, TechWrapper*>::const_iterator>
          const_iterator;
  const_iterator begin() const { return const_iterator(m_techsByPriority.begin()); }
  const_iterator end() const { return const_iterator(m_techsByPriority.end()); }

  TechSet& operator=(const TechSet& other)
  {
    m_techs.clear();
    m_techsByPriority.clear();
    for (stdext::hash_map<SparObject*, TechWrapper>::const_iterator it = other.m_techs.begin(); it != other.m_techs.end(); ++it)
    {
      add(it->first, it->second.m_priority);
    }
    return *this;
  }

  bool operator==(const TechSet& other) const
  {
    return m_techsByPriority == other.m_techsByPriority;
  }
  bool operator!=(const TechSet& other) const
  {
    return m_techsByPriority != other.m_techsByPriority;
  }

private:
  stdext::hash_map<SparObject*, TechWrapper> m_techs;
  std::multimap<unsigned int, TechWrapper*> m_techsByPriority;
};