#pragma once
#include "InfluenceMap.h"
#include <BWAPI.h>
#include <cassert>
#include <boost/numeric/conversion/bounds.hpp>

struct Visibility
{
  static int neverVisible() { return boost::numeric::bounds<int>::lowest(); }
  static int currentlyVisible() { return boost::numeric::bounds<int>::highest(); }

  /**
   * @brief Last frame at which the tile was visible, or MIN_INT if it never was and MAX_INT if it currently is.
   */
  int lastVisible;

  Visibility() { reset(); }

  void reset()
  {
    lastVisible = neverVisible();
  }

  void display(int x, int y, int w, int h, BWAPI::Player* player) const
  {
    // do nothing
    unused(x);
    unused(y);
    unused(w);
    unused(h);
    unused(player);
  }

  static void update(InfluenceMap<Visibility>& map)
  {
    assert(map.getPlayer() == BWAPI::Broodwar->self());

    assert(map.getWidth()  == static_cast<size_t>(BWAPI::Broodwar->mapWidth())
        && map.getHeight() == static_cast<size_t>(BWAPI::Broodwar->mapHeight()));

    // Cached to reduce the number of calls to BWAPI
    const int currentFrame = BWAPI::Broodwar->getFrameCount();

    for (size_t y=0; y<map.getHeight(); ++y)
    {
      for (size_t x=0; x<map.getWidth(); ++x)
      {
        Visibility influence = map.getData(x, y);
        influence.update(currentFrame, BWAPI::Broodwar->isVisible(x, y));
        map.setInfluence(x, y, influence);
      }
    }
  }

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

private:
  void update(int currentFrame, bool visible)
  {
    if (visible)
    {
      lastVisible = currentlyVisible();
    }
    else if (lastVisible == currentlyVisible())
    {
      assert(currentFrame == BWAPI::Broodwar->getFrameCount());
      lastVisible = currentFrame - 1;
    }
  }
};