#pragma once
#include "../Layer3Tactical.h"
#include "L3Priority.h"
#include "Processes/BuildCannonsProcess.h"
#include "Processes/BuildExpansionsProcess.h"
#include "Processes/BuildGatewaysProcess.h"
#include "Processes/BuildPylonsProcess.h"
#include "Processes/ManageArmyProcess.h"
#include "Processes/ManageResourcesProcess.h"
#include "Processes/ProgressTechProcess.h"
#include "Processes/RecruitArmyProcess.h"
#include <boost/iterator/transform_iterator.hpp>
#include <hash_map>

class ResourcesAcquisitionPercentage;
class L3BaseLocation;

class Layer3Script : public Layer3Tactical
{
public:
  Layer3Script(Layer3TacticalRecognition& layer3TacticalRecognition, Layer2Actions& layer2Actions);
  ~Layer3Script();

  void onDependentTechsUpdate();

  Layer2Actions& getLayer2() const { return m_layer2Actions; }
  int countSupplyForOneRoundTroops() const;
  L3BaseLocation* getMainBase() const { return m_mainBase; }
  L3BaseLocation* getNearestOwnMiningBaseLocation(BWAPI::Position position) const;

  typedef boost::transform_iterator<extract_pair_second_const<stdext::hash_map<const BaseLocation*, L3BaseLocation*>::const_iterator::value_type>,
                                    stdext::hash_map<const BaseLocation*, L3BaseLocation*>::const_iterator>
          base_location_iterator;

  ///////////////////////
  // Level 4 interface //
  ///////////////////////
  virtual void setTargetTechs(const TechSet& targetTechs)
  {
    Layer3Tactical::setTargetTechs(targetTechs);

    m_progressTech.onNewTargetTechs(targetTechs);

    updateResourcesAcquisitionPercentage();
  }

  virtual void setTargetArmyComposition(const ArmyComposition& targetArmyComposition)
  {
    Layer3Tactical::setTargetArmyComposition(targetArmyComposition);

    m_recruitArmy.onNewTargetArmyComposition(targetArmyComposition);

    updateResourcesAcquisitionPercentage();
  }

  /////////
  // GUI //
  /////////
  const ProgressTechProcess* getProgressTechProcess() const
  {
    return &m_progressTech;
  }

  static Layer3Script& getInstance()
  {
    return static_cast<Layer3Script&>(Layer3Tactical::getInstance());
  }

#pragma region Types_Matching

  class L3UnitData : public virtual UnitData
  {
  public:
    L3UnitData(BWAPI::Unit* unit)
      : UnitData(unit)
    {
    }
  };

  typedef L3BaseLocation L3BaseLocation;

#pragma endregion

  void onFrame();
protected:
  virtual void onStart();
  virtual void onEnd();

  void onUnitCompleted(void* data, BWAPI::Unit* unit);

  void onUnitReleased(void* data, BWAPI::Unit* unit, const GroupTacticalAction* action);

  ResourcesAcquisitionPercentage computeTargetResourcesAcquisitionRatio() const;
  void updateResourcesAcquisitionPercentage();

  EVENT_HANDLER2(Layer3Script, onUnitReleased, BWAPI::Unit*, const GroupTacticalAction*) m_unitReleasedEventHandler;

  EVENT_HANDLER1(Layer3Script, onUnitCompleted, BWAPI::Unit*) m_unitCompletedEventHandler;

  // Periodic tasks
  ManageResourcesProcess m_manageResources;
  BuildPylonsProcess m_buildPylons;
  BuildGatewaysProcess m_buildGateways;
  BuildExpansionsProcess m_buildExpansions;
  ProgressTechProcess m_progressTech;
  RecruitArmyProcess m_recruitArmy;
  ManageArmyProcess m_manageArmy;
  BuildCannonsProcess m_buildCannons;

  L3BaseLocation* m_mainBase;
};
