#pragma once
#include "PrimitiveLocation.h"

class Map;
class Region;

/**
* @brief Locations where one player may collect minerals and/or gas.
*/
class BaseLocation : public PrimitiveLocation
{
public:
  BaseLocation(const BWTA::BaseLocation* baseLocation, Region* parent);

  Region* getParent() const
  {
    return m_parent;
  }

  // These two functions are needed by the ResourceManager (in the PerceptualState)

  virtual size_t getNbProbesOnMinerals() const = 0;

  virtual size_t getNbProbesOnGas() const = 0;

  virtual bool isInLocation(BWAPI::Position position) const;
  virtual bool isInLocation(BWAPI::TilePosition tile) const;

  virtual std::pair<BWAPI::TilePosition, int> getOldestVisibleTile() const;

  virtual BWAPI::TilePosition getRandomTile() const;

  virtual BWAPI::Position getCurrentCenter() const
  {
    return m_baseLocation->getPosition();
  }

  int getLastVisible() const;

  bool hasEnemyResourceDepot() const
  {
    return getNbEnemyUnits(BWAPI::UnitTypes::Protoss_Nexus) > 0 ||
           getNbEnemyUnits(BWAPI::UnitTypes::Terran_Command_Center) > 0 ||
           getNbEnemyUnits(BWAPI::UnitTypes::Zerg_Hatchery) > 0 ||
           getNbEnemyUnits(BWAPI::UnitTypes::Zerg_Lair) > 0 ||
           getNbEnemyUnits(BWAPI::UnitTypes::Zerg_Hive) > 0;
  }

  bool hasEnemyArmyUnits() const;

  typedef unit_type_const_iterator crystal_iterator;
  std::pair<crystal_iterator, crystal_iterator> getLastKnownCrystalsLeft() const
  {
    // If ever this becomes false the new minerals must be returned as well
    assert(getNbNeutralUnits(BWAPI::UnitTypes::Resource_Mineral_Field_Type_2) == 0);
    assert(getNbNeutralUnits(BWAPI::UnitTypes::Resource_Mineral_Field_Type_3) == 0);

    return getNeutralUnits(BWAPI::UnitTypes::Resource_Mineral_Field);
  }

  size_t getLastKnownNbEnemyRefineries() const
  {
    return getNbEnemyUnits(BWAPI::UnitTypes::Protoss_Assimilator) +
           getNbEnemyUnits(BWAPI::UnitTypes::Terran_Refinery) +
           getNbEnemyUnits(BWAPI::UnitTypes::Zerg_Extractor);
  }

  size_t getLastKnownNbCrystalsLeft() const
  {
    assert(getNbNeutralUnits(BWAPI::UnitTypes::Resource_Mineral_Field_Type_2) == 0);
    assert(getNbNeutralUnits(BWAPI::UnitTypes::Resource_Mineral_Field_Type_3) == 0);

    return getNbNeutralUnits(BWAPI::UnitTypes::Resource_Mineral_Field);
  }

  //bool isPossibleStartLocation() const
  //{
  //  // BWTA bug?
  //  return m_baseLocation->isStartLocation();
  //}

  size_t probesMineralsSaturation() const
  {
    return static_cast<size_t>(std::ceil(2.5 * double(getLastKnownNbCrystalsLeft())));
  }

  size_t probesGasSaturation() const
  {
    return 3 * getNbGeysers();
  }

  size_t getNbGeysers() const
  {
    return getNbOwnUnits(BWAPI::UnitTypes::Protoss_Assimilator) +
           getNbNeutralUnits(BWAPI::UnitTypes::Resource_Vespene_Geyser) + 
           getLastKnownNbEnemyRefineries();
  }

  const BWTA::BaseLocation* getBWTABaseLocation() const
  {
    return m_baseLocation;
  }

  virtual void _output(std::ostream& out) const
  {
    out << "Base location " << m_baseLocation->getPosition();
  }

protected:
  friend class Map;

  Region* const m_parent;
  const BWTA::BaseLocation* const m_baseLocation;
  const std::vector<BWAPI::TilePosition> m_tiles;
};
