#pragma once
#include "../Scenario.h"

class TestSensingScenario : public Scenario<TestSensingScenario>
{
public:
  class CustomLayer1DecisionMaking : public InactiveLayer1
  {
  public:
    CustomLayer1DecisionMaking(Layer1GameState& layer1GameState)
      : InactiveLayer1(layer1GameState)
      , m_lastFrame(-1)
      , m_eventHandler(*this)
      , m_morphHandler(*this)
      , m_renegadeHandler(*this)
      , m_enemyUnitAddedEventHandler(*this)
      , m_enemyUnitRemovedEventHandler(*this)
    {
    }

    virtual void onFrame()
    {
      InactiveLayer1::onFrame();
      if (m_lastFrame != -1)
      {
        m_lastFrame = -1;
        const Map* map = Spar->getPerceptualState()->getMap();

        SPAR_LOG(LogTypes.PERCEPTUAL_STATE, "List of own units:");
        std::pair<Map::unit_const_iterator, Map::unit_const_iterator> ownBuildings = map->getOwnUnits();
        for (Map::unit_const_iterator it = ownBuildings.first; it != ownBuildings.second; ++it)
        {
          const UnitRecord* ownUnit = *it;
          SPAR_LOG(LogTypes.PERCEPTUAL_STATE, "  %s", toString(ownUnit).c_str());
        }

        SPAR_LOG(LogTypes.PERCEPTUAL_STATE, "List of enemy units:");
        std::pair<Map::unit_const_iterator, Map::unit_const_iterator> enemyBuildings = map->getEnemyUnits();
        for (Map::unit_const_iterator it = enemyBuildings.first; it != enemyBuildings.second; ++it)
        {
          const UnitRecord* enemyUnit = *it;
          SPAR_LOG(LogTypes.PERCEPTUAL_STATE, "  %s", toString(enemyUnit).c_str());
        }

        SPAR_LOG(LogTypes.PERCEPTUAL_STATE, "List of neutral units:");
        std::pair<Map::unit_const_iterator, Map::unit_const_iterator> neutralUnits = map->getNeutralUnits();
        for (Map::unit_const_iterator it = neutralUnits.first; it != neutralUnits.second; ++it)
        {
          const UnitRecord* neutralUnit = *it;
          SPAR_LOG(LogTypes.PERCEPTUAL_STATE, "  %s", toString(neutralUnit).c_str());
        }
      }
    }

  protected:
    virtual void onStart()
    {
      InactiveLayer1::onStart();
      m_unitDiscoverID = BWAPI::getUnitDiscoverEvent().subscribe(&m_eventHandler, NULL);
      m_unitEvadeID = BWAPI::getUnitEvadeEvent().subscribe(&m_eventHandler, NULL);
      m_unitShowID = BWAPI::getUnitShowEvent().subscribe(&m_eventHandler, NULL);
      m_unitHideID = BWAPI::getUnitHideEvent().subscribe(&m_eventHandler, NULL);
      m_unitCreateID = BWAPI::getUnitCreateEvent().subscribe(&m_eventHandler, NULL);
      m_unitDestroyID = BWAPI::getUnitDestroyEvent().subscribe(&m_eventHandler, NULL);
      m_unitMorphID = BWAPI::getUnitMorphEvent().subscribe(&m_morphHandler, NULL);
      m_unitRenegadeID = BWAPI::getUnitRenegadeEvent().subscribe(&m_renegadeHandler, NULL);

      m_enemyUnitAddedID = Spar->getPerceptualState()->getMap()->getEnemyUnitAddedEvent().subscribe(&m_enemyUnitAddedEventHandler, NULL);
      m_enemyUnitRemovedID = Spar->getPerceptualState()->getMap()->getEnemyUnitRemovedEvent().subscribe(&m_enemyUnitRemovedEventHandler, NULL);
    }

    virtual void onEnd()
    {
      InactiveLayer1::onEnd();
      BWAPI::getUnitDiscoverEvent().unsubscribe(m_unitDiscoverID);
      BWAPI::getUnitEvadeEvent().unsubscribe(m_unitEvadeID);
      BWAPI::getUnitShowEvent().unsubscribe(m_unitShowID);
      BWAPI::getUnitHideEvent().unsubscribe(m_unitHideID);
      BWAPI::getUnitCreateEvent().unsubscribe(m_unitCreateID);
      BWAPI::getUnitDestroyEvent().unsubscribe(m_unitDestroyID);
      BWAPI::getUnitMorphEvent().unsubscribe(m_unitMorphID);
      BWAPI::getUnitRenegadeEvent().unsubscribe(m_unitRenegadeID);

      Spar->getPerceptualState()->getMap()->getEnemyUnitAddedEvent().unsubscribe(m_enemyUnitAddedID);
      Spar->getPerceptualState()->getMap()->getEnemyUnitRemovedEvent().unsubscribe(m_enemyUnitRemovedID);
    }

    void onEvent(void*, BWAPI::Unit*)
    {
      m_lastFrame = BWAPI::Broodwar->getFrameCount();
    }
    void onMorph(void*, BWAPI::Unit*, BWAPI::UnitType)
    {
      m_lastFrame = BWAPI::Broodwar->getFrameCount();
    }
    void onRenegade(void*, BWAPI::Unit*, BWAPI::Player*)
    {
      m_lastFrame = BWAPI::Broodwar->getFrameCount();
    }
    void onEnemyUnitAddedEvent(void*, UnitRecord*)
    {
      m_lastFrame = BWAPI::Broodwar->getFrameCount();
    }
    void onEnemyUnitRemovedEvent(void*, UnitRecord*, BWAPI::UnitType, BWAPI::Player*)
    {
      m_lastFrame = BWAPI::Broodwar->getFrameCount();
    }

    int m_lastFrame;
    EVENT_HANDLER1(CustomLayer1DecisionMaking, onEvent, BWAPI::Unit*) m_eventHandler;
    Event<boost::tuple<BWAPI::Unit*>>::SubscriberID m_unitDiscoverID;
    Event<boost::tuple<BWAPI::Unit*>>::SubscriberID m_unitEvadeID;
    Event<boost::tuple<BWAPI::Unit*>>::SubscriberID m_unitShowID;
    Event<boost::tuple<BWAPI::Unit*>>::SubscriberID m_unitHideID;
    Event<boost::tuple<BWAPI::Unit*>>::SubscriberID m_unitCreateID;
    Event<boost::tuple<BWAPI::Unit*>>::SubscriberID m_unitDestroyID;
    EVENT_HANDLER2(CustomLayer1DecisionMaking, onMorph, BWAPI::Unit*, BWAPI::UnitType) m_morphHandler;
    Event<boost::tuple<BWAPI::Unit*, BWAPI::UnitType>>::SubscriberID m_unitMorphID;
    EVENT_HANDLER2(CustomLayer1DecisionMaking, onRenegade, BWAPI::Unit*, BWAPI::Player*) m_renegadeHandler;
    Event<boost::tuple<BWAPI::Unit*, BWAPI::Player*>>::SubscriberID m_unitRenegadeID;
    EVENT_HANDLER1(CustomLayer1DecisionMaking, onEnemyUnitAddedEvent, UnitRecord*) m_enemyUnitAddedEventHandler;
    Event<boost::tuple<UnitRecord*>>::SubscriberID m_enemyUnitAddedID;
    EVENT_HANDLER3(CustomLayer1DecisionMaking, onEnemyUnitRemovedEvent, UnitRecord*, BWAPI::UnitType, BWAPI::Player*) m_enemyUnitRemovedEventHandler;
    Event<boost::tuple<UnitRecord*, BWAPI::UnitType, BWAPI::Player*>>::SubscriberID m_enemyUnitRemovedID;
  };

  template <class SparAIFactory>
  SparAIContainer* instantiateSparAIImpl(SparAIFactory& sparFactory)
  {
    typedef SparAIModule<Layer1GameState, Layer2Cueing, Layer3TacticalRecognition, Layer4StrategicRecognition,
                         CustomLayer1DecisionMaking, InactiveLayer2, InactiveLayer3, InactiveLayer4> SPAR;

    SPAR* module = sparFactory.instantiate<Layer1GameState, Layer2Cueing, Layer3TacticalRecognition, Layer4StrategicRecognition,
                                           CustomLayer1DecisionMaking, InactiveLayer2, InactiveLayer3, InactiveLayer4>(this);

    BWAPI::Broodwar->setLocalSpeed(30);

    return module;
  }

protected:
  virtual void onSparStarted(void*)
  {
  }
};