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

const UnitCreationMonitoringProcess::TerminationMode UnitCreationMonitoringProcess::UnitDestroyed;

UnitCreationMonitoringProcess::UnitCreationMonitoringProcess(const Process* parent, BWAPI::Unit* unit, const char* description)
  : MonitoringProcessT(parent)
  , MonitoringProcess(parent)
  , m_unitDestroyedEventHandler(*this)
  , m_unit(unit)
  , m_description(description)
{}

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

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

void UnitCreationMonitoringProcess::onEnd()
{
  MonitoringProcessT::onEnd();
  BWAPI::getIndividualUnitDestroyEvent(m_unit).unsubscribe(m_unitDestroyedSubscriberId);
}

void UnitCreationMonitoringProcess::onTimeoutImpl()
{
  if (!testCompletion())
  {
    setNewTimeout(true);
    m_timeoutEvent.activate();
  }
}

void UnitCreationMonitoringProcess::onUnitDestroyed(void*)
{
  if (!testCompletion())
  {
    terminate(UnitDestroyed);
  }
}

bool UnitCreationMonitoringProcess::testCompletion()
{
  bool completed = m_unit->getType().isBuilding() ?
      m_unit->isCompleted() :
      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 UnitCreationMonitoringProcess::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());
  }

  const int time = m_unit->getType().isBuilding() ?
      m_unit->getRemainingBuildTime() + 71:
      std::max(m_unit->getRemainingBuildTime(), m_unit->getRemainingTrainTime()) + 1; // Archons morph time is given by getRemainingBuildTime()

  // It seems to take one additional frame to complete the unit (Protoss buildings take 71 frames to be useable after the remainingBuildTime hits zero)
  m_timeoutEvent.setWaitingTimeBeforeExecution(time);
}

void UnitCreationMonitoringProcess::cancel()
{
  // TODO: this should terminate on Cancelled instead of UnitDestroyed... but the only way to differentiate is 
  //       whether we got the refund or not.

  m_unit->cancelConstruction();
}