#include "stdafx.h"
#include "UnitTrainingMonitoringProcess.h"
#include "../../../SparAIContainer.h"

//
// When a building currently training a unit gets destroyed:
// 1- onUnitDestroyed(building)
// 2- onUnitDestroyed(unit) one frame later
// So the only way to destroy the unit first would be to explicitely cancel the training.
//

UnitTrainingMonitoringProcess::UnitTrainingMonitoringProcess(const Process* parent, BWAPI::Unit* building, BWAPI::Unit* unit, const char* description)
  : InBuildingMonitoringProcessT(parent, building)
  , InBuildingMonitoringProcess(parent, building)
  , MonitoringProcess(parent)
  , m_unitDestroyedEventHandler(*this)
  , m_unit(unit)
  , m_description(description)
{
  assert(!m_unit->getType().isBuilding());
}

void UnitTrainingMonitoringProcess::_output(std::ostream& out) const
{
  out << m_description << " in progress: " << *m_unit;
}

void UnitTrainingMonitoringProcess::onStart()
{
  m_unitDestroyedSubscriberId = BWAPI::getIndividualUnitDestroyEvent(m_unit).subscribe(&m_unitDestroyedEventHandler, NULL);
  InBuildingMonitoringProcessT::onStart();
}

void UnitTrainingMonitoringProcess::onEnd()
{
  BWAPI::getIndividualUnitDestroyEvent(m_unit).unsubscribe(m_unitDestroyedSubscriberId);
  InBuildingMonitoringProcessT::onEnd();
}

void UnitTrainingMonitoringProcess::onUnitDestroyed(void*)
{
  // If the training building gets destroyed, the onBuildingDestroyed callback will necessarily happen first,
  // so at this point we're sure the training was actually cancelled.

  if (!testCompletion())
  {
    terminate(Cancelled);
  }
}

bool UnitTrainingMonitoringProcess::testCompletion()
{
  bool completed = m_unit->isCompleted() && !m_unit->isMorphing() && m_unit->getRemainingBuildTime() == 0 && m_unit->getRemainingTrainTime() == 0;
  if (completed)
  {
    SPAR_LOG(LogTypes.PERCEPTUAL_STATE, "%s completed (0x%x) %s %d"
      , m_description
      , m_unit
      , m_unit->getType().getName().c_str()
      , m_unit->getID());
    Spar->getPerceptualState()->getUnitCompletedEvent().raise(m_unit);
    terminate(Success);
  }
  return isTerminated();
}

void UnitTrainingMonitoringProcess::setNewTimeout(bool error)
{
  if (error)
  {
    SPAR_LOG(LogTypes.GENERAL_ERROR, "%s (0x%x) %s %d not completed when it should"
      , m_description
      , m_unit
      , m_unit->getType().getName().c_str()
      , m_unit->getID());
  }

  int time = std::max(m_unit->getRemainingBuildTime(), m_unit->getRemainingTrainTime()); // Archons morph time is given by getRemainingBuildTime()
  // +1 to make sure we do not start an infinite loop
  m_timeoutEvent.setWaitingTimeBeforeExecution(time+1);
}

void UnitTrainingMonitoringProcess::cancel()
{
  // This will trigger a onUnitDestroy callback
  m_building->cancelTrain();
}