#pragma once
#include <string>
#include <map>
#include <set>
#include <BWAPI/Race.h>
#include <BWAPI/UnitSizeType.h>
#include "Type.h"
#define BWAPI_UNIT_TYPE_MAX_COUNT 234

namespace BWAPI
{
  class TechType;
  class UpgradeType;
  class WeaponType;

  /** The UnitType class is used to get information about a particular type of unit, such as the build time
   * of a Lurker, or the mineral price of an Ultralisk. TODO Add the unittype table from the wiki*/
  class UnitType : public Type
  {
  public:
    UnitType();
    UnitType(int id);

    /** Returns the name of the unit. */
    const std::string &getName() const;
    const char *c_str() const;

    /** Returns the race that the unit belongs to. For example UnitTypes::Terran_SCV.getRace() will return
     * Races::Terran. */
    Race getRace() const;

    /** Returns what builds this unit type. The second number will usually be 1 unless the unit type is
     * Protoss_Archon or Protoss_Dark_Archon. Units that cannot be created, such as critters and mineral
     * fields, will return a pair where the unit type is UnitTypes::None, and the second component is 0.
     *
     * Example: UnitTypes::Terran_Marine.whatBuilds() will return an std::pair, where the first component
     * is UnitTypes::Terran_Barracks. */
    const std::pair< UnitType, int > whatBuilds() const;

    /** Returns the units the player is required to have before it can train or build the given unit type.
     *
     * Example: UnitTypes::Terran_Battlecruiser.requiredUnits() will return a map of three keys:
     * UnitTypes::Terran_Starport, UnitTypes::Terran_Control_Tower, and UnitTypes::Terran_Physics_Lab. */
    const std::map< UnitType, int >& requiredUnits() const;

    /** Included in the API for completeness, since the only units that actually needs tech to be trained
     * are the Zerg_Lurker and Zerg_Lurker_Egg. The tech type needed is TechTypes::Lurker_Aspect. */
    TechType requiredTech() const;

    /** Returns the tech used to cloak the unit, or TechTypes::None if the unit cannot cloak or is
        permanently cloaked */
    TechType cloakingTech() const;

    /** Returns the set of tech types this unit can use, provided the tech types have been researched and
     * the unit has enough energy. */
    const std::set< TechType >& abilities() const;

    /** Returns the set of upgrade types that can affect this unit. */
    const std::set< UpgradeType >& upgrades() const;

    /** Returns the upgrade that increase's the unit's armor, or UpgradeTypes::None if no upgrade
     * increase's this unit's armor. For example UnitTypes::Terran_Marine.armorUpgrade() will return a
     * pointer to UpgradeTypes::Terran_Infantry_Armor. */
    UpgradeType armorUpgrade() const;

    /** Returns the maximum amount of hit points the unit type can have. */
    int maxHitPoints() const;

    /** Returns the maximum amount of shields the unit type can have. */
    int maxShields() const;

    /** Returns the maximum amount of energy the unit type can have. */
    int maxEnergy() const;

    /** Returns the amount of armor the non-upgraded unit type has. */
    int armor() const;

    /** Returns the mineral price of the unit.
     *
     * Example: UnitTypes::Siege_Tank_Tank_Mode.mineralPrice() returns 150. */
    int mineralPrice() const;

    /** UnitTypes::Siege_Tank_Tank_Mode.gasPrice() returns 100. */
    int gasPrice() const;

    /** Returns the number of frames needed to make this unit type. */
    int buildTime() const;

    /** Returns the amount of supply used by this unit. Supply counts returned by BWAPI are double what you
     *  would expect to see from playing the game. This is because zerglings take up 0.5 in-game supply. */
    int supplyRequired() const;

    /** Returns the amount of supply produced by this unit (i.e. for a Protoss_Pylon). Supply counts
     * returned by BWAPI are double what you would expect to see from playing the game. This is because
     * zerglings take up 0.5 in-game supply. */
    int supplyProvided() const;

    /** Returns the amount of space this unit type takes up inside a bunker or transport unit. */
    int spaceRequired() const;

    /** Returns the amount of space this unit type provides. */
    int spaceProvided() const;

    /** Returns the score which is used to determine the total scores in the after-game stats screen. */
    int buildScore() const;

    /** Returns the score which is used to determine the total scores in the after-game stats screen. */
    int destroyScore() const;

    /** Returns the size of the unit - either Small, Medium, Large, or Independent. */
    UnitSizeType size() const;

    /** Returns the tile width of the unit. Useful for determining the size of buildings. For example
     * UnitTypes::Terran_Supply_Depot.tileWidth() will return 3. */
    int tileWidth() const;

    /** Returns the tile height of the unit. Useful for determining the size of buildings. For example
     * UnitTypes::Terran_Supply_Depot.tileHeight() will return 2. */
    int tileHeight() const;

    /** Distance from the center of the unit to the left edge of the unit, measured in pixels. */
    int dimensionLeft() const;

    /** Distance from the center of the unit to the top edge of the unit, measured in pixels. */
    int dimensionUp() const;

    /** Distance from the center of the unit to the right edge of the unit, measured in pixels. */
    int dimensionRight() const;

    /** Distance from the center of the unit to the bottom edge of the unit, measured in pixels. */
    int dimensionDown() const;

    /** Returns the range at which the unit will start targeting enemy units, measured in pixels. */
    int seekRange() const;

    /** Returns how far the un-upgraded unit type can see into the fog of war, measured in pixels. */
    int sightRange() const;

    /** Returns the unit's ground weapon. */
    WeaponType groundWeapon() const;

    // TODO: add doc
    int maxGroundHits() const;

    /** Returns the unit's air weapon. */
    WeaponType airWeapon() const;

    // TODO: add doc
    int maxAirHits() const;

    /** Returns the unit's non-upgraded top speed in pixels per frame. For Terran buildings that can lift
     * off and the Zerg Infested Command Center, this returns how fast the building moves when it is
     * lifted. */
    double topSpeed() const;

    /** Returns how fast the unit can accelerate to its top speed. What units this quantity is measured in
     * is currently unknown. */
    int acceleration() const;

    /** Related to how fast the unit can halt. What units this quantity is measured in is currently
     * unknown. */
    int haltDistance() const;

    /** Related to how fast the unit can turn. What units this quantity is measured in is currently
     * unknown. */
    int turnRadius() const;

    /** Returns true if the unit can train other units. For example, UnitTypes::Terran_Barracks.canProduce()
     * will return true, while UnitTypes::Terran_Marine?.canProduce() will return false. This is also true
     * for two non-building units: Protoss Carrier (can produce interceptors) and Protoss Reaver
     * (can produce scarabs). */
    bool canProduce() const;

    /** Returns true if the unit can attack (either ground or air). Returns false for units that can only
     * inflict damage via special abilities (such as Protoss High Templar). */
    bool canAttack() const;

    /** Returns true if the unit can move. Note that buildings will return false, even Terran buildings
     * which can move once lifted. */
    bool canMove() const;

    /** Returns true for flying/air units. */
    bool isFlyer() const;

    /** Returns true for units that regenerate health (i.e. zerg units). */
    bool regeneratesHP() const;

    /** Returns true if the unit type is capable of casting spells / using technology. */
    bool isSpellcaster() const;

    /** Returns true for the two units that are permanently cloaked - Protoss Observer and Protoss Dark
     * Templar. */
    bool hasPermanentCloak() const;

    /** Returns true for units that cannot be destroyed (i.e. Terran Nuclear Missile, Mineral Field,
     * Vespene Geyser, etc) */
    bool isInvincible() const;

    /** Returns true if the unit is organic, such as a Terran Marine. */
    bool isOrganic() const;

    /** Returns true if the unit is mechanical such as a Terran Vulture. */
    bool isMechanical() const;

    /** Returns true for the four robotic Protoss units - Probe, Shuttle, Reaver, and Observer. */
    bool isRobotic() const;

    /** Returns true for the seven units that can detect cloaked units - Terran Science Vessel, Spell
     * Scanner Sweep, Zerg Overlord, Protoss Observer, Terran Missile Turret, Zerg Spore Colony, and Protoss
     * Photon Cannon. */
    bool isDetector() const;

    /** Returns true for the five units that hold resources - Mineral Field, Vespene Geyser,
     * Terran Refinery, Zerg Extractor, and Protoss Assimilator. */
    bool isResourceContainer() const;

    /** Returns true for the five units that can accept resources - Terran Command Center, Protoss Nexus,
     * Zerg Hatchery, Zerg Lair, and Zerg Hive. */
    bool isResourceDepot() const;

    /** Returns true for Terran Refinery, Zerg Extractor, and Protoss Assimilator. */
    bool isRefinery() const;

    /** Returns true for Protoss Probe, Terran SCV, and Zerg Drone. */
    bool isWorker() const;

    /** Returns true for buildings that must be near a pylon to be constructed. */
    bool requiresPsi() const;

    /** Returns true for buildings that can only be built on zerg creep. */
    bool requiresCreep() const;

    /** Returns true for Zergling and Scourge. */
    bool isTwoUnitsInOneEgg() const;

    /** Returns true for Zerg Lurker and units that can burrow when burrow tech is researched. */
    bool isBurrowable() const;

    /** Returns true for units that can be cloaked - Terran Ghost and Terran Wraith. Does not include units
     * which have permanent cloak (Protoss Observer and Protoss Dark Templar). */
    bool isCloakable() const;

    /** Returns true if the unit is a building (also true for mineral field and vespene geyser). */
    bool isBuilding() const;

    /** Returns true if the unit is an add-on, such as a Terran Comsat Station. */
    bool isAddon() const;

    /** Returns true for Terran buildings that can lift off (i.e. Barracks). */
    bool isFlyingBuilding() const;

    /** Returns true if the unit is neutral, such as a critter or mineral field. */
    bool isNeutral() const;

    /** Returns true if the unit is a Hero unit. */
    bool isHero() const;

    /** Returns true if the unit is a Powerup unit. */
    bool isPowerup() const;

    /** Returns true if the unit is a regular Beacon. */
    bool isBeacon() const;

    /** Returns true if the unit is a flag Beacon. */
    bool isFlagBeacon() const;

    /** Returns true if the unit is a special building. */
    bool isSpecialBuilding() const;

    /** Returns true if the unit is a spell unit. */
    bool isSpell() const;

    /** Returns true if the unit produces larva. */
    bool producesLarva() const;

    /** Returns true if the unit is one of the three mineral field types. */
    bool isMineralField() const;

    /** Returns true if the unit is capable of constructing an addon.
        This consists of Command Center, Factory, Starport, and Science Facility. */
    bool canBuildAddon() const;
  };
  namespace UnitTypes
  {

    /** Given the name of a unit type, this function will return the unit type.
     * For example, UnitTypes::getUnitType("Terran Marine") will return UnitTypes::Terran_Marine. */
    UnitType getUnitType(std::string name);

    int maxUnitWidth();
    int maxUnitHeight();

    /** Returns the set of all the UnitTypes. */
    const std::set<UnitType>& allUnitTypes();

    /** Returns the set of all the MacroTypes. */
    const std::set<UnitType>& allMacroTypes();
    void init();
    extern const UnitType Terran_Marine;
    extern const UnitType Hero_Jim_Raynor_Marine;
    extern const UnitType Terran_Ghost;
    extern const UnitType Hero_Sarah_Kerrigan;
    extern const UnitType Hero_Samir_Duran;
    extern const UnitType Hero_Infested_Duran;
    extern const UnitType Hero_Alexei_Stukov;
    extern const UnitType Terran_Vulture;
    extern const UnitType Hero_Jim_Raynor_Vulture;
    extern const UnitType Terran_Goliath;
    extern const UnitType Hero_Alan_Schezar;
    extern const UnitType Terran_Siege_Tank_Tank_Mode;
    extern const UnitType Hero_Edmund_Duke_Tank_Mode;
    extern const UnitType Terran_SCV;
    extern const UnitType Terran_Wraith;
    extern const UnitType Hero_Tom_Kazansky;
    extern const UnitType Terran_Science_Vessel;
    extern const UnitType Hero_Magellan;
    extern const UnitType Terran_Dropship;
    extern const UnitType Terran_Battlecruiser;
    extern const UnitType Hero_Arcturus_Mengsk;
    extern const UnitType Hero_Hyperion;
    extern const UnitType Hero_Norad_II;
    extern const UnitType Hero_Gerard_DuGalle;
    extern const UnitType Terran_Vulture_Spider_Mine;
    extern const UnitType Terran_Nuclear_Missile;
    extern const UnitType Terran_Siege_Tank_Siege_Mode;
    extern const UnitType Hero_Edmund_Duke_Siege_Mode;
    extern const UnitType Terran_Firebat;
    extern const UnitType Hero_Gui_Montag;
    extern const UnitType Spell_Scanner_Sweep;
    extern const UnitType Terran_Medic;
    extern const UnitType Terran_Civilian;
    extern const UnitType Zerg_Larva;
    extern const UnitType Zerg_Egg;
    extern const UnitType Zerg_Zergling;
    extern const UnitType Hero_Devouring_One;
    extern const UnitType Hero_Infested_Kerrigan;
    extern const UnitType Zerg_Hydralisk;
    extern const UnitType Hero_Hunter_Killer;
    extern const UnitType Zerg_Ultralisk;
    extern const UnitType Hero_Torrasque;
    extern const UnitType Zerg_Broodling;
    extern const UnitType Zerg_Drone;
    extern const UnitType Zerg_Overlord;
    extern const UnitType Hero_Yggdrasill;
    extern const UnitType Zerg_Mutalisk;
    extern const UnitType Hero_Kukulza_Mutalisk;
    extern const UnitType Zerg_Guardian;
    extern const UnitType Hero_Kukulza_Guardian;
    extern const UnitType Zerg_Queen;
    extern const UnitType Hero_Matriarch;
    extern const UnitType Zerg_Defiler;
    extern const UnitType Hero_Unclean_One;
    extern const UnitType Zerg_Scourge;
    extern const UnitType Zerg_Infested_Terran;
    extern const UnitType Terran_Valkyrie;
    extern const UnitType Zerg_Cocoon;
    extern const UnitType Protoss_Corsair;
    extern const UnitType Hero_Raszagal;
    extern const UnitType Protoss_Dark_Templar;
    extern const UnitType Hero_Dark_Templar;
    extern const UnitType Hero_Zeratul;
    extern const UnitType Zerg_Devourer;
    extern const UnitType Protoss_Dark_Archon;
    extern const UnitType Protoss_Probe;
    extern const UnitType Protoss_Zealot;
    extern const UnitType Hero_Fenix_Zealot;
    extern const UnitType Protoss_Dragoon;
    extern const UnitType Hero_Fenix_Dragoon;
    extern const UnitType Protoss_High_Templar;
    extern const UnitType Hero_Tassadar;
    extern const UnitType Hero_Aldaris;
    extern const UnitType Protoss_Archon;
    extern const UnitType Hero_Tassadar_Zeratul_Archon;
    extern const UnitType Protoss_Shuttle;
    extern const UnitType Protoss_Scout;
    extern const UnitType Hero_Mojo;
    extern const UnitType Hero_Artanis;
    extern const UnitType Protoss_Arbiter;
    extern const UnitType Hero_Danimoth;
    extern const UnitType Protoss_Carrier;
    extern const UnitType Hero_Gantrithor;
    extern const UnitType Protoss_Interceptor;
    extern const UnitType Protoss_Reaver;
    extern const UnitType Hero_Warbringer;
    extern const UnitType Protoss_Observer;
    extern const UnitType Protoss_Scarab;
    extern const UnitType Critter_Rhynadon;
    extern const UnitType Critter_Bengalaas;
    extern const UnitType Special_Cargo_Ship;
    extern const UnitType Special_Mercenary_Gunship;
    extern const UnitType Critter_Scantid;
    extern const UnitType Critter_Kakaru;
    extern const UnitType Critter_Ragnasaur;
    extern const UnitType Critter_Ursadon;
    extern const UnitType Zerg_Lurker_Egg;
    extern const UnitType Zerg_Lurker;
    extern const UnitType Spell_Disruption_Web;
    extern const UnitType Terran_Command_Center;
    extern const UnitType Terran_Comsat_Station;
    extern const UnitType Terran_Nuclear_Silo;
    extern const UnitType Terran_Supply_Depot;
    extern const UnitType Terran_Refinery;
    extern const UnitType Terran_Barracks;
    extern const UnitType Terran_Academy;
    extern const UnitType Terran_Factory;
    extern const UnitType Terran_Starport;
    extern const UnitType Terran_Control_Tower;
    extern const UnitType Terran_Science_Facility;
    extern const UnitType Terran_Covert_Ops;
    extern const UnitType Terran_Physics_Lab;
    extern const UnitType Terran_Machine_Shop;
    extern const UnitType Terran_Engineering_Bay;
    extern const UnitType Terran_Armory;
    extern const UnitType Terran_Missile_Turret;
    extern const UnitType Terran_Bunker;
    extern const UnitType Special_Crashed_Norad_II;
    extern const UnitType Special_Ion_Cannon;
    extern const UnitType Zerg_Infested_Command_Center;
    extern const UnitType Zerg_Hatchery;
    extern const UnitType Zerg_Lair;
    extern const UnitType Zerg_Hive;
    extern const UnitType Zerg_Nydus_Canal;
    extern const UnitType Zerg_Hydralisk_Den;
    extern const UnitType Zerg_Defiler_Mound;
    extern const UnitType Zerg_Greater_Spire;
    extern const UnitType Zerg_Queens_Nest;
    extern const UnitType Zerg_Evolution_Chamber;
    extern const UnitType Zerg_Ultralisk_Cavern;
    extern const UnitType Zerg_Spire;
    extern const UnitType Zerg_Spawning_Pool;
    extern const UnitType Zerg_Creep_Colony;
    extern const UnitType Zerg_Spore_Colony;
    extern const UnitType Zerg_Sunken_Colony;
    extern const UnitType Special_Overmind_With_Shell;
    extern const UnitType Special_Overmind;
    extern const UnitType Zerg_Extractor;
    extern const UnitType Special_Mature_Chrysalis;
    extern const UnitType Special_Cerebrate;
    extern const UnitType Special_Cerebrate_Daggoth;
    extern const UnitType Protoss_Nexus;
    extern const UnitType Protoss_Robotics_Facility;
    extern const UnitType Protoss_Pylon;
    extern const UnitType Protoss_Assimilator;
    extern const UnitType Protoss_Observatory;
    extern const UnitType Protoss_Gateway;
    extern const UnitType Protoss_Photon_Cannon;
    extern const UnitType Protoss_Citadel_of_Adun;
    extern const UnitType Protoss_Cybernetics_Core;
    extern const UnitType Protoss_Templar_Archives;
    extern const UnitType Protoss_Forge;
    extern const UnitType Protoss_Stargate;
    extern const UnitType Special_Stasis_Cell_Prison;
    extern const UnitType Protoss_Fleet_Beacon;
    extern const UnitType Protoss_Arbiter_Tribunal;
    extern const UnitType Protoss_Robotics_Support_Bay;
    extern const UnitType Protoss_Shield_Battery;
    extern const UnitType Special_Khaydarin_Crystal_Form;
    extern const UnitType Special_Protoss_Temple;
    extern const UnitType Special_XelNaga_Temple;
    extern const UnitType Resource_Mineral_Field;
    extern const UnitType Resource_Mineral_Field_Type_2;
    extern const UnitType Resource_Mineral_Field_Type_3;
    extern const UnitType Special_Independant_Starport;
    extern const UnitType Resource_Vespene_Geyser;
    extern const UnitType Special_Warp_Gate;
    extern const UnitType Special_Psi_Disrupter;
    extern const UnitType Special_Power_Generator;
    extern const UnitType Special_Overmind_Cocoon;
    extern const UnitType Special_Zerg_Beacon;
    extern const UnitType Special_Terran_Beacon;
    extern const UnitType Special_Protoss_Beacon;
    extern const UnitType Special_Zerg_Flag_Beacon;
    extern const UnitType Special_Terran_Flag_Beacon;
    extern const UnitType Special_Protoss_Flag_Beacon;
    extern const UnitType Spell_Dark_Swarm;
    extern const UnitType Powerup_Uraj_Crystal;
    extern const UnitType Powerup_Khalis_Crystal;
    extern const UnitType Powerup_Flag;
    extern const UnitType Powerup_Young_Chrysalis;
    extern const UnitType Powerup_Psi_Emitter;
    extern const UnitType Powerup_Data_Disk;
    extern const UnitType Powerup_Khaydarin_Crystal;
    extern const UnitType Powerup_Mineral_Cluster_Type_1;
    extern const UnitType Powerup_Mineral_Cluster_Type_2;
    extern const UnitType Powerup_Protoss_Gas_Orb_Type_1;
    extern const UnitType Powerup_Protoss_Gas_Orb_Type_2;
    extern const UnitType Powerup_Zerg_Gas_Sac_Type_1;
    extern const UnitType Powerup_Zerg_Gas_Sac_Type_2;
    extern const UnitType Powerup_Terran_Gas_Tank_Type_1;
    extern const UnitType Powerup_Terran_Gas_Tank_Type_2;
    extern const UnitType Special_Map_Revealer;
    extern const UnitType Special_Floor_Missile_Trap;
    extern const UnitType Special_Floor_Hatch;
    extern const UnitType Special_Upper_Level_Door;
    extern const UnitType Special_Right_Upper_Level_Door;
    extern const UnitType Special_Pit_Door;
    extern const UnitType Special_Right_Pit_Door;
    extern const UnitType Special_Floor_Gun_Trap;
    extern const UnitType Special_Wall_Missile_Trap;
    extern const UnitType Special_Wall_Flame_Trap;
    extern const UnitType Special_Right_Wall_Missile_Trap;
    extern const UnitType Special_Right_Wall_Flame_Trap;
    extern const UnitType Special_Start_Location;

    extern const UnitType None;
    extern const UnitType AllUnits;
    extern const UnitType Men;
    extern const UnitType Buildings;
    extern const UnitType Factories;
    extern const UnitType Unknown;

  }
}
