#pragma once

#include <deque>
#include <limits>

#include "Common.h"

#include "MapTools.h"
#include "BaseLocationManager.h"
#include "UnitInfoManager.h"
#include "WorkerManager.h"
#include "BotConfig.h"
#include "GameCommander.h"
#include "BuildingPlacer.h"
#include "TechTree.h"
#include "MetaType.h"
#include "Unit.h"

class IDABot : public sc2::Agent 
{
    MapTools                m_map;
    BaseLocationManager     m_bases;
    UnitInfoManager         m_unitInfo;
    WorkerManager           m_workers;
    BotConfig               m_config;
    TechTree                m_techTree;
    GameCommander           m_gameCommander;
    // TODO: This should not be exported for student use
    BuildingPlacer          m_buildingPlacer;

    std::vector<Unit>       m_allUnits;
    std::vector<CCPosition> m_baseLocations;

    void setUnits();
    void OnError(const std::vector<sc2::ClientError> & client_errors, 
                 const std::vector<std::string> & protocol_errors = {}) override;
public:

    IDABot();

    void OnGameStart() override;
    void OnStep() override;
	void OnStep_UpdateIDABot();

    /*
        My stuff
    */

    // Worker management
	enum Assignment {
		Scout,
		Mineral,
		Gas,
        BuildWalking,
        BuildBuilding
	};

    struct AssignmentData {
        // When assigned to building, this corresponds to the location for the building
        CCTilePosition buildGoal;
    };

    // Building management

    // Worker assignment book-keeping
	std::map<Unit, Assignment> workerAssignment;

    // When workers are assigned to either minerals or gas, they get assigned a base
    std::map<const BaseLocation *, std::vector<Unit>> base_assignment;

    std::vector<UnitType> build_order;  // TODO: Not used

    std::vector<UnitType> CreateBuildPlan();
    void CreateMaximizeMilitaryBuildPlan(std::vector<UnitType> &build_plan, size_t &available_food);
    void CreateMaximizeEconomyBuildPlan(size_t &number_of_minerals, size_t &number_of_workers, size_t &available_food, std::vector<UnitType> &build_plan);
	void assignScout();
	void manageWorkers(std::vector<UnitType> & build_plan);
    void manageBuilding(std::vector<UnitType> & build_plan);
    bool isFree(Unit & worker);
	void assignWork(Unit & worker, Assignment assignment);
    std::map<UnitType, int> numberOfTypeBeingBuilt() const;
    const BaseLocation * AssignBase(Unit & worker);
    CCTilePosition getBuildPosition(UnitType & building);
	Unit getClosestMineral(const CCPosition & unit) const;

    struct BuildStatus
    {
        UnitType type;
        CCTilePosition position;
        int idle;
    };
    std::map<Unit, BuildStatus> currently_building;

    // Military management
    std::map<UnitType, int> military_goal;

    // Economy
    struct Resources
    {
        int minerals;
        int gas;
    };

    Resources military_spending{};
    Resources economy_spending{};

    // Getters
	std::vector<Unit> getWorkers();

    // Maybe
    std::vector<Unit> getProducer(const MetaType & type, bool includeBusy = false, bool include_incomplete = false);

    /*
	    API for students
    */
          BotConfig & Config();
          WorkerManager & Workers();
    const BaseLocationManager & Bases() const;
    const MapTools & Map() const;
    const UnitInfoManager & UnitInfo() const;
    const TypeData & Data(const UnitType & type) const;
    const TypeData & Data(const CCUpgrade & type) const;
    const TypeData & Data(const MetaType & type) const;
    const TypeData & Data(const Unit & unit) const;
    CCRace GetPlayerRace(int player) const;
    CCPosition GetStartLocation() const;

    int GetCurrentFrame() const;
    int GetMinerals() const;
    int GetCurrentSupply() const;
    int GetMaxSupply() const;
    int GetGas() const;
    Unit GetUnit(const CCUnitID & tag) const;
    const std::vector<Unit> & GetAllUnits() const;
	const std::vector<Unit> & GetMyUnits() const;
    const std::vector<Unit> GetUnits(const UnitType & type, int player = Players::Self) const;
    const std::vector<CCPosition> & GetStartLocations() const;
};