#include "stdafx.h"
#include "Layer2Managers.h"

bool Layer2Managers::includedIn(Resources r1, Resources r2)
{
  return r1.getGas() <= r2.getGas() && r1.getMinerals() <= r2.getMinerals()
      && r1.getSupply() <= r2.getSupply();
}

Layer2Managers::Layer2Managers(Layer2Cueing& layer2Cueing, Layer1FSM& layer1FSM)
  : Layer2Actions(layer2Cueing, layer1FSM)
  , m_requestCounter(0)
//, m_actionEndedEventHandler(*this)
{
}

Layer2Managers::~Layer2Managers()
{
}

void Layer2Managers::onStart()
{
  Layer2Actions::onStart();
}

void Layer2Managers::onFrame()
{
  Layer2Actions::onFrame();

  //TODO: This should not be called every frame !
  
  Resources remainingResource = Resources(BWAPI::Broodwar->self()) - m_reservedResources;
  m_resourcesRequests.do_while(UseResources(m_resourcesRequests, remainingResource, this));

#ifdef NON_FINAL_VERSION
  if (Config::getInstance().displayOwnUnitsActions())
  {
    const std::set<BWAPI::Unit*>& units=BWAPI::Broodwar->self()->getUnits();
    for(std::set<BWAPI::Unit*>::const_iterator it = units.begin(); it != units.end(); ++it)
    {
      BWAPI::Unit* unit = *it;
      UnitData& data = BWAPI::getUnitData(unit);
      const GroupTacticalAction* action = dynamic_cast<Layer2UnitData&>(data).getCurrentAction();
      if (action != NULL)
      {
        const int x = unit->getPosition().x();
        const int y = unit->getPosition().y();
        const char activeColor = '\x17';
        const std::string actionName = action->getName();
        BWAPI::Broodwar->drawTextMap(x, y, "%c%s: %d", activeColor, actionName.c_str(), action->getPriority());    
      }
    }
  }
  m_buildingPlacer.onFrameDebug();
#endif
}

void Layer2Managers::onEnd()
{
  Layer2Actions::onEnd();

  m_resourcesRequests.do_while(ClearResourcesRequestsFunctor());
}

Layer2Managers::ResourcesRequestsType::IterationStep Layer2Managers::UseResources::operator()(Layer2Managers::ResourcesRequest* request)
{
  SafeMultisetBase::IterationStep result;

  bool haveMineral = (request->actionResources.getMinerals() == 0 || request->actionResources.getMinerals() <= m_remainingResources.getMinerals()) &&
    (request->actionResources.getGas() == 0 || request->actionResources.getGas() <= m_remainingResources.getGas()) && 
    (request->actionResources.getSupply() == 0 || request->actionResources.getSupply() <= m_remainingResources.getSupply());
  m_remainingResources -= request->actionResources.getResources();

  if(request->actionResources.getBuildingLocation() == NULL)
  {
    if(haveMineral)
    {
      m_layer2->reserveResources(request->actionResources.getResources());
      request->reserved = true;
      request->isInContainer = false;
      boost::tuple<BWAPI::Unit*> arg = boost::tuples::make_tuple((BWAPI::Unit*)NULL);
      request->eventHandler.onEvent(NULL, arg);
      result.deleteCurrent = true;
    }
  } 
  else
  {
    std::pair<AutoPopulatedLocation::unit_type_const_iterator, AutoPopulatedLocation::unit_type_const_iterator> buildingList = 
      request->actionResources.getBuildingLocation()->getOwnUnits(request->actionResources.getBuildingType());
    AutoPopulatedLocation::unit_type_const_iterator buildingIt = buildingList.first;
    bool done = false;
    while(!done && buildingIt != buildingList.second)
    {
      BWAPI::Unit* unit = (*buildingIt)->getUnit();
      Layer2ManagersUnitData& unitData = dynamic_cast<Layer2ManagersUnitData&>(BWAPI::getUnitData(unit));
      if(!unitData.isInUse() && 
        (unitData.getLastRequestedFrame() < BWAPI::Broodwar->getFrameCount() || unitData.getLastRequestedPriority() <= request->priority) 
        && unit->isCompleted() && !unit->isUnpowered())
      {
        if(haveMineral)
        {
          done = true;
          unitData.setInUseFlag(true);
          m_layer2->reserveResources(request->actionResources.getResources());
          request->reserved = true;
          request->isInContainer = false;
          boost::tuple<BWAPI::Unit*> arg = boost::tuples::make_tuple(unit);
          request->eventHandler.onEvent(NULL, arg);
          result.deleteCurrent = true;
        } 
        else
        {
          unitData.setLastRequestedFrame(BWAPI::Broodwar->getFrameCount());
          unitData.setLastRequestedPriority(request->priority);
        }
      }
      ++buildingIt;
    }
    //TODO: 
    // if(!done)
    // Notify Layer3 Action delayed no building available (+ Resources?)
  }

  if(!haveMineral)
  {
    //TODO: Notify layer 3, Delayed action
  }
  return result;
}

void Layer2Managers::requestResources(unsigned int priority, ActionResources resources, Layer2Managers::ResourcesRequest& request)
{
  // Automatic mechanism to erase request resource when its owner is deleted (or terminates)?

  request.id = ++m_requestCounter;
  request.priority = priority;
  request.actionResources = resources;

  ResourcesRequestsType::iterator pos = m_resourcesRequests.insert(&request);
  request.posInContainer = pos;
  request.isInContainer = true;
}

void Layer2Managers::terminateResourcesRequest(ResourcesRequest& request)
{
  if (request.reserved)
  {
    request.reserved = false;
    unreserveResources(request.actionResources.getResources());
  }
  if (request.isInContainer)
  {
    request.isInContainer = false;
    m_resourcesRequests.erase(request.posInContainer);
  }
}

void Layer2Managers::changeResourcesRequestPriority(ResourcesRequest& request, unsigned int newPriority)
{
  assert(request.isInContainer);

  m_resourcesRequests.erase(request.posInContainer);
  request.priority = newPriority;
  ResourcesRequestsType::iterator pos = m_resourcesRequests.insert(&request);
  request.posInContainer = pos;
}

void Layer2Managers::reserveResources(Resources resources)
{
  m_reservedResources += resources;
}

void Layer2Managers::unreserveResources(Resources resources)
{
  m_reservedResources -= resources;
  assert(m_reservedResources.getMinerals() >= 0);
  assert(m_reservedResources.getGas() >= 0);
  assert(m_reservedResources.getSupply() >= 0);
}

Resources Layer2Managers::getLowestPriorityAvailableResources() const
{
  return getAvailableResources(boost::numeric::bounds<int>::lowest());
}

Resources Layer2Managers::getHighestPriorityAvailableResources() const
{
  return Resources(BWAPI::Broodwar->self()) - m_reservedResources;
}

Resources Layer2Managers::getAvailableResources(unsigned int priority) const
{
  //
  // Returns the total amount of resources which are currently available for a process which would like
  // to execute an action of this priority.
  // This corresponds to the amount of resources currently registered (Broodwar->self()), 
  // - minus the resources not yet deducted due to latency problems (minerals count being updated 2-3 frames later...)
  // - minus the resources reserved for higher-priority actions
  //
  // Note: if Build actions cannot be cancelled once the probe is sent (even if higher priority actions need resources), 
  //       the amount of resources needed for all these pending Build actions must still be deduced, regardless of their priority.
  //
  
  return m_resourcesRequests.do_while(AvailableResourcesFunctor(Resources(BWAPI::Broodwar->self()) - m_reservedResources, priority)).getResources();
}

size_t Layer2Managers::getNbAvailableBuildings(const BWAPI::UnitType buildingType, const AutoPopulatedLocation* location) const
{
  size_t nbAvailable = 0;
  std::pair<AutoPopulatedLocation::unit_type_const_iterator, AutoPopulatedLocation::unit_type_const_iterator> buildingList =
    location->getOwnUnits(buildingType);
  for (AutoPopulatedLocation::unit_const_iterator buildingIt = buildingList.first; buildingIt != buildingList.second; ++buildingIt)
  {
    BWAPI::Unit* building = (*buildingIt)->getUnit();
    if(building->isCompleted() && !building->isUnpowered())
    {
      Layer2ManagersUnitData& unitData = dynamic_cast<Layer2ManagersUnitData&>(BWAPI::getUnitData(building));
      if(!unitData.isInUse())
        ++nbAvailable;
    }
  }
  return nbAvailable;
}

bool Layer2Managers::needsAdditionalPylons() const
{
  return m_buildingPlacer.needsAdditionalPylons();
}

void Layer2Managers::ResourcesRequest::_output(std::ostream& out) const
{
  out << priority << " - " << actionResources;
#ifdef NON_FINAL_VERSION
  out << " (" << eventHandler.m_owner << ")";
#endif
}