#pragma once
#include "ShapeLocation.h"
#include "../../../../Utils/functors.h"
#include <boost/numeric/conversion/bounds.hpp>

class RectangularLocation : public ShapeLocation
{
public:
  RectangularLocation(BWAPI::Position topLeftCorner, BWAPI::Position bottomRightCorner)
    : m_topLeftCorner(topLeftCorner)
    , m_bottomRightCorner(bottomRightCorner)
  {
    assert(m_topLeftCorner.x() <= m_bottomRightCorner.x());
    assert(m_topLeftCorner.y() <= m_bottomRightCorner.y());
  }

  virtual bool isInLocation(BWAPI::Position position) const
  {
    return is_between<int, true>(m_topLeftCorner.x(), m_bottomRightCorner.x())(position.x())
        && is_between<int, true>(m_topLeftCorner.y(), m_bottomRightCorner.y())(position.y());
  }
  
  virtual bool isInLocation(BWAPI::TilePosition tile) const
  {
    BWAPI::TilePosition topLeftTile(m_topLeftCorner);
    BWAPI::TilePosition bottomRightTile(m_bottomRightCorner);
    return is_between<int, true>(topLeftTile.x(), bottomRightTile.x())(tile.x())
        && is_between<int, true>(topLeftTile.y(), bottomRightTile.y())(tile.y());
  }

  RectangularLocation expand(int horizontalExpansion, int verticalExpansion) const
  {
    // Is it necessary to keep the position inside bounds?
    return RectangularLocation(BWAPI::Position(m_topLeftCorner.x() - horizontalExpansion, m_topLeftCorner.y() - verticalExpansion),
                               BWAPI::Position(m_bottomRightCorner.x() + horizontalExpansion, m_bottomRightCorner.y() + verticalExpansion));
  }

  virtual BWAPI::Position getCurrentCenter() const
  {
    int x = (m_bottomRightCorner.x() - m_topLeftCorner.x())/2;
    int y = (m_bottomRightCorner.y() - m_topLeftCorner.y())/2;
    return m_topLeftCorner + BWAPI::Position(x,y);
  }

  template <class UnitGroup>
  static RectangularLocation getBoundingBox(const UnitGroup& group)
  {
    int xMin = boost::numeric::bounds<int>::highest();
    int xMax = boost::numeric::bounds<int>::lowest();
    int yMin = boost::numeric::bounds<int>::highest();
    int yMax = boost::numeric::bounds<int>::lowest();

    for (UnitGroup::const_iterator it = group.begin();
         it != group.end();
         ++it)
    {
      xMin = std::min(xMin, (*it)->getPosition().x());
      xMax = std::max(xMax, (*it)->getPosition().x());
      yMin = std::min(yMin, (*it)->getPosition().y());
      yMax = std::max(yMax, (*it)->getPosition().y());
    }
    return RectangularLocation(BWAPI::Position(xMin, yMin),
                               BWAPI::Position(xMax, yMax));
  }

  BWAPI::Position getTopLeftCorner() const
  {
    return m_topLeftCorner;
  }

  BWAPI::Position getTopRightCorner() const
  {
    return BWAPI::Position(m_bottomRightCorner.x() , m_topLeftCorner.y());
  }

  BWAPI::Position getBottomRightCorner() const
  {
    return m_bottomRightCorner;
  }

  BWAPI::Position getBottomLeftCorner() const
  {
    return BWAPI::Position(m_topLeftCorner.x(), m_bottomRightCorner.y());
  }


protected:
  virtual void _output(std::ostream& out) const
  {
    out << "Rectangle(" << m_topLeftCorner << ", " << m_bottomRightCorner << ")";
  }

  const BWAPI::Position m_topLeftCorner;
  const BWAPI::Position m_bottomRightCorner;

private :

  // const member prevent the use of assignment operator
  RectangularLocation & operator = (const RectangularLocation & rhs);

};