#pragma once

#include "Common.h"

#include <chrono>

#include "UnitData.h"
#include "BuildOrderQueue.h"
#include "InformationManager.h"
#include "WorkerManager.h"
#include "BuildManager.h"
#include "ConstructionManager.h"
#include "ScoutManager.h"

#include "MapTools.h"
#include "StrategyData.h"
#include "Squad.h"
#include "Strategy_ScoutTaskManager.h"
#include "Strategy_InitialBuildOrderManager.h"
#include "Strategy_BuildTaskManager.h"
#include "Strategy_ExpansionTaskManager.h"
#include "Strategy_CombatTaskManager.h"
#include "UXManager_Additional.h"

#include "ConfidenceMap.h"

namespace MyBot
{

	/// 상황을 판단하여, 정찰, 빌드, 공격, 방어 등을 수행하도록 총괄 지휘를 하는 class
	/// InformationManager 에 있는 정보들로부터 상황을 판단하고, 
	/// BuildManager 의 buildQueue에 빌드 (건물 건설 / 유닛 훈련 / 테크 리서치 / 업그레이드) 명령을 입력합니다.
	/// 정찰, 빌드, 공격, 방어 등을 수행하는 코드가 들어가는 class
	class StrategyManager
	{
		StrategyManager();

		/// 디버깅용 플래그 : 어느 코드가 에러를 일으키는지 알기위한 플래그
		bool isToFindError;

	public:
		/// static singleton 객체를 리턴합니다
		static StrategyManager &	Instance();

		// 성경
		std::vector<std::string> words;
		void setWord();
		void sendWord();


		UnitData * enemyUnitData;
		UnitData * myUnitData;
		BWTA::BaseLocation * enemyMainBaseLocation;
		BWTA::BaseLocation * myMainBaseLocation;
		BWTA::BaseLocation * myFirstExpansionBaseLocation;
		BWTA::Chokepoint * myFirstChokepoint;
		BWTA::BaseLocation * enemyFirstExpansionBaseLocation;
		BWTA::Chokepoint * enemyFirstChokepoint;
		BWAPI::Player selfPlayer;
		BWAPI::Player enemyPlayer;
		BWAPI::Race			selfRace;		///< 아군 Player의 종족		
		BWAPI::Race			enemyRace;		///< 적군 Player의 종족  

		/// TODO : StartLocation 위치. BuildStrategy. CombatStrategy. 
		/// 한 게임에 대한 기록을 저장하는 자료구조
		class GameRecord {
		public:
			std::string mapName;
			std::string enemyName;
			std::string enemyRace;
			std::string enemyRealRace;
			std::string myName;
			std::string myRace;
			long long gameStartedTime = 0;
			int gameFrameCount = 0;
			int myWinCount = 0;
			int myLoseCount = 0;
		};
		/// 과거 전체 게임들의 기록을 저장하는 자료구조
		std::vector<GameRecord> gameRecordList;

		/// 과거 전체 게임 기록을 로딩합니다
		void loadGameRecordList();
		/// 과거 전체 게임 기록 + 이번 게임 기록을 저장합니다
		void saveGameRecordList(bool isWinner);
		/// 이번 게임 중간에 상시적으로 로그를 저장합니다
		void saveGameLog();


		BuildStrategy myCurrentBuildStrategy;		///< 아군의 현재 BuildStrategy		
		BuildStrategy myOldBuildStrategy;			///< 아군의 과거 BuildStrategy
		BuildStrategy enemyCurrentBuildStrategy;	///< 적군의 현재 BuildStrategy		
		BuildStrategy enemyOldBuildStrategy;		///< 적군의 과거 BuildStrategy		

		CombatStrategy myCurrentCombatStrategy;		///< 아군의 현재 CombatStrategy		
		CombatStrategy myOldCombatStrategy;			///< 아군의 과거 CombatStrategy
		CombatStrategy enemyCurrentCombatStrategy;	///< 적군의 현재 CombatStrategy		
		CombatStrategy enemyOldCombatStrategy;		///< 적군의 과거 CombatStrategy		

		std::set<StrategicTask> taskList;			///< 아군의 현재 StrategicTask 목록

		std::map<BWTA::BaseLocation *, LocationInfo> baseLocationStatusMap; ///< BaseLocation 과 LocationStatus 간 매핑 관계		
		std::map<BWTA::Chokepoint *, LocationInfo> chokepointStatusMap; ///< Chokepoint 과 LocationStatus 간 매핑 관계		

		std::set<BWTA::Chokepoint *> frontlineChokepointList;	/// < 최전방 길목들
		BWAPI::Position frontlinePosition;						/// < 최전방 길목들의 중심


		/// 아주 안전한 위치. 마린/드라군/히드라 사정거리 바깥
		std::set<BWAPI::WalkPosition>* safeLevel9Positions;

		/// 보통 안전한 위치. 두꺼운 경계
		std::set<BWAPI::WalkPosition>* safeLevel5Positions;

		/// 조금 안전한 위치. Sea
		std::set<BWAPI::WalkPosition>* safeLevel1Positions;


		/// 아군 부대
		Squad reservedSquad;				///< 예비 부대

		Squad mainCombatZerglingSquad;		///< 메인 공격 저글링 부대 : 본진 / 앞마당 공격
		Squad mainCombatHydraliskSquad;		///< 메인 공격 히드라 부대 : 본진 / 앞마당 공격
		Squad mainCombatLurkerSquad;		///< 메인 공격 럴커 부대 : 본진 / 앞마당 공격
		Squad mainCombatMutaliskSquad;		///< 메인 공격 뮤탈/디바우어러/가디언 부대 : 본진 / 앞마당 공격
		Squad mainCombatScourgeSquad;		///< 메인 공격 스커지 부대 : 본진 / 앞마당 공격
		Squad mainCombatOverlordSquad;		///< 메인 공격 오버로드 부대 : 오버로드
		Squad mainCombatQueenSquad;			///< 메인 공격 퀸 부대 : 퀸
		Squad mainCombatDefilerSquad;		///< 메인 공격 디파일러 부대 : 디파일러, 먹이용 저글링, 전담 오버로드들
		Squad mainCombatUltraliskSquad;		///< 메인 공격 울트라 부대 : 본진 / 앞마당 공격

		Squad reinforceMainCombatMutaliskSquad;		///< 메인 공격 뮤탈 증원 부대

		Squad subCombatSquad;				///< 서브 공격 부대 : 멀티 공격
		Squad subCombatOverlordSquad;		///< 서브 공격 지원 부대 : 오버로드. 

		Squad subCombatReinforceSquad;		///< 서브 증원 부대

		Squad mainBaseDefenseSquad;			///< 본진에 들어온 적 공격유닛들을 공격하는 부대

		Squad mainBaseClearSquad1;			///< 본진에 들어온 적 정찰유닛을 긴급공격하는 부대 : 저글링, 일꾼 등
		Squad mainBaseClearSquad2;			///< 본진에 들어온 적 건물을 긴급공격하는 부대 : 저글링, 일꾼 등
		Squad mainChokepointBlockingSquad;	///< 본진에 적 정찰/공격유닛이 못들어오도록 막는 부대 : 저글링, 일꾼 등
		Squad mainBasePatrolSquad;			///< 본진 주변 패트롤 부대 : 저글링, 스커지, 오버로드
		
		Squad firstExpansionDefenseSquad;	///< 앞마당 방어 부대 : 히드라, 뮤탈
		Squad firstExpansionPatrolSquad;	///< 앞마당 주변 패트롤 부대 : 저글링, 스커지, 오버로드

		Squad secondExpansionDefenseSquad;	///< 멀티 방어 부대 : 히드라, 뮤탈
		Squad secondExpansionPatrolSquad;	///< 멀티 주변 패트롤 부대 : 저글링, 스커지, 오버로드

		Squad dropAttackSquad;				///< 드랍부대 : 히드라, 럴커. 전담 오버로드
		Squad ambushCombatLurkerSquad;		///< 별동 매복부대 : 럴커. 전담 오버로드

		Squad newExpansionSetupSquad;		///< 신규 멀티 개척 부대 : 드론, 오버로드, 저글링, 히드라

		Squad scoutDroneSquad;				///< 정찰 드론 부대 : 초반에만 운용. 지상 정찰. 
		Squad scoutOverlordSquad1;			///< 정찰 오버로드 부대 : 초반에만 운용. 공중 정찰. 
		Squad scoutOverlordSquad2;			///< 정찰 오버로드 부대 : 초반에만 운용. 공중 정찰. 
		Squad scoutOverlordSquad3;			///< 정찰 오버로드 부대 : 초반에만 운용. 공중 정찰. 
		Squad scoutOverlordSquad4;			///< 정찰 오버로드 부대 : 초반에만 운용. 공중 정찰. 
		Squad scoutOverlordSquad5;			///< 정찰 오버로드 부대 : 초반에만 운용. 공중 정찰. 
		Squad scoutZerglingSquad;			///< 정찰 저글링 부대 : 지상 정찰
		Squad scoutScourgeSquad;			///< 정찰 스커지 부대 : 공중 정찰
		Squad scoutQueenSquad;				///< 정찰 퀸 부대 : 공중 정찰

		Squad multiObstructZerglingSquad;	///< 멀티방해 저글링 부대
		Squad multiObstructLurkerSquad;		///< 멀티방해 러커 부대

		//Squad workerSquad;					///< 일꾼 부대

		std::set<Squad*> allMySquadList;	///< 아군 전체 부대

		std::set<Squad*> squadListToBreakUp;	///< 해체 예정 부대


		// 정찰
		int lastFrameOfScoutEnemyBaseLocation;
		int lastFrameOfMultiObstructZerglingSquad;

		// 확장
		int numRiskToStartToConstructFirstExpansion;
		int lastFrameOfConstructHatcheryOnFirstExpansion;
		int lastFrameOfConstructCreepColonyOnFirstExpansion;
		int numRiskToStartToConstructSecondExpansion;
		bool isStartedToConstructSecondExpansion;


		// 건설
		bool isAlreadySentFirstExpansionHatcheryDrone;
		bool isAlreadySentFirstExpansionCreepColonyDrone;

		void moveWorkerToConstructionPlace();

		/// 대결이 시작될 때 일회적으로 전략 초기 세팅 관련 로직을 실행합니다
		void onStart();
		void setInitialBuildOrder();

		///  대결이 종료될 때 일회적으로 전략 결과 정리 관련 로직을 실행합니다
		void onEnd(bool isWinner);

		/// 대결 진행 중 매 프레임마다 대결 전략 관련 로직을 실행합니다
		void update();

		/// 핵미사일 목적 지점
		BWAPI::Position nukeDetectedPosition;

		/// 적군에게 핵미사일 보유 여부
		bool isEnemyHasNuclearMissile;

		/// 핵미사일 발사 감지 시점
		/// -1 = 핵미사일 발사된 적 없음
		/// 0 초과 = 핵미사일 발사된 시점
		int nukeDetectedFrame;
		/// 핵미사일 발사가 감지되었을 때 발생하는 이벤트를 처리합니다
		void onNukeDetect(BWAPI::Position target);

		/// 아군과 적군의 상황에 대한 판단, 그에 따른 StrategicTask 목록 업데이트 
		void updateStatusAndStrategyAndTaskList();

		void figureOutEnemyStrategy();
		void updateMyStrategy();
		void updateScoutTask();
		void updateBuildTask();
		void updateCombatTask();
		void updateExpansionTask();


		/// 아군 유닛들의 부대 배치 조정 및 타스트 실행
		void updateSquad();

		/// 정찰 유닛 플래그. 해당 유닛으로 정찰하는 것이 무의미하거나 손실이 클 것 같으면 false 로 만든다.
		bool isToScoutWithOverlord;		///< 적이 공중유닛 생산 시작하면 false
		bool isToScoutWithWorker;		///< 적군 종족이 프로토스, 테란이면 false
		bool isToScoutWithZergling;		///< 왠만하면 true
		bool isToScoutWithScourge;		///< 왠만하면 true
		bool isToScoutWithQueen;		///< 왠만하면 true
		bool isEnemyHasDangerousUnitToOverlord; ///< 적이 오버로드 공격 가능 유닛 생산 시작하면 true

		/// 정찰 시작하기 위한 최소 일꾼 인원 수
		int workerScoutCondition_completedWorkerCount;
		/// 저글링 정찰 유닛 수
		int zerglingScouterNumberFromCombatSquad;

		/// 정찰
		void executeScout();
		void cancelScoutDroneSquad();
		void cancelScoutOverlordSquad();

		bool isToBurrowOnEmptyBaseLocation;
		bool isToBurrowAndAmbushAttackWithLurker;

		// 전투 
		bool isToMakeQueen;
		bool isToMakeGuardian;
		bool isToMakeDevourer;
		bool isToMakeUltralisk;

		bool isDefenseEnough();
		bool isGoodTimingToStartGroundAttack();
		bool isGoodTimingToStartAirAttack();

		/// 각각의 유닛에 대해 성공적인 임무 수행을 위해 정교한 유닛 컨트롤
		/// 빌드 관련 타스크들을 실행
		void executeBuild();

		/// 전투 관련 타스크 실행
		void executeCombat();

		/// 확장기지 건설
		int lastFrameOfExpansionStart;
		void executeExpansion();
		bool isGoodTimingToStartAnotherExpansion();

		BWTA::BaseLocation* getMyNextExpansionLocation(TaskGoalType taskGoalType);
		std::vector<BWTA::BaseLocation*> getEnemyNextExpansionLocaion();


		// 지도 : ConfidenceMap
		void updateConfidenceMap();

		ConfidenceMap* confidenceMapForMutalisk;
		ConfidenceMap* confidenceMapForZergling;

		/// 헬퍼함수 - fake mineral 등에 의해 실제적으로 못가는 곳인지 판단
		bool isPossibleToGo(BWTA::BaseLocation* from, BWTA::BaseLocation* to);

		/// 헬퍼함수 - fake mineral 없애기
		BWAPI::TilePosition getNearestFakeMineralPosition(BWAPI::TilePosition fromPosition);
		BWAPI::TilePosition getNearestBaseLocationPositionBlockedByFakeMineral(BWAPI::TilePosition fromPosition);
		bool isFakeMineralCleared;
		BWAPI::Unit fakeMineralClearWorker;
		void clearFakeMineral();

		/// 헬퍼함수 - 안전한 위치 반환
		BWAPI::Position					getSafePositionForUnit(BWAPI::Unit overlord, BWAPI::Position desiredPosition = BWAPI::Positions::None);
		/// 헬퍼함수 - 안전한 위치 반환
		BWAPI::Position					getSafePosition(BWAPI::Position desiredPosition);




		/// 헬퍼함수 - 아군 빌드 전략 세팅
		void setMyBuildStrategy(BuildStrategy newBuildStrategy);
		/// 헬퍼함수 - 적군 빌드 전략 세팅
		void setEnemyBuildStrategy(BuildStrategy newBuildStrategy);
		/// 헬퍼함수 - 아군 전투 전략 세팅
		void setMyCombatStrategy(CombatStrategy newCombatStrategy);
		/// 헬퍼함수 - 적군 전투 전략 세팅
		void setEnemyCombatStrategy(CombatStrategy newCombatStrategy);

		/// 헬퍼함수 - 타스크 완료여부 파악
		bool isCompletedTask(StrategicTask task);
		/// 헬퍼함수 - 타스크 추가
		void insertTask(StrategicTask newTask);
		/// 헬퍼함수 - 타스크 제거
		void eraseTask(StrategicTask targetTask);
		/// 헬퍼함수 - 완료된 타스크 삭제한다
		void clearCompletedTask();

		/// 헬퍼함수 - 게임 기본 정보 업데이트
		void updateInformations();

		/// 헬퍼함수 - 안전 지역 업데이트
		void updateSafePositions();

		/// 헬퍼함수 - 초기 부대 설정
		void setInitialSquad();
		/// 헬퍼함수 - 부대 이름
		std::string getSquadInfo(Squad* squad);
		/// 헬퍼함수 - 사망한 유닛을 부대에서 삭제한다
		void clearDeadUnitInSquad();
		/// 헬퍼함수 - 부대간 유닛 전입전출
		void moveUnitBetweenSquad(Squad* from, Squad* to, BWAPI::UnitType unitType, int count = 1, BWAPI::Unit unit = nullptr);
		/// 헬퍼함수 - 부대 해체 (모든 유닛을 reservedSquad 로 전출)
		void breakUpSquad(Squad* targetSquad);

		/// 헬퍼함수 - Task 가 없는 Squad 는 해체한다
		//void breakUpSquadsThatHasNoTask();

		/// 헬퍼함수 - 부대 내 해당 유닛타입의 유닛수 반환
		int getUnitNumberInSquad(Squad* targetSquad, BWAPI::UnitType targetUnitType = BWAPI::UnitTypes::None);

		/// 헬퍼함수 - 전체 공격 유닛 숫자 세기
		int getMainCombatUnitCount();

		/// 헬퍼함수 - 오버로드 정찰하기 위험한 상황인가
		bool getEnemyHasDangerousUnitToOverlord();
		/// 헬퍼함수 - 드론 정찰을 중지시켜야 하는 상황인가
		bool isVeryDangerousToScoutWithDrone();
		/// 헬퍼함수 - 오버로드 정찰을 중지시켜야 하는 상황인가
		bool isVeryDangerousToEveryOverlord();

		/// 헬퍼함수 - 아군 전방 길목들 리턴
		std::set<BWTA::Chokepoint*> getMyFrontLineChokepoints();

	};
}
