#pragma once
#include <BWAPI.h>
#include "GameData.h"
#include "Client.h"
#include "Shape.h"
#include "Command.h"
#include "UnitCommand.h"
#include "ForceImpl.h"
#include "PlayerImpl.h"
#include "RegionImpl.h"
#include "UnitImpl.h"
#include "BulletImpl.h"
#include <list>
#include <map>
#include <set>
#include <vector>

namespace BWAPI
{
  class Force;
  class Player;
  class Unit;
  class GameImpl : public Game
  {
    private :
      int addShape(const BWAPIC::Shape &s);
      int addString(const char* text);
      int addText(BWAPIC::Shape &s, const char* text);
      int addCommand(const BWAPIC::Command &c);
      void clearAll();

      GameData* data;
      std::vector<ForceImpl>  forceVector;
      std::vector<PlayerImpl> playerVector;
      std::vector<UnitImpl>   unitVector;
      std::vector<BulletImpl> bulletVector;
      RegionImpl *regionArray[5000];

      std::set<Force*> forces;
      std::set<Player*> playerSet;
      std::set<Unit*> accessibleUnits;//all units that are accessible (and definitely alive)
      //notDestroyedUnits - accessibleUnits = all units that may or may not be alive (status unknown)
      std::set<Unit*> minerals;
      std::set<Unit*> geysers;
      std::set<Unit*> neutralUnits;
      std::set<Unit*> staticMinerals;
      std::set<Unit*> staticGeysers;
      std::set<Unit*> staticNeutralUnits;
      std::set<Bullet*> bullets;
      std::set<Position> nukeDots;
      std::set<Unit*> selectedUnits;
      std::set<Unit*> pylons;
      std::set<Unit*> unitsOnTileData[256][256];
      std::set<Region*> regionsList;

      std::set< TilePosition > startLocations;
      std::list< Event > events;
      bool flagEnabled[2];
      Player* thePlayer;
      Player* theEnemy;
      Player* theNeutral;
      std::set<Player*> _allies;
      std::set<Player*> _enemies;
      std::set<Player*> _observers;
      Error lastError;
      int textSize;

    public :
      Event makeEvent(BWAPIC::Event e);
      int addUnitCommand(BWAPIC::UnitCommand& c);
      bool inGame;
      GameImpl(GameData* data);
      void onMatchStart();
      void onMatchEnd();
      void onMatchFrame();
      const GameData* getGameData() const;
      Unit *_unitFromIndex(int index);

      virtual std::set< Force* >& getForces();
      virtual std::set< Player* >& getPlayers();
      virtual std::set< Unit* >& getAllUnits();
      virtual std::set< Unit* >& getMinerals();
      virtual std::set< Unit* >& getGeysers();
      virtual std::set< Unit* >& getNeutralUnits();

      virtual std::set< Unit* >& getStaticMinerals();
      virtual std::set< Unit* >& getStaticGeysers();
      virtual std::set< Unit* >& getStaticNeutralUnits();

      virtual std::set< Bullet* >& getBullets();
      virtual std::set< Position >& getNukeDots();
      virtual std::list< Event>& getEvents();

      virtual Force* getForce(int forceID);
      virtual Player* getPlayer(int playerID);
      virtual Unit* getUnit(int unitID);
      virtual Unit* indexToUnit(int unitIndex);
      virtual Region* getRegion(int regionID);

      virtual GameType getGameType();
      virtual int getLatency();
      virtual int getFrameCount();
      virtual int getReplayFrameCount();
      virtual int getFPS();
      virtual double getAverageFPS();
      virtual BWAPI::Position getMousePosition();
      virtual bool getMouseState(MouseButton button);
      virtual bool getMouseState(int button);
      virtual bool getKeyState(Key key);
      virtual bool getKeyState(int key);
      virtual BWAPI::Position getScreenPosition();
      virtual void setScreenPosition(int x, int y);
      virtual void setScreenPosition(BWAPI::Position p);
      virtual void pingMinimap(int x, int y);
      virtual void pingMinimap(BWAPI::Position p);

      virtual bool  isFlagEnabled(int flag);
      virtual void  enableFlag(int flag);
      virtual std::set<Unit*>& getUnitsOnTile(int x, int y);
      virtual std::set<Unit*>& getUnitsInRectangle(int left, int top, int right, int bottom) const;
      virtual std::set<Unit*>& getUnitsInRectangle(BWAPI::Position topLeft, BWAPI::Position bottomRight) const;
      virtual std::set<Unit*>& getUnitsInRadius(BWAPI::Position center, int radius) const;
      virtual Error getLastError() const;
      virtual bool  setLastError(BWAPI::Error e);

      virtual int         mapWidth();
      virtual int         mapHeight();
      virtual std::string mapFileName();
      virtual std::string mapPathName();
      virtual std::string mapName();
      virtual std::string mapHash();

      virtual bool isWalkable(int x, int y);
      virtual int  getGroundHeight(int x, int y);
      virtual int  getGroundHeight(TilePosition position);
      virtual bool isBuildable(int x, int y, bool includeBuildings = false);
      virtual bool isBuildable(TilePosition position, bool includeBuildings = false);
      virtual bool isVisible(int x, int y);
      virtual bool isVisible(TilePosition position);
      virtual bool isExplored(int x, int y);
      virtual bool isExplored(TilePosition position);
      virtual bool hasCreep(int x, int y);
      virtual bool hasCreep(TilePosition position);
      virtual bool hasPower(int tileX, int tileY, UnitType unitType = UnitTypes::None) const;
      virtual bool hasPower(TilePosition position, UnitType unitType = UnitTypes::None) const;
      virtual bool hasPower(int tileX, int tileY, int tileWidth, int tileHeight, UnitType unitType = UnitTypes::None) const;
      virtual bool hasPower(TilePosition position, int tileWidth, int tileHeight, UnitType unitType = UnitTypes::None) const;
      virtual bool hasPowerPrecise(int x, int y, UnitType unitType = UnitTypes::None ) const;
      virtual bool hasPowerPrecise(Position position, UnitType unitType = UnitTypes::None) const;

      virtual bool canBuildHere(const Unit* builder, TilePosition position, UnitType type, bool checkExplored = false);
      virtual bool canMake(const Unit* builder, UnitType type);
      virtual bool canResearch(const Unit* unit, TechType type);
      virtual bool canUpgrade(const Unit* unit, UpgradeType type);
      virtual std::set< TilePosition >& getStartLocations();

      virtual void printf(const char* format, ...);
      virtual void sendText(const char* format, ...);
      virtual void sendTextEx(bool toAllies, const char *format, ...);

      virtual void changeRace(BWAPI::Race race);
      virtual bool isInGame();
      virtual bool isMultiplayer();
      virtual bool isBattleNet();
      virtual bool isPaused();
      virtual bool isReplay();

      virtual void startGame();
      virtual void pauseGame();
      virtual void resumeGame();
      virtual void leaveGame();
      virtual void restartGame();
      virtual void setLocalSpeed(int speed = -1);
      virtual bool issueCommand(const std::set<BWAPI::Unit*>& units, UnitCommand command);
      virtual std::set<BWAPI::Unit*>& getSelectedUnits();
      virtual Player* self();
      virtual Player* enemy();
      virtual Player* neutral();
      virtual std::set<BWAPI::Player*>& allies();
      virtual std::set<BWAPI::Player*>& enemies();
      virtual std::set<BWAPI::Player*>& observers();

      virtual void setTextSize(int size = 1);
      virtual void drawText(int ctype, int x, int y, const char *format, ...);
      virtual void drawTextMap(int x, int y, const char *format, ...);
      virtual void drawTextMouse(int x, int y, const char *format, ...);
      virtual void drawTextScreen(int x, int y, const char *format, ...);

      virtual void drawBox(int ctype, int left, int top, int right, int bottom, Color color, bool isSolid = false);
      virtual void drawBoxMap(int left, int top, int right, int bottom, Color color, bool isSolid = false);
      virtual void drawBoxMouse(int left, int top, int right, int bottom, Color color, bool isSolid = false);
      virtual void drawBoxScreen(int left, int top, int right, int bottom, Color color, bool isSolid = false);

      virtual void drawTriangle(int ctype, int ax, int ay, int bx, int by, int cx, int cy, Color color, bool isSolid = false);
      virtual void drawTriangleMap(int ax, int ay, int bx, int by, int cx, int cy, Color color, bool isSolid = false);
      virtual void drawTriangleMouse(int ax, int ay, int bx, int by, int cx, int cy, Color color, bool isSolid = false);
      virtual void drawTriangleScreen(int ax, int ay, int bx, int by, int cx, int cy, Color color, bool isSolid = false);

      virtual void drawCircle(int ctype, int x, int y, int radius, Color color, bool isSolid = false);
      virtual void drawCircleMap(int x, int y, int radius, Color color, bool isSolid = false);
      virtual void drawCircleMouse(int x, int y, int radius, Color color, bool isSolid = false);
      virtual void drawCircleScreen(int x, int y, int radius, Color color, bool isSolid = false);

      virtual void drawEllipse(int ctype, int x, int y, int xrad, int yrad, Color color, bool isSolid = false);
      virtual void drawEllipseMap(int x, int y, int xrad, int yrad, Color color, bool isSolid = false);
      virtual void drawEllipseMouse(int x, int y, int xrad, int yrad, Color color, bool isSolid = false);
      virtual void drawEllipseScreen(int x, int y, int xrad, int yrad, Color color, bool isSolid = false);

      virtual void drawDot(int ctype, int x, int y, Color color);
      virtual void drawDotMap(int x, int y, Color color);
      virtual void drawDotMouse(int x, int y, Color color);
      virtual void drawDotScreen(int x, int y, Color color);

      virtual void drawLine(int ctype, int x1, int y1, int x2, int y2, Color color);
      virtual void drawLineMap(int x1, int y1, int x2, int y2, Color color);
      virtual void drawLineMouse(int x1, int y1, int x2, int y2, Color color);
      virtual void drawLineScreen(int x1, int y1, int x2, int y2, Color color);

      virtual void *getScreenBuffer();
      virtual int  getLatencyFrames();
      virtual int  getLatencyTime();
      virtual int  getRemainingLatencyFrames();
      virtual int  getRemainingLatencyTime();
      virtual int  getRevision();
      virtual bool isDebug();
      virtual bool isLatComEnabled();
      virtual void setLatCom(bool isEnabled);
      virtual bool isGUIEnabled();
      virtual void setGUI(bool enabled = true);
      virtual int  getInstanceNumber();
      virtual int  getAPM(bool includeSelects = false);
      virtual bool setMap(const char *mapFileName);
      virtual void setFrameSkip(int frameSkip = 1);
      virtual bool hasPath(Position source, Position destination) const;
      virtual bool setAlliance(BWAPI::Player *player, bool allied = true, bool alliedVictory = true);
      virtual bool setVision(BWAPI::Player *player, bool enabled = true);
      virtual int  elapsedTime() const;
      virtual void setCommandOptimizationLevel(int level = 2);
      virtual int  countdownTimer() const;
      virtual const std::set<BWAPI::Region*> &getAllRegions() const;
      virtual BWAPI::Region *getRegionAt(int x, int y) const;
      virtual BWAPI::Region *getRegionAt(BWAPI::Position position) const;
      virtual int getLastEventTime() const;
      virtual bool setReplayVision(BWAPI::Player *player, bool enabled = true);
      virtual bool setRevealAll(bool reveal = true);
  };
}
