#pragma once
#include "InfluenceMap.h"
#include <BWAPI.h>
#include <cassert>
#include <functional>
#include <set>

class UnitRecord;

/**
 * @brief Structure that holds information about whether a tile is in the detection radii of at least one unit
 *        (only for the enemy player at the moment).
 */
struct Detection
{
  int detectionLevel;

  Detection() { reset(); }
  void reset() { detectionLevel = 0; }

  bool isNull() const
  {
    return detectionLevel == 0;
  }

  int getDetectionLevel() const { return detectionLevel; }

  void display(int x, int y, int w, int h, BWAPI::Player* player) const
  {
    if (detectionLevel > 0 && player != BWAPI::Broodwar->self())
    {
      BWAPI::Broodwar->drawBoxMap(x, y, x+w, y+h,  BWAPI::Color(100, 100, (detectionLevel + 50)), false);
    }
  }

  static void update(InfluenceMap<Detection>& map);

  static const char* name() { return "Detection"; }

  static int get(const InfluenceMapRef<Detection>::map_ptr &map, const BWAPI::TilePosition &pos)
  {
    return map->getData(pos).getDetectionLevel();
  }

private:
  template <class Iterator>
  static void update(InfluenceMap<Detection>& map, Iterator begin, Iterator end)
  {
    for (; begin != end; ++begin)
    {
      update(map, *begin);
    }
  }

  static void update(InfluenceMap<Detection>& map, BWAPI::Unit* unit);
  static void update(InfluenceMap<Detection>& map, const UnitRecord* unit);

public:
  class UpdateDetection : std::binary_function<int, int, void>
  {
    InfluenceMap<Detection> &m_map;
    const int m_x;
    const int m_y;
    const int m_level;
  public:
    UpdateDetection(InfluenceMap<Detection> &map, int x, int y, int level) 
      : m_map(map), m_x(x), m_y(y), m_level(level) {}

    result_type operator()(first_argument_type x, second_argument_type y)
    {
      x += m_x;
      y += m_y;

      if(m_map.isValid(x, y))
      {
        Detection detection = m_map.getData(x, y);
        detection.detectionLevel += m_level;
        m_map.setInfluence(x, y, detection);
      }
    }
    private :
      //const member prevent the use of the assignment operator
      UpdateDetection& operator=(const UpdateDetection&);
  };
};
