#include "stdafx.h"
#include "BuildBehavior.h"
#include "../../Layer1FSM.h"

// TODO: timeout should include move state as well

const BuildBehavior::TerminationMode BuildBehavior::TerminatedNoPower;
const BuildBehavior::TerminationMode BuildBehavior::TerminatedTimeout;

BuildBehavior::BuildBehavior(const Process* parent, unsigned int initialPriority, BWAPI::Unit* buildingUnit, 
                             BWAPI::TilePosition buildTile, BWAPI::UnitType buildingType, int nbFramesTimeout)
  : UnitBehaviorT(parent, initialPriority, buildingUnit)
  , UnitBehavior(parent, initialPriority, buildingUnit)
  , Behavior(parent, initialPriority)
  , FSM(parent)
  , Process(parent)
  , m_gotoState(*this)
  , m_buildState(*this)
  , m_finalState(*this)
  , m_buildTile(buildTile)
  , m_buildingType(buildingType)
  , m_building(NULL)
  , m_nbFramesTimeout(nbFramesTimeout)
  , m_states(m_gotoState, m_buildState, m_finalState)
{
}

///////////////////////////////////////////////////
// Goto state

BuildBehavior::Goto::Goto(BuildBehavior& behavior)
  : BehaviorStateT(behavior)
  //, m_moveCommand(*this)
  , m_timerEvent(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 20, 5)
  , m_transitionToBuildState(
      *this,
      UnitReachedPosition(behavior.m_unit,BWAPI::Position(((m_context.m_buildTile.x()-1)*TILE_SIZE)-16,((m_context.m_buildTile.y()-1)*TILE_SIZE)-16),256), 
      NoTransitionAction(), 
      behavior.m_buildState)
  , m_events(m_timerEvent)
  , m_transitions(m_transitionToBuildState)
{
}

void BuildBehavior::Goto::onEnter()
{
  BWAPI::Position destination(((m_context.m_buildTile.x()-1)*TILE_SIZE)-16,((m_context.m_buildTile.y()-1)*TILE_SIZE)-16);
  destination.makeValid();
  Layer1FSM::getInstance().getArbitrator().
    submitCommand(m_context.m_unit, instantiateMoveCommand(), Arbitration::ActionImportance(m_context));
}

void BuildBehavior::Goto::onTimer()
{
  Layer1FSM::getInstance().getArbitrator().submitCommand(m_context.m_unit, instantiateMoveCommand(), Arbitration::ActionImportance(m_context));
  m_transitionToBuildState.testTransition(boost::make_tuple());
}

MoveCommand BuildBehavior::Goto::instantiateMoveCommand()
{
  BWAPI::Position destination(((m_context.m_buildTile.x()-1)*TILE_SIZE)-16,((m_context.m_buildTile.y()-1)*TILE_SIZE)-16);
  destination.makeValid();
  return MoveCommand(*this, destination);
}

///////////////////////////////////////////////////
// Build state

BuildBehavior::Build::Build(BuildBehavior& behavior)
  : BehaviorStateT(behavior)
  //, m_buildAction(*this)
  , m_timerEvent(*this, Scheduler::FSM_SCHEDULING_PRIORITY, 5, 5)
  , m_transitionToFinalState(
      *this,
      BuildingStarted(behavior.m_buildTile,behavior.m_buildingType,behavior.m_building), 
      NoTransitionAction(), 
      behavior.m_finalState)
  , m_events(m_timerEvent)
  , m_transitions(m_transitionToFinalState)
{
}

void BuildBehavior::Build::onEnter()
{
  m_initialFrame = BWAPI::Broodwar->getFrameCount();
  Layer1FSM::getInstance().getArbitrator().
    submitCommand(m_context.m_unit, BuildCommand(*this, m_context.m_buildingType, m_context.m_buildTile), Arbitration::ActionImportance(m_context));
}

void BuildBehavior::Build::onTimer()
{
  if ((BWAPI::Broodwar->getFrameCount() - m_initialFrame) > m_context.m_nbFramesTimeout)
  {
    m_context.terminate(TerminatedTimeout);
  }
  else if (m_context.m_buildingType.requiresPsi() && !BWAPI::Broodwar->hasPower(m_context.m_buildTile, m_context.m_buildingType))
  {
    m_context.terminate(TerminatedNoPower);
  }
  else if (!m_transitionToFinalState.testTransition(boost::make_tuple()))
  {
    Layer1FSM::getInstance().getArbitrator().
        submitCommand(m_context.m_unit, BuildCommand(*this, m_context.m_buildingType, m_context.m_buildTile), Arbitration::ActionImportance(m_context));
  }
}
