#pragma once
#include "../../../../Scheduler/SchedulerTaskHelper.h"
#include "../../Interface/Actions/Recruit.h"
#include "L2TacticalAction.h"
#include "../Layer2ManagersBase.h"

class L2IndividualRecruit;

class L2Recruit : public virtual Recruit, public L2TacticalAction
{
  friend L2IndividualRecruit;
  typedef std::list<L2IndividualRecruit*> RecruitListType;
  typedef RecruitListType::iterator RecruitListID;
public:
  L2Recruit(const Process* parent, unsigned int priority, AutoPopulatedLocation* location, const std::map<BWAPI::UnitType, size_t>& unitTypes, bool isInfinite = false)
    : Recruit(parent, priority, location, unitTypes, isInfinite)
    , L2TacticalAction(parent, priority)
    , TacticalAction(parent, priority)
    , Process(parent)
    , m_cancelled(false)
  {}

  L2Recruit(const Process* parent, unsigned int priority, AutoPopulatedLocation* location, BWAPI::UnitType unitType, bool isInfinite = false)
    : Recruit(parent, priority, location, unitType, isInfinite)
    , L2TacticalAction(parent, priority)
    , TacticalAction(parent, priority)
    , Process(parent)
    , m_cancelled(false)
  {}

  ~L2Recruit();

  virtual UnitRecordGroup _getUnitsInvolved() const;

protected:
  virtual void onStartImplementation();
  virtual void onCancelImplementation();
  virtual void onEndImplementation();

  void onIndividualRecruitTerminated(RecruitListID id);
  void onIndividualRecruitUnitTraining(const L2IndividualRecruit* recruit);

  bool m_cancelled;
  
  RecruitListType m_individualRecruits;
};

class L2IndividualRecruit : public Component
{
public:
  L2IndividualRecruit(L2Recruit* l2Recruit, BWAPI::UnitType unitType)
    : Component()
    , m_timeoutEvent(*this, Scheduler::SCHEDULING_MEDIUM_PRIORITY,1,0)
    , m_l2Recruit(l2Recruit)
    , m_unitToBuild(unitType)
    , m_resourceAllocatedEventHandler(*this)
    , m_unitCreatedEventHandler(*this)
    , m_unitCompletedEventHandler(*this)
    , m_unitDestroyedEventHandler(*this)
    , m_buildingDestroyedEventHandler(*this)
    , m_buildingToUse(NULL)
    , m_unitInConstruction(NULL)
    , m_currentState(UNINITIALIZED)
    , m_cancelled(false)
    , m_resourcesRequest(m_resourceAllocatedEventHandler)
  {}

  void execute(L2Recruit::RecruitListID it);
  void cancel(bool cancelCurrentUnit);
  void terminate();
  void _getUnitsInvolved(L2Recruit::UnitRecordGroup& unitsInvolved) const;
  BWAPI::UnitType getUnitToBuild() const { return m_unitToBuild; }

protected:
  virtual void _output(std::ostream& out) const;
  
  void onResourcesAllocated(void* data, BWAPI::Unit* building);
  void onTimeout();
  void onUnitCreated(void* data, BWAPI::Unit* unit);
  void onUnitCompleted(void* data, BWAPI::Unit* unit);
  void onUnitDestroyed(void* data);
  void onBuildingDestroyed(void* data);

  void requestResources();
  void recruitDone();
  void recruitFailed();

  enum RecruitState
  {
    UNINITIALIZED,
    WAITING_FOR_TECH,
    WAITING_FOR_RESOURCES,
    ACTION_SENT,
    UNIT_CREATED,
    WAITING_TO_FINISH,
    TERMINATED
  };

  L2Recruit* const m_l2Recruit;
  const BWAPI::UnitType m_unitToBuild;
  L2Recruit::RecruitListID m_id;

  RecruitState m_currentState;
  bool m_cancelled;
  BWAPI::Unit* m_buildingToUse;
  BWAPI::Unit* m_unitInConstruction;

  SCHEDULER_EVENT_HANDLER(L2IndividualRecruit,onTimeout) m_timeoutEvent;
  Event<boost::tuples::tuple<BWAPI::Unit*>>::SubscriberID m_unitCreatedSubscriberId;
  Event<boost::tuples::tuple<BWAPI::Unit*>>::SubscriberID m_unitCompletedSubscriberId;
  Event<boost::tuples::tuple<>>::SubscriberID m_unitDestroyedSubscriberId;
  Event<boost::tuples::tuple<>>::SubscriberID m_buildingDestroyedSubscriberId;
  Layer2ManagersBase::ResourcesRequest m_resourcesRequest;

  EVENT_HANDLER1(L2IndividualRecruit, onResourcesAllocated, BWAPI::Unit*) m_resourceAllocatedEventHandler;
  EVENT_HANDLER1(L2IndividualRecruit, onUnitCreated, BWAPI::Unit*) m_unitCreatedEventHandler;
  EVENT_HANDLER1(L2IndividualRecruit, onUnitCompleted, BWAPI::Unit*) m_unitCompletedEventHandler;
  EVENT_HANDLER(L2IndividualRecruit, onUnitDestroyed) m_unitDestroyedEventHandler;
  EVENT_HANDLER(L2IndividualRecruit, onBuildingDestroyed) m_buildingDestroyedEventHandler;
};

