#pragma once
#include "../../../../Config/Config.h"
#include "../../../../Utils/Component.h"
#include "../../../../Utils/Functors.h"
#include "../../../../Utils/RaiseEventTask.h"
#include "../../../../BWAPI_Addon.h"
#include "../../../UnitRecord/UnitRecord.h"
#include "../StaticLocation.h"

#include <boost/iterator/transform_iterator.hpp>
#include <boost/iterator/filter_iterator.hpp>
#include <hash_map>

class Map;

/**
 * @brief Location which regularly updates its list of units, allowing one to access these units in near-constant time.
 */
class AutoPopulatedLocation : public StaticLocation
{
protected:
  /**
   * @brief List of tasks which will be executed when said list goes out of scope.
   */
  class RaiseEventTaskList : public Component
  {
  public:
    RaiseEventTaskList(const Component& owner)
      : m_owner(owner)
    {}
    ~RaiseEventTaskList()
    {
      for (std::list<RaiseEventTask*>::const_iterator it = m_deferredEvents.begin(); it != m_deferredEvents.end(); ++it)
      {
        (*it)->run();
        delete *it;
      }
    }
    template <class Arguments>
    void add(Event<Arguments>& ev, const Arguments& arguments)
    {
      m_deferredEvents.push_back(RaiseEventTask::instantiate(ev, arguments));
    }
  protected:
    virtual void _output(std::ostream& out) const
    {
      out << "RaiseEvent task list (owner " << m_owner << ")";
    }

    const Component& m_owner;
    std::list<RaiseEventTask*> m_deferredEvents;

  private:
    RaiseEventTaskList(const RaiseEventTaskList&);

    //const member prevent the use of the assignment operator
    RaiseEventTaskList& operator=(const RaiseEventTaskList&);
  };

  /**
   * @brief Stores data alongside the actual unit.
   */
  struct PlayerData;
  class UnitWrapper
  {
  public:
    UnitWrapper(UnitRecord* unit)
      : m_unit(unit)
      , m_nb(0)
      , m_playerData(NULL)
    {}
    UnitRecord* getUnit() { return m_unit; }
    const UnitRecord* getUnit() const { return m_unit; }

    /**
     * @brief Unit record stored in this wrapper.
     */
    UnitRecord* const m_unit;
    /**
     * @brief The number of times this unit was stored in sub-locations (always 1 for primitive locations).
     */
    size_t m_nb;
    /**
     * @brief The player last known to be controlling the unit.
     */
    PlayerData* m_playerData;

    stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::const_iterator m_posInUnits;
    UnitRecord::DataID m_dataID;

  private :
    //Const datamember prevent the use of the assigment operator
    UnitWrapper& operator=(const UnitWrapper&);
  };
  
  struct extract_unit : std::unary_function<UnitWrapper, UnitRecord*>
  {
    UnitRecord* operator()(UnitWrapper& unitWrapper) const
    {
      return unitWrapper.getUnit();
    }
  };
  
  struct extract_unit_const : std::unary_function<UnitWrapper, const UnitRecord*>
  {
    extract_unit_const() {}
    extract_unit_const(const extract_unit&) {}

    const UnitRecord* operator()(const UnitWrapper& unitWrapper) const
    {
      return unitWrapper.getUnit();
    }
  };
  struct WrapperIsBuilding
  {
    bool operator()(const UnitWrapper& wrapper) const
    {
      return wrapper.getUnit()->getType().isBuilding();
    }
  };

  typedef boost::transform_iterator<extract_pair_second<stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::iterator::value_type>, 
                                    stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::iterator> 
          unit_wrapper_iterator;
  typedef boost::filter_iterator<WrapperIsBuilding, unit_wrapper_iterator>
          building_wrapper_iterator;
  typedef boost::transform_iterator<extract_pair_second_const<stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::const_iterator::value_type>, 
                                    stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::const_iterator> 
          unit_wrapper_const_iterator;
  typedef boost::filter_iterator<WrapperIsBuilding, unit_wrapper_const_iterator>
          building_wrapper_const_iterator;

public:
  AutoPopulatedLocation();
  virtual ~AutoPopulatedLocation()
  {
  }

  typedef boost::transform_iterator<extract_unit,
                                    unit_wrapper_iterator>
          unit_iterator;
  typedef unit_iterator
          unit_type_iterator;
  typedef boost::transform_iterator<extract_unit, 
                                    building_wrapper_iterator>
          building_iterator;
  typedef boost::transform_iterator<extract_unit_const,
                                    unit_wrapper_const_iterator>
          unit_const_iterator;
  typedef unit_const_iterator
          unit_type_const_iterator;
  typedef boost::transform_iterator<extract_unit_const, building_wrapper_const_iterator>
          building_const_iterator;

  void addSubscriber(AutoPopulatedLocation* subscriber) const
  {
    m_subscribers.push_back(subscriber);
  }

#pragma region Own_Units

  std::pair<unit_iterator, unit_iterator> 
    getOwnUnits()
  {
    return std::make_pair(unit_iterator(unit_wrapper_iterator(getPlayerData(BWAPI::Broodwar->self()).m_units.begin())),
                          unit_iterator(unit_wrapper_iterator(getPlayerData(BWAPI::Broodwar->self()).m_units.end())));
  }

  std::pair<unit_const_iterator, unit_const_iterator> 
    getOwnUnits() const
  {
    return std::make_pair(unit_const_iterator(unit_wrapper_const_iterator(getPlayerData(BWAPI::Broodwar->self()).m_units.begin())),
                          unit_const_iterator(unit_wrapper_const_iterator(getPlayerData(BWAPI::Broodwar->self()).m_units.end())));
  }

  std::pair<unit_type_iterator, unit_type_iterator> 
    getOwnUnits(BWAPI::UnitType type)
  {
    std::pair<stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::iterator, stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::iterator> iterators = getPlayerData(BWAPI::Broodwar->self()).m_units.equal_range(type);
    return std::make_pair(unit_type_iterator(unit_wrapper_iterator(iterators.first)), 
                          unit_type_iterator(unit_wrapper_iterator(iterators.second)));
  }

  std::pair<unit_type_const_iterator, unit_type_const_iterator> 
    getOwnUnits(BWAPI::UnitType type) const
  {
    std::pair<stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::const_iterator, stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::const_iterator> iterators = getPlayerData(BWAPI::Broodwar->self()).m_units.equal_range(type);
    return std::make_pair(unit_type_const_iterator(unit_wrapper_const_iterator(iterators.first)), 
                          unit_type_const_iterator(unit_wrapper_const_iterator(iterators.second)));
  }

  std::pair<building_iterator, building_iterator>
    getOwnBuildings()
  {
    std::pair<building_wrapper_iterator, building_wrapper_iterator> ownWrappers = getOwnBuildingWrappers();
    return std::make_pair(building_iterator(ownWrappers.first), 
                          building_iterator(ownWrappers.second));
  }

  std::pair<building_const_iterator, building_const_iterator>
    getOwnBuildings() const
  {
    std::pair<building_wrapper_const_iterator, building_wrapper_const_iterator> ownWrappers = getOwnBuildingWrappers();
    return std::make_pair(building_const_iterator(ownWrappers.first), 
                          building_const_iterator(ownWrappers.second));
  }

  /**
   * @return The number of own units, either accessible or in the fog of war, present in this location.
   */
  size_t getNbOwnUnits() const
  {
    return getPlayerData(BWAPI::Broodwar->self()).m_units.size();
  }

  /**
   * @param type The type of unit.
   * @return The number of own units of this type, either accessible or in the fog of war, present in this location.
   */
  size_t getNbOwnUnits(BWAPI::UnitType type) const
  {
    return getPlayerData(BWAPI::Broodwar->self()).m_units.count(type);
  }

  /**
   * @return The number of own buildings, either accessible or in the fog of war, present in this location.
   */
  size_t getNbOwnBuildings() const
  {
    std::pair<building_const_iterator, building_const_iterator> ownBuildings = getOwnBuildings();
    return std::distance(ownBuildings.first, ownBuildings.second);
  }

#pragma endregion

#pragma region Enemy_Units

  std::pair<unit_iterator, unit_iterator> 
    getEnemyUnits()
  {
    return std::make_pair(unit_iterator(unit_wrapper_iterator(getPlayerData(BWAPI::Broodwar->enemy()).m_units.begin())),
                          unit_iterator(unit_wrapper_iterator(getPlayerData(BWAPI::Broodwar->enemy()).m_units.end())));
  }

  std::pair<unit_const_iterator, unit_const_iterator> 
    getEnemyUnits() const
  {
    return std::make_pair(unit_const_iterator(unit_wrapper_const_iterator(getPlayerData(BWAPI::Broodwar->enemy()).m_units.begin())),
                          unit_const_iterator(unit_wrapper_const_iterator(getPlayerData(BWAPI::Broodwar->enemy()).m_units.end())));
  }

  std::pair<unit_type_iterator, unit_type_iterator> 
    getEnemyUnits(BWAPI::UnitType type)
  {
    std::pair<stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::iterator, stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::iterator> iterators = getPlayerData(BWAPI::Broodwar->enemy()).m_units.equal_range(type);
    return std::make_pair(unit_type_iterator(unit_wrapper_iterator(iterators.first)), 
                          unit_type_iterator(unit_wrapper_iterator(iterators.second)));
  }

  std::pair<unit_type_const_iterator, unit_type_const_iterator> 
    getEnemyUnits(BWAPI::UnitType type) const
  {
    std::pair<stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::const_iterator, stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::const_iterator> iterators = getPlayerData(BWAPI::Broodwar->enemy()).m_units.equal_range(type);
    return std::make_pair(unit_type_const_iterator(unit_wrapper_const_iterator(iterators.first)), 
                          unit_type_const_iterator(unit_wrapper_const_iterator(iterators.second)));
  }

  std::pair<building_iterator, building_iterator>
    getEnemyBuildings()
  {
    std::pair<building_wrapper_iterator, building_wrapper_iterator> enemyWrappers = getEnemyBuildingWrappers();
    return std::make_pair(building_iterator(enemyWrappers.first), 
                          building_iterator(enemyWrappers.second));
  }

  std::pair<building_const_iterator, building_const_iterator>
    getEnemyBuildings() const
  {
    std::pair<building_wrapper_const_iterator, building_wrapper_const_iterator> enemyWrappers = getEnemyBuildingWrappers();
    return std::make_pair(building_const_iterator(enemyWrappers.first), 
                          building_const_iterator(enemyWrappers.second));
  }

  /**
   * @return The number of enemy units, either accessible or in the fog of war, present in this location.
   */
  size_t getNbEnemyUnits() const
  {
    return getPlayerData(BWAPI::Broodwar->enemy()).m_units.size();
  }

  /**
   * @param type The type of unit.
   * @return The number of enemy units of this type, either accessible or in the fog of war, present in this location.
   */
  size_t getNbEnemyUnits(BWAPI::UnitType type) const
  {
    return getPlayerData(BWAPI::Broodwar->enemy()).m_units.count(type);
  }

  /**
   * @return The number of enemy buildings, either accessible or in the fog of war, present in this location.
   */
  size_t getNbEnemyBuildings() const
  {
    std::pair<building_const_iterator, building_const_iterator> enemyBuildings = getEnemyBuildings();
    return std::distance(enemyBuildings.first, enemyBuildings.second);
  }

#pragma endregion

#pragma region Neutral_Units

  std::pair<unit_iterator, unit_iterator> 
    getNeutralUnits()
  {
    return std::make_pair(unit_iterator(unit_wrapper_iterator(getPlayerData(BWAPI::Broodwar->neutral()).m_units.begin())),
                          unit_iterator(unit_wrapper_iterator(getPlayerData(BWAPI::Broodwar->neutral()).m_units.end())));
  }

  std::pair<unit_const_iterator, unit_const_iterator> 
    getNeutralUnits() const
  {
    return std::make_pair(unit_const_iterator(unit_wrapper_const_iterator(getPlayerData(BWAPI::Broodwar->neutral()).m_units.begin())),
                          unit_const_iterator(unit_wrapper_const_iterator(getPlayerData(BWAPI::Broodwar->neutral()).m_units.end())));
  }

  std::pair<unit_type_iterator, unit_type_iterator> 
    getNeutralUnits(BWAPI::UnitType type)
  {
    std::pair<stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::iterator, stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::iterator> iterators = getPlayerData(BWAPI::Broodwar->neutral()).m_units.equal_range(type);
    return std::make_pair(unit_type_iterator(unit_wrapper_iterator(iterators.first)), 
                          unit_type_iterator(unit_wrapper_iterator(iterators.second)));
  }

  std::pair<unit_type_const_iterator, unit_type_const_iterator> 
    getNeutralUnits(BWAPI::UnitType type) const
  {
    std::pair<stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::const_iterator, stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::const_iterator> iterators = getPlayerData(BWAPI::Broodwar->neutral()).m_units.equal_range(type);
    return std::make_pair(unit_type_const_iterator(unit_wrapper_const_iterator(iterators.first)), 
                          unit_type_const_iterator(unit_wrapper_const_iterator(iterators.second)));
  }

  std::pair<building_iterator, building_iterator>
    getNeutralBuildings()
  {
    std::pair<building_wrapper_iterator, building_wrapper_iterator> neutralWrappers = getNeutralBuildingWrappers();
    return std::make_pair(building_iterator(neutralWrappers.first), 
                          building_iterator(neutralWrappers.second));
  }

  std::pair<building_const_iterator, building_const_iterator>
    getNeutralBuildings() const
  {
    std::pair<building_wrapper_const_iterator, building_wrapper_const_iterator> neutralWrappers = getNeutralBuildingWrappers();
    return std::make_pair(building_const_iterator(neutralWrappers.first), 
                          building_const_iterator(neutralWrappers.second));
  }

  /**
   * @return The number of neutral units, either accessible or in the fog of war, present in this location.
   */
  size_t getNbNeutralUnits() const
  {
    return getPlayerData(BWAPI::Broodwar->neutral()).m_units.size();
  }

  /**
   * @param type The type of unit.
   * @return The number of neutral units of this type, either accessible or in the fog of war, present in this location.
   */
  size_t getNbNeutralUnits(BWAPI::UnitType type) const
  {
    return getPlayerData(BWAPI::Broodwar->neutral()).m_units.count(type);
  }

  /**
   * @return The number of neutral buildings, either accessible or in the fog of war, present in this location.
   */
  size_t getNbNeutralBuildings() const
  {
    std::pair<building_const_iterator, building_const_iterator> neutralBuildings = getNeutralBuildings();
    return std::distance(neutralBuildings.first, neutralBuildings.second);
  }

#pragma endregion

#pragma region Shortcuts

  //template <class Predicate>
  //std::pair<boost::filter_iterator<Predicate, unit_const_iterator>, boost::filter_iterator<Predicate, unit_const_iterator>> 
  //  getOwnUnits(Predicate predicate) const
  //{
  //  std::pair<unit_const_iterator, unit_const_iterator> ownUnits = getOwnUnits();
  //  return std::make_pair(boost::filter_iterator<Predicate, unit_const_iterator>(predicate, ownUnits.first, ownUnits.second),
  //                        boost::filter_iterator<Predicate, unit_const_iterator>(predicate, ownUnits.second, ownUnits.second));
  //}

  //template <class Predicate>
  //std::pair<boost::filter_iterator<Predicate, unit_const_iterator>, boost::filter_iterator<Predicate, unit_const_iterator>> 
  //  getOwnUnits(BWAPI::UnitType type, Predicate predicate) const
  //{
  //  std::pair<unit_const_iterator, unit_const_iterator> ownUnits = getOwnUnits(unitType);
  //  return std::make_pair(boost::filter_iterator<Predicate, unit_const_iterator>(predicate, ownUnits.first, ownUnits.second),
  //                        boost::filter_iterator<Predicate, unit_const_iterator>(predicate, ownUnits.second, ownUnits.second));
  //}

  //typedef boost::filter_iterator<IsCompleted, unit_const_iterator> completed_unit_const_iterator;
  //std::pair<completed_unit_const_iterator, completed_unit_const_iterator>
  //  getCompletedOwnUnits() const
  //{
  //  std::pair<unit_const_iterator, unit_const_iterator> ownUnits = getOwnUnits();
  //  return std::make_pair(completed_unit_const_iterator(IsCompleted(), ownUnits.first, ownUnits.second),
  //                        completed_unit_const_iterator(IsCompleted(), ownUnits.second, ownUnits.second));
  //}

  typedef boost::filter_iterator<IsCompleted<UnitRecord>, unit_const_iterator> completed_unit_const_iterator;
  std::pair<completed_unit_const_iterator, completed_unit_const_iterator>
    getCompletedOwnUnits(BWAPI::UnitType type) const
  {
    std::pair<unit_const_iterator, unit_const_iterator> ownUnits = getOwnUnits(type);
    return std::make_pair(completed_unit_const_iterator(IsCompleted<UnitRecord>(), ownUnits.first, ownUnits.second),
                          completed_unit_const_iterator(IsCompleted<UnitRecord>(), ownUnits.second, ownUnits.second));
  }

  //typedef boost::filter_iterator<IsCompletedBuilding, unit_const_iterator> completed_building_const_iterator;
  //std::pair<completed_building_const_iterator, completed_building_const_iterator>
  //  getCompletedOwnBuildings() const
  //{
  //  std::pair<unit_const_iterator, unit_const_iterator> ownUnits = getOwnUnits();
  //  return std::make_pair(completed_building_const_iterator(IsCompletedBuilding(), ownUnits.first, ownUnits.second),
  //                        completed_building_const_iterator(IsCompletedBuilding(), ownUnits.second, ownUnits.second));
  //}

  //size_t getNbCompletedOwnUnits() const
  //{
  //  std::pair<completed_unit_const_iterator, completed_unit_const_iterator> ownUnits =
  //    getCompletedOwnUnits();
  //  return std::distance(ownUnits.first, ownUnits.second);
  //}

  size_t getNbCompletedOwnUnits(BWAPI::UnitType type) const
  {
    std::pair<completed_unit_const_iterator, completed_unit_const_iterator> ownUnits =
      getCompletedOwnUnits(type);
    return std::distance(ownUnits.first, ownUnits.second);
  }

  //size_t getNbCompletedOwnBuildings() const
  //{
  //  std::pair<completed_building_const_iterator, completed_building_const_iterator> ownBuildings = 
  //    getCompletedOwnBuildings();
  //  return std::distance(ownBuildings.first, ownBuildings.second);
  //}

#pragma region Shortcuts

  DECLARE_EVENT1(OnUnitAdded, UnitRecord*);
  //DECLARE_EVENT2(OnUnitMorph, UnitRecord*, BWAPI::UnitType);
  DECLARE_EVENT3(OnUnitRemoved, UnitRecord*, BWAPI::UnitType, BWAPI::Player*);

  /**
   * @brief The OnUnitAdded event is raised when a new unit has been added to this location.
   * @note This happens when a new unit is created in this location, when a previously unseen unit is
   *       spotted in this location, or when a unit moves to this location (not when units renegade).
   */
  OnUnitAdded& getUnitAddedEvent() const { return m_unitAddedEvent; }

  /**
   * @brief The OnUnitRemoved event is raised when a unit has been removed to this location.
   * @note This happens when a unit is destroyed in this location, when a unit moves out from this location,
   *       or when a unit previously in the fog of war cannot be spotted again (not when units renegade).
   */
  OnUnitRemoved& getUnitRemovedEvent() const { return m_unitRemovedEvent; }

  /**
   * @brief The OnOwnUnitAdded event is raised when a new own unit has been added to this location.
   * @note This happens when a new own unit is created (but is not necessarily completed yet) in this location,
   *       when an existing unit moves to this location or when an existing unit renegades to our team.
   */
  OnUnitAdded& getOwnUnitAddedEvent() const { return getPlayerData(BWAPI::Broodwar->self()).m_unitAddedEvent; }
  /**
   * @brief The OnOwnUnitRemoved event is raised when an own unit has been removed from this location.
   * @note This happens when an own unit is destroyed in this location, when an existing unit moves out from this
   *       location, or when an existing own unit renegades to another team.
   */
  OnUnitRemoved& getOwnUnitRemovedEvent() const { return getPlayerData(BWAPI::Broodwar->self()).m_unitRemovedEvent; }

  /**
   * @brief The OnEnemyUnitAdded event is raised when a new enemy unit has been added to this location.
   * @note This happens when a new enemy unit is created in this location, when a previously unseen enemy unit is 
   *       spotted in this location, when an enemy unit moves to this location or when a unit renegades to the enemy. 
   *       Note that it does NOT happen when a unit in the fog of war (like a building) is discovered again or when 
   *       a unit morphes.
   */
  OnUnitAdded& getEnemyUnitAddedEvent() const { return getPlayerData(BWAPI::Broodwar->enemy()).m_unitAddedEvent; }
  /**
   * @brief The OnEnemyUnitRemoved event is raised when an enemy unit has been removed from this location.
   * @note This happens when an enemy unit is destroyed in this location, when an enemy unit moves out from this location,
   *       when an enemy unit renegades to another team, or when an enemy unit previously in the fog of war cannot be
   *       spotted again.
   */
  OnUnitRemoved& getEnemyUnitRemovedEvent() const { return getPlayerData(BWAPI::Broodwar->enemy()).m_unitRemovedEvent; }

  /**
   * @brief The OnNeutralUnitAdded event is raised when a new neutral unit has been added to this location.
   * @note This happens when a new neutral unit is created in this location, when a previously unseen neutral unit is 
   *       spotted in this location, when a neutral unit moves to this location or when a unit renegades to the neutral team. 
   *       Note that it does NOT happen when a unit in the fog of war (like a building) is discovered again or when 
   *       a unit morphes.
   */
  OnUnitAdded& getNeutralUnitAddedEvent() const { return getPlayerData(BWAPI::Broodwar->neutral()).m_unitAddedEvent; }
  /**
   * @brief The OnNeutralUnitRemoved event is raised when a neutral unit has been removed from this location.
   * @note This happens when a neutral unit is destroyed in this location, when a neutral unit moves out from this location,
   *       when a neutral unit renegades to another team, or when a neutral unit previously in the fog of war cannot be
   *       spotted again.
   */
  OnUnitRemoved& getNeutralUnitRemovedEvent() const { return getPlayerData(BWAPI::Broodwar->neutral()).m_unitRemovedEvent; }

protected:
  friend class Map;
  friend class UnitRecord;

#pragma region Wrappers

  std::pair<unit_wrapper_iterator, unit_wrapper_iterator> 
    getOwnUnitWrappers()
  {
    return std::make_pair(unit_wrapper_iterator(getPlayerData(BWAPI::Broodwar->self()).m_units.begin()), 
                          unit_wrapper_iterator(getPlayerData(BWAPI::Broodwar->self()).m_units.end()));
  }

  std::pair<unit_wrapper_const_iterator, unit_wrapper_const_iterator> 
    getOwnUnitWrappers() const
  {
    return std::make_pair(unit_wrapper_const_iterator(getPlayerData(BWAPI::Broodwar->self()).m_units.begin()), 
                          unit_wrapper_const_iterator(getPlayerData(BWAPI::Broodwar->self()).m_units.end()));
  }

  std::pair<building_wrapper_iterator, building_wrapper_iterator> 
    getOwnBuildingWrappers()
  {
    std::pair<unit_wrapper_iterator, unit_wrapper_iterator> unitWrappers = getOwnUnitWrappers();
    return std::make_pair(building_wrapper_iterator(WrapperIsBuilding(), unitWrappers.first, unitWrappers.second), 
                          building_wrapper_iterator(WrapperIsBuilding(), unitWrappers.second, unitWrappers.second));
  }

  std::pair<building_wrapper_const_iterator, building_wrapper_const_iterator> 
    getOwnBuildingWrappers() const
  {
    std::pair<unit_wrapper_const_iterator, unit_wrapper_const_iterator> unitWrappers = getOwnUnitWrappers();
    return std::make_pair(building_wrapper_const_iterator(WrapperIsBuilding(), unitWrappers.first, unitWrappers.second), 
                          building_wrapper_const_iterator(WrapperIsBuilding(), unitWrappers.second, unitWrappers.second));
  }

  std::pair<unit_wrapper_iterator, unit_wrapper_iterator> 
    getEnemyUnitWrappers()
  {
    return std::make_pair(unit_wrapper_iterator(getPlayerData(BWAPI::Broodwar->enemy()).m_units.begin()), 
                          unit_wrapper_iterator(getPlayerData(BWAPI::Broodwar->enemy()).m_units.end()));
  }

  std::pair<unit_wrapper_const_iterator, unit_wrapper_const_iterator> 
    getEnemyUnitWrappers() const
  {
    return std::make_pair(unit_wrapper_const_iterator(getPlayerData(BWAPI::Broodwar->enemy()).m_units.begin()), 
                          unit_wrapper_const_iterator(getPlayerData(BWAPI::Broodwar->enemy()).m_units.end()));
  }

  std::pair<building_wrapper_iterator, building_wrapper_iterator> 
    getEnemyBuildingWrappers()
  {
    std::pair<unit_wrapper_iterator, unit_wrapper_iterator> unitWrappers = getEnemyUnitWrappers();
    return std::make_pair(building_wrapper_iterator(WrapperIsBuilding(), unitWrappers.first, unitWrappers.second), 
                          building_wrapper_iterator(WrapperIsBuilding(), unitWrappers.second, unitWrappers.second));
  }

  std::pair<building_wrapper_const_iterator, building_wrapper_const_iterator> 
    getEnemyBuildingWrappers() const
  {
    std::pair<unit_wrapper_const_iterator, unit_wrapper_const_iterator> unitWrappers = getEnemyUnitWrappers();
    return std::make_pair(building_wrapper_const_iterator(WrapperIsBuilding(), unitWrappers.first, unitWrappers.second), 
                          building_wrapper_const_iterator(WrapperIsBuilding(), unitWrappers.second, unitWrappers.second));
  }

  std::pair<unit_wrapper_iterator, unit_wrapper_iterator> 
    getNeutralUnitWrappers()
  {
    return std::make_pair(unit_wrapper_iterator(getPlayerData(BWAPI::Broodwar->neutral()).m_units.begin()), 
                          unit_wrapper_iterator(getPlayerData(BWAPI::Broodwar->neutral()).m_units.end()));
  }

  std::pair<unit_wrapper_const_iterator, unit_wrapper_const_iterator> 
    getNeutralUnitWrappers() const
  {
    return std::make_pair(unit_wrapper_const_iterator(getPlayerData(BWAPI::Broodwar->neutral()).m_units.begin()), 
                          unit_wrapper_const_iterator(getPlayerData(BWAPI::Broodwar->neutral()).m_units.end()));
  }

  std::pair<building_wrapper_iterator, building_wrapper_iterator> 
    getNeutralBuildingWrappers()
  {
    std::pair<unit_wrapper_iterator, unit_wrapper_iterator> unitWrappers = getNeutralUnitWrappers();
    return std::make_pair(building_wrapper_iterator(WrapperIsBuilding(), unitWrappers.first, unitWrappers.second), 
                          building_wrapper_iterator(WrapperIsBuilding(), unitWrappers.second, unitWrappers.second));
  }

  std::pair<building_wrapper_const_iterator, building_wrapper_const_iterator> 
    getNeutralBuildingWrappers() const
  {
    std::pair<unit_wrapper_const_iterator, unit_wrapper_const_iterator> unitWrappers = getNeutralUnitWrappers();
    return std::make_pair(building_wrapper_const_iterator(WrapperIsBuilding(), unitWrappers.first, unitWrappers.second), 
                          building_wrapper_const_iterator(WrapperIsBuilding(), unitWrappers.second, unitWrappers.second));
  }

#pragma endregion

  void onUnitAddedToLocation(UnitRecord* unitRecord, AutoPopulatedLocation*, RaiseEventTaskList& deferredEvents);
  void onUnitRemovedFromLocation(UnitRecord* unitRecord, AutoPopulatedLocation*, RaiseEventTaskList& deferredEvents);
  void onUnitMorph(void* data, UnitRecord* unitRecord, BWAPI::UnitType previousType, RaiseEventTaskList& deferredEvents);
  void onUnitRenegade(void* data, UnitRecord* unitRecord, BWAPI::Player* previousPlayer, RaiseEventTaskList& deferredEvents);

  struct PlayerData : public Component
  {
    PlayerData()
      : m_unitAddedEvent(*this)
      , m_unitRemovedEvent(*this)
      //, m_unitMorphEvent(*this)
    {}
    stdext::hash_multimap<BWAPI::UnitType, UnitWrapper> m_units;
    BWAPI::Player* m_player;
    EVENT(OnUnitAdded) m_unitAddedEvent;
    EVENT(OnUnitRemoved) m_unitRemovedEvent;
    //EVENT(OnUnitMorph) m_unitMorphEvent;
  protected:
    virtual void _output(std::ostream& out) const;
  };

  PlayerData& getPlayerData(BWAPI::Player* player)
  {
    assert((player->getID() + 1) < (int)array_size(m_playerData));
    return m_playerData[player->getID() + 1];
  }

  const PlayerData& getPlayerData(BWAPI::Player* player) const
  {
    assert((player->getID() + 1) < (int)array_size(m_playerData));
    return m_playerData[player->getID() + 1];
  }

  /**
   * @brief Units information for all players involved in the game.
   */
  PlayerData m_playerData[Config::MAX_NB_PLAYERS];

  /**
   * @brief List of locations interested in knowing when units are added to/removed from this location.
   *        Could be connected to this location via the OnUnitAdded/OnUnitRemoved events but the deferredEvents
   *        parameter breaks down the interface.
   */
  mutable std::list<AutoPopulatedLocation*> m_subscribers;

  EVENT(OnUnitAdded) m_unitAddedEvent;
  EVENT(OnUnitRemoved) m_unitRemovedEvent;
};
