#pragma once
#include "Component.h"
#include <BWAPI.h>
#include <BWTA.h>
#include <algorithm>

/**
 * @brief Static class providing group-based functions.
 */
class GroupHelper
{
public:
  template <class UnitGroup>
  static BWAPI::Unit* getNearest(const UnitGroup& group, BWAPI::Position position)
  {
    return getNearest(group.begin(), group.end(), position);
  }

  template <class UnitIterator>
  static BWAPI::Unit* getNearest(UnitIterator begin, UnitIterator end, BWAPI::Position position)
  {
    //if (this->empty()) return NULL;

    UnitIterator i=begin;
    BWAPI::Unit* result=*i;
    double d=(*i)->getDistance(position);
    i++;
    for(;i!=end;i++)
    {
      double d2=(*i)->getDistance(position);
      if (d2<d)
      {
        d=d2;
        result=*i;
      }
    }
    return result;
  }

  template <class UnitGroup>
  static BWAPI::Position getCenter(const UnitGroup& group)
  {
    return getCenter(group.begin(), group.end());
  }

  template <class UnitIterator>
  static BWAPI::Position getCenter(UnitIterator begin, UnitIterator end)
  {
    int count=0;
    double x=0;
    double y=0;
    for(UnitIterator i=begin;i!=end;i++)
    {
      BWAPI::Position p((*i)->getPosition());
      if (p!=BWAPI::Positions::None && p!=BWAPI::Positions::Unknown)
      {
        count++;
        x+=p.x();
        y+=p.y();
      }
    }
    if (count==0)
    {
      return BWAPI::Positions::None;
    }
    return BWAPI::Position((int)(x/count),(int)(y/count));
  }
};

/**
 * @brief Base class for groups which implements useful shorthand functions.
 * @tparam DerivedGroup Type of derived groups, typically PersistentUnitGroup.
 */
template <class DerivedGroup>
class GroupBase : public virtual Component
{
public: 
  bool attackMove(BWAPI::Position position) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    { 
      retval = retval && (*i)->attack(position);
    }
    return retval;
  }

  bool move(BWAPI::Position position) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    { 
      retval = retval && (*i)->move(position);
    }
    return retval;
  }

  bool attackUnit(BWAPI::Unit* target) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->attack(target);
    }
    return retval;
  }
  bool rightClick(BWAPI::Position position) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->rightClick(position);
    }
    return retval;
  }
  bool rightClick(BWAPI::Unit* target) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->rightClick(target);
    }
    return retval;
  }
  bool train(BWAPI::UnitType  type) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->train(type);
    }
    return retval;
  }
  bool build(BWAPI::TilePosition position, BWAPI::UnitType  type) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->build(position,type);
    }
    return retval;
  }
  bool buildAddon(BWAPI::UnitType  type) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->buildAddon(type);
    }
    return retval;
  }
  bool research(BWAPI::TechType  tech) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->research(tech);
    }
    return retval;
  }
  bool upgrade(BWAPI::UpgradeType  upgrade) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->upgrade(upgrade);
    }
    return retval;
  }
  bool stop() const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->stop();
    }
    return retval;
  }
  bool holdPosition() const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->holdPosition();
    }
    return retval;
  }
  bool patrol(BWAPI::Position position) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->patrol(position);
    }
    return retval;
  }
  bool follow(BWAPI::Unit* target) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->follow(target);
    }
    return retval;
  }
  bool setRallyPoint(BWAPI::Position target) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->setRallyPoint(target);
    }
    return retval;
  }
  bool setRallyPoint(BWAPI::Unit* target) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->setRallyPoint(target);
    }
    return retval;
  }
  bool repair(BWAPI::Unit* target) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->repair(target);
    }
    return retval;
  }
  bool morph(BWAPI::UnitType type) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->morph(type);
    }
    return retval;
  }
  bool burrow() const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->burrow();
    }
    return retval;
  }
  bool unburrow() const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->unburrow();
    }
    return retval;
  }
  bool siege() const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->siege();
    }
    return retval;
  }
  bool unsiege() const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->unsiege();
    }
    return retval;
  }
  bool cloak() const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->cloak();
    }
    return retval;
  }
  bool decloak() const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->decloak();
    }
    return retval;
  }
  bool lift() const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->lift();
    }
    return retval;
  }
  bool land(BWAPI::TilePosition position) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->land(position);
    }
    return retval;
  }
  bool load(BWAPI::Unit* target) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->load(target);
    }
    return retval;
  }
  bool unload(BWAPI::Unit* target) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->unload(target);
    }
    return retval;
  }
  bool unloadAll() const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->unloadAll();
    }
    return retval;
  }
  bool unloadAll(BWAPI::Position position) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->unloadAll(position);
    }
    return retval;
  }
  bool cancelConstruction() const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->cancelConstruction();
    }
    return retval;
  }
  bool haltConstruction() const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->haltConstruction();
    }
    return retval;
  }
  bool cancelMorph() const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->cancelMorph();
    }
    return retval;
  }
  bool cancelTrain() const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->cancelTrain();
    }
    return retval;
  }
  bool cancelTrain(int slot) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->cancelTrain(slot);
    }
    return retval;
  }
  bool cancelAddon() const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->cancelAddon();
    }
    return retval;
  }
  bool cancelResearch() const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->cancelResearch();
    }
    return retval;
  }
  bool cancelUpgrade() const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->cancelUpgrade();
    }
    return retval;
  }
  bool useTech(BWAPI::TechType  tech) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->useTech(tech);
    }
    return retval;
  }
  bool useTech(BWAPI::TechType  tech, BWAPI::Position position) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->useTech(tech,position);
    }
    return retval;
  }
  bool useTech(BWAPI::TechType  tech, BWAPI::Unit* target) const
  {
    bool retval=true;
    for(DerivedGroup::const_iterator i = static_cast<const DerivedGroup*>(this)->begin();i!=static_cast<const DerivedGroup*>(this)->end();++i)
    {
      retval = retval && (*i)->useTech(tech,target);
    }
    return retval;
  }

  BWAPI::Unit* getNearest(BWAPI::Position position) const
  {
    return GroupHelper::getNearest(static_cast<const DerivedGroup*>(this)->begin(), static_cast<const DerivedGroup*>(this)->end(), position);
  }

  BWAPI::Position getCenter() const
  {
    return GroupHelper::getCenter(static_cast<const DerivedGroup*>(this)->begin(), static_cast<const DerivedGroup*>(this)->end());
  }

protected:
  virtual void _output(std::ostream& out) const
  {
    std::copy(static_cast<const DerivedGroup*>(this)->begin(), 
              static_cast<const DerivedGroup*>(this)->end(), 
              std::ostream_iterator<BWAPI::Unit*>(out, "; "));
  }
};