#include "stdafx.h"
#include "AutoPopulatedLocation.h"
#include "../../../../BWAPI_Addon.h"

AutoPopulatedLocation::AutoPopulatedLocation()
  : m_unitAddedEvent(*this)
  , m_unitRemovedEvent(*this)
{
  for (size_t i = 0; i < array_size(m_playerData); ++i)
  {
    m_playerData[i].m_player = BWAPI::Broodwar->getPlayer((int)i);
  }
}

void AutoPopulatedLocation::onUnitAddedToLocation(UnitRecord* unitRecord, AutoPopulatedLocation*, RaiseEventTaskList& deferredEvents)
{
  std::pair<UnitRecord::DataID, bool> result = unitRecord->addData(this, NULL);
  if (result.second)
  {
    PlayerData& playerData = getPlayerData(unitRecord->getPlayer());
    stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::iterator it = 
      playerData.m_units.insert(std::make_pair(unitRecord->getType().getID(), UnitWrapper(unitRecord)));
    UnitWrapper* unitWrapper = &it->second;
    unitWrapper->m_playerData = &playerData;
    unitWrapper->m_posInUnits = it;
    unitWrapper->m_dataID = result.first;
    result.first->second = unitWrapper;

    deferredEvents.add(m_unitAddedEvent, boost::make_tuple(unitRecord));
    deferredEvents.add(playerData.m_unitAddedEvent, boost::make_tuple(unitRecord));
    for (std::list<AutoPopulatedLocation*>::const_iterator it = m_subscribers.begin(); it != m_subscribers.end(); ++it)
    {
      (*it)->onUnitAddedToLocation(unitRecord, this, deferredEvents);
    }
  }
  else
  {
    assert(static_cast<UnitWrapper*>(result.first->second)->m_nb > 0);
  }

  UnitWrapper* unitWrapper = static_cast<UnitWrapper*>(result.first->second);
  ++unitWrapper->m_nb;
}
void AutoPopulatedLocation::onUnitRemovedFromLocation(UnitRecord* unitRecord, AutoPopulatedLocation*, RaiseEventTaskList& deferredEvents)
{
  bool removed = false;
  UnitRecord::DataID dataID = unitRecord->getData(this);
  assert(unitRecord->isValid(dataID));

  UnitWrapper* unitWrapper = static_cast<UnitWrapper*>(dataID->second);
  if (--unitWrapper->m_nb == 0)
  {
    removed = true;
    BWAPI::UnitType unitType = unitWrapper->m_posInUnits->first;
    BWAPI::Player* player = unitWrapper->m_playerData->m_player;

    unitRecord->removeData(unitWrapper->m_dataID);
    PlayerData& playerData = *unitWrapper->m_playerData;
    playerData.m_units.erase(unitWrapper->m_posInUnits);
    
    deferredEvents.add(m_unitRemovedEvent, boost::make_tuple(unitRecord, unitType, player));
    deferredEvents.add(playerData.m_unitRemovedEvent, boost::make_tuple(unitRecord, unitType, player));
    for (std::list<AutoPopulatedLocation*>::const_iterator it = m_subscribers.begin(); it != m_subscribers.end(); ++it)
    {
      (*it)->onUnitRemovedFromLocation(unitRecord, this, deferredEvents);
    }
  }
}

void AutoPopulatedLocation::onUnitMorph(void* data, UnitRecord* unitRecord, BWAPI::UnitType previousType, RaiseEventTaskList&)
{
  UnitWrapper* oldWrapper = static_cast<UnitWrapper*>(data);

  unused(previousType);
  assert(oldWrapper->m_posInUnits->first == previousType);

  if (oldWrapper->m_posInUnits->first != unitRecord->getType())
  {
    UnitWrapper copyWrapper(*oldWrapper);

    stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>& units = oldWrapper->m_playerData->m_units;
    units.erase(oldWrapper->m_posInUnits);

    stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::iterator it = units.insert(std::make_pair(unitRecord->getType(), copyWrapper));
    UnitWrapper* newWrapper = &it->second;
    newWrapper->m_posInUnits = it;
    newWrapper->m_dataID->second = newWrapper;

    //deferredEvents.add(newWrapper->m_playerData->m_unitMorphEvent, boost::make_tuple(unitRecord, previousType));
  }
}

void AutoPopulatedLocation::onUnitRenegade(void* data, UnitRecord* unitRecord, BWAPI::Player*, RaiseEventTaskList& deferredEvents)
{
  UnitWrapper* oldWrapper = static_cast<UnitWrapper*>(data);
  PlayerData* oldPlayerData = oldWrapper->m_playerData;
  PlayerData* newPlayerData = &getPlayerData(unitRecord->getPlayer());
  if (oldPlayerData != newPlayerData)
  {
    UnitWrapper copyWrapper(*oldWrapper);

    BWAPI::UnitType unitType = oldWrapper->m_posInUnits->first;
    BWAPI::Player* player = oldPlayerData->m_player;
    oldPlayerData->m_units.erase(oldWrapper->m_posInUnits);
    deferredEvents.add(oldPlayerData->m_unitRemovedEvent, boost::make_tuple(unitRecord, unitType, player));

    stdext::hash_multimap<BWAPI::UnitType, UnitWrapper>::iterator it = 
      newPlayerData->m_units.insert(std::make_pair(unitRecord->getType(), copyWrapper));
    UnitWrapper* newWrapper = &it->second;
    newWrapper->m_playerData = newPlayerData;
    newWrapper->m_posInUnits = it;
    newWrapper->m_dataID->second = newWrapper;
    deferredEvents.add(newPlayerData->m_unitAddedEvent, boost::make_tuple(unitRecord));
  }
}

void AutoPopulatedLocation::PlayerData::_output(std::ostream& out) const
{
  if(m_player)
    out << "Player data for player " << *m_player;
  else
    out << "Inactive player data";
}
