// This file is part of ZetaGlest <https://github.com/ZetaGlest>
//
// Copyright (C) 2018  The ZetaGlest team
//
// ZetaGlest is a fork of MegaGlest <https://megaglest.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>

#ifndef _GAME_H_
#define _GAME_H_

#ifdef WIN32
#   include <winsock2.h>
#   include <winsock.h>
#endif

#include <vector>
#include "gui.h"
#include "game_camera.h"
#include "world.h"
#include "ai_interface.h"
#include "program.h"
#include "chat_manager.h"
#include "script_manager.h"
#include "game_settings.h"
#include "network_interface.h"
#include "data_types.h"
#include "selection.h"
#include "leak_dumper.h"

using std::vector;
using namespace Shared::Platform;
using namespace Shared::PlatformCommon;

namespace Shared {
	namespace Graphics {
		class VideoPlayer;
	}
};

namespace Game {
	class GraphicMessageBox;
	class ServerInterface;

	enum LoadGameItem {
		lgt_FactionPreview = 0x01,
		lgt_TileSet = 0x02,
		lgt_TechTree = 0x04,
		lgt_Map = 0x08,
		lgt_Scenario = 0x10,

		lgt_All =
		(lgt_FactionPreview | lgt_TileSet | lgt_TechTree | lgt_Map |
			lgt_Scenario)
	};

	// =====================================================
	//      class Game
	//
	//      Main game class
	// =====================================================
	class Game :
		public
		ProgramState,
		public
		FileCRCPreCacheThreadCallbackInterface,
		public CustomInputCallbackInterface, public ClientLagCallbackInterface {
	public:
		static const float highlightTime;

	private:
		typedef vector < Ai * > Ais;
		typedef vector < AiInterface * > AiInterfaces;

	private:
		//main data
		World world;
		AiInterfaces aiInterfaces;
		Gui gui;
		GameCamera gameCamera;
		Commander commander;
		Console console;
		ChatManager chatManager;
		ScriptManager scriptManager;

		//misc
		Checksum checksum;
		string loadingText;
		int mouse2d;
		int mouseX;
		int mouseY;               //coords win32Api
		Vec2i mouseCellPos;

		int updateFps, lastUpdateFps, avgUpdateFps;
		int framesToCatchUpAsClient;
		int framesToSlowDownAsClient;
		int receivedTooEarlyInFrames[GameConstants::networkSmoothInterval];
		int
			framesNeededToWaitForServerMessage[GameConstants::
			networkSmoothInterval];
		int
			totalRenderFps,
			renderFps, lastRenderFps, avgRenderFps, currentAvgRenderFpsTotal;
		uint64 tickCount;
		bool paused;
		bool pauseRequestSent;
		bool resumeRequestSent;
		bool pauseStateChanged;
		bool pausedForJoinGame;
		bool pausedBeforeJoinGame;

		bool gameOver;
		bool renderNetworkStatus;
		bool renderInGamePerformance;
		bool showFullConsole;
		bool setMarker;
		bool cameraDragAllowed;
		bool mouseMoved;
		float scrollSpeed;
		bool camLeftButtonDown;
		bool camRightButtonDown;
		bool camUpButtonDown;
		bool camDownButtonDown;

		int speed;
		GraphicMessageBox mainMessageBox;
		GraphicMessageBox errorMessageBox;

		//misc ptr
		ParticleSystem *weatherParticleSystem;
		GameSettings gameSettings;
		Vec2i lastMousePos;
		time_t lastRenderLog2d;
		DisplayMessageFunction originalDisplayMsgCallback;
		bool isFirstRender;

		bool quitTriggeredIndicator;
		bool quitPendingIndicator;

		int original_updateFps;
		int original_cameraFps;

		bool captureAvgTestStatus;
		int updateFpsAvgTest;
		int renderFpsAvgTest;

		int renderExtraTeamColor;
		static const int renderTeamColorCircleBit = 1;
		static const int renderTeamColorPlaneBit = 2;

		bool photoModeEnabled;
		int healthbarMode;
		bool visibleHUD;
		bool timeDisplay;
		bool withRainEffect;
		Program *program;

		bool gameStarted;

		time_t lastMaxUnitCalcTime;

		PopupMenu popupMenu;
		PopupMenu popupMenuSwitchTeams;
		PopupMenu popupMenuDisconnectPlayer;

		std::map < int, int > switchTeamIndexMap;
		GraphicMessageBox switchTeamConfirmMessageBox;

		std::map < int, int > disconnectPlayerIndexMap;
		int playerIndexDisconnect;
		GraphicMessageBox disconnectPlayerConfirmMessageBox;

		int exitGamePopupMenuIndex;
		int joinTeamPopupMenuIndex;
		int pauseGamePopupMenuIndex;
		int saveGamePopupMenuIndex;
		int loadGamePopupMenuIndex;
		//int markCellPopupMenuIndex;
		//int unmarkCellPopupMenuIndex;
		int keyboardSetupPopupMenuIndex;
		int disconnectPlayerPopupMenuIndex;
		//GLuint statelist3dMenu;
		ProgramState *currentUIState;

		bool isMarkCellEnabled;
		Vec2i cellMarkedPos;
		MarkedCell cellMarkedData;
		bool isMarkCellTextEnabled;

		Texture2D *markCellTexture;
		bool isUnMarkCellEnabled;
		Texture2D *unmarkCellTexture;
		std::map < Vec2i, MarkedCell > mapMarkedCellList;
		Texture2D *highlightCellTexture;
		std::vector < MarkedCell > highlightedCells;
		bool masterserverMode;

		StrSound *currentAmbientSound;

		time_t lastNetworkPlayerConnectionCheck;

		time_t lastMasterServerGameStatsDump;

		XmlNode *loadGameNode;
		int lastworldFrameCountForReplay;
		std::vector < std::pair < int, NetworkCommand > > replayCommandList;

		std::vector < string > streamingVideos;
		::Shared::Graphics::VideoPlayer * videoPlayer;
		bool playingStaticVideo;

		Unit *currentCameraFollowUnit;

		std::map < int, HighlightSpecialUnitInfo > unitHighlightList;

		MasterSlaveThreadController masterController;

		bool inJoinGameLoading;
		bool initialResumeSpeedLoops;

		bool quitGameCalled;
		bool disableSpeedChange;

		std::map < int, FowAlphaCellsLookupItem > teamFowAlphaCellsLookupItem;
		std::map < string, int64 > gamePerformanceCounts;

		bool networkPauseGameForLaggedClientsRequested;
		bool networkResumeGameForLaggedClientsRequested;

	public:
		Game();
		Game(Program * program, const GameSettings * gameSettings,
			bool masterserverMode);
		~Game();

		void reInitGUI();
		bool isFlagType1BitEnabled(FlagTypes1 type) const;

		bool isMarkCellMode()const {
			return isMarkCellEnabled;
		}
		const Texture2D *getMarkCellTexture() const {
			return markCellTexture;
		}
		bool isUnMarkCellMode() const {
			return isUnMarkCellEnabled;
		}
		const Texture2D *getUnMarkCellTexture() const {
			return unmarkCellTexture;
		}

		std::map < Vec2i, MarkedCell > getMapMarkedCellList() const {
			return mapMarkedCellList;
		}

		const Texture2D *getHighlightCellTexture() const {
			return highlightCellTexture;
		}
		const std::vector < MarkedCell > *getHighlightedCells() const {
			return &highlightedCells;
		}
		void addOrReplaceInHighlightedCells(MarkedCell mc);

		bool isMasterserverMode()const {
			return masterserverMode;
		}
		//get
		GameSettings *getGameSettings() {
			return &gameSettings;
		}
		void setGameSettings(GameSettings * settings) {
			gameSettings = *settings;
		}
		const GameSettings *getReadOnlyGameSettings() const {
			return &gameSettings;
		}
		void setQuitPendingIndicator() {
			quitPendingIndicator = true;
		}

		const GameCamera *getGameCamera() const {
			return &gameCamera;
		}
		GameCamera *getGameCameraPtr() {
			return &gameCamera;
		}
		const Commander *getCommander() const {
			return &commander;
		}
		Gui *getGuiPtr() {
			return &gui;
		}
		const Gui *getGui() const {
			return &gui;
		}
		Commander *getCommander() {
			return &commander;
		}
		Console *getConsole() {
			return &console;
		}
		ScriptManager *getScriptManager() {
			return &scriptManager;
		}
		World *getWorld() {
			return &world;
		}
		const World *getWorld() const {
			return &world;
		}

		Program *getProgram() {
			return program;
		}

		Vec2i getMouseCellPos()const {
			return mouseCellPos;
		}
		bool isValidMouseCellPos() const;

		void removeUnitFromSelection(const Unit * unit);
		bool addUnitToSelection(Unit * unit);
		void addUnitToGroupSelection(Unit * unit, int groupIndex);
		void removeUnitFromGroupSelection(int unitId, int groupIndex);
		void recallGroupSelection(int groupIndex);

		Uint64 getTickCount() {
			return tickCount;
		}
		bool getPaused();
		void
			setPaused(bool value, bool forceAllowPauseStateChange,
				bool clearCaches, bool joinNetworkGame);
		void tryPauseToggle(bool pause);
		void setupRenderForVideo();
		void saveGame();
		const int getTotalRenderFps() const {
			return totalRenderFps;
		}

		void toggleTeamColorMarker();
		//init
		void resetMembers();
		virtual void load(int loadTypes);
		virtual void load();
		virtual void init();
		virtual void init(bool initForPreviewOnly);
		virtual void update();
		virtual void updateCamera();
		virtual void render();
		virtual void tick();

		//event managing
		virtual bool textInput(std::string text);
		virtual bool sdlKeyDown(SDL_KeyboardEvent key);
		virtual void keyDown(SDL_KeyboardEvent key);
		virtual void keyUp(SDL_KeyboardEvent key);
		virtual void keyPress(SDL_KeyboardEvent c);
		virtual void mouseDownLeft(int x, int y);
		virtual void mouseDownRight(int x, int y);
		virtual void mouseUpCenter(int x, int y);
		virtual void mouseUpLeft(int x, int y);
		virtual void mouseDoubleClickLeft(int x, int y);
		virtual void mouseDoubleClickRight(int x, int y);
		virtual void eventMouseWheel(int x, int y, int zDelta);
		virtual void mouseMove(int x, int y, const MouseState * mouseState);

		virtual bool isInSpecialKeyCaptureEvent() {
			return chatManager.getEditEnabled();
		}

		virtual bool quitTriggered();
		virtual Stats quitAndToggleState();
		Stats quitGame();
		static void exitGameState(Program * program, Stats & endStats);

		void startPerformanceTimer();
		void endPerformanceTimer();
		Vec2i getPerformanceTimerResults();

		//static Texture2D * findFactionLogoTexture(const GameSettings *settings, Logger *logger=NULL,string factionLogoFilter=GameConstants::LOADING_SCREEN_FILE_FILTER, bool useTechDefaultIfFilterNotFound=true);
		static string
			findFactionLogoFile(const GameSettings * settings, Logger * logger =
				NULL, const string & factionLogoFilter =
				GameConstants::LOADING_SCREEN_FILE_FILTER);
		static string
			extractScenarioLogoFile(const GameSettings * settings,
				string & result, bool & loadingImageUsed,
				Logger * logger =
				NULL, string factionLogoFilter =
				GameConstants::LOADING_SCREEN_FILE_FILTER);
		static string extractFactionLogoFile(bool & loadingImageUsed,
			const string & factionName,
			string scenarioDir,
			const string & techName,
			Logger * logger =
			NULL, string factionLogoFilter =
			GameConstants::
			LOADING_SCREEN_FILE_FILTER);
		static string extractTechLogoFile(string scenarioDir,
			const string & techName,
			bool & loadingImageUsed,
			Logger * logger =
			NULL,
			const string & factionLogoFilter =
			GameConstants::
			LOADING_SCREEN_FILE_FILTER);

		void loadHudTexture(const GameSettings * settings);

		bool getGameOver() {
			return gameOver;
		}
		bool hasGameStarted() {
			return gameStarted;
		}
		virtual vector < Texture2D * >processTech(string techName);
		virtual void consoleAddLine(string line);

		void endGame();

		void playStaticVideo(const string & playVideo);
		void playStreamingVideo(const string & playVideo);
		void stopStreamingVideo(const string & playVideo);
		void stopAllVideo();

		string saveGame(string name, const string & path = "saved/");
		static void
			loadGame(string name, Program * programPtr, bool isMasterserverMode,
				const GameSettings * joinGameSettings = NULL);

		void
			addNetworkCommandToReplayList(NetworkCommand * networkCommand,
				int worldFrameCount);

		bool factionLostGame(int factionIndex);

		void addCellMarker(Vec2i cellPos, MarkedCell cellData);
		void removeCellMarker(Vec2i surfaceCellPos, const Faction * faction);
		void showMarker(Vec2i cellPos, MarkedCell cellData);

		void
			highlightUnit(int unitId, float radius, float thickness,
				Vec4f color);
		void unhighlightUnit(int unitId);

		bool showTranslatedTechTree()const;

		void DumpCRCWorldLogIfRequired(string fileSuffix = "");

		bool getDisableSpeedChange()const {
			return disableSpeedChange;
		}
		void setDisableSpeedChange(bool value) {
			disableSpeedChange = value;
		}

		string getGamePerformanceCounts(bool displayWarnings) const;
		virtual void addPerformanceCount(string key, int64 value);
		bool getRenderInGamePerformance()const {
			return renderInGamePerformance;
		}

		void
			ReplaceDisconnectedNetworkPlayersWithAI(bool isNetworkGame, NetworkRole role, bool showMessage);

	private:
		//render
		void render3d();
		void render2d();

		//misc
		void checkWinner();
		void checkWinnerStandard();
		void checkWinnerScripted();
		void setEndGameTeamWinnersAndLosers();

		//bool hasBuilding(const Faction *faction);
		bool factionLostGame(const Faction * faction);
		void incSpeed();
		void decSpeed();
		int getUpdateLoops();

		void showLoseMessageBox();
		void showWinMessageBox();
		void
			showMessageBox(const string & text, const string & header,
				bool toggle);
		void
			showErrorMessageBox(const string & text, const string & header,
				bool toggle);

		void renderWorker();
		static int ErrorDisplayMessage(const char *msg, bool exitApp);

		void calcCameraMoveX();
		void calcCameraMoveZ();

		int getFirstUnusedTeamNumber();
		void updateWorldStats();

		void setupPopupMenus(bool checkClientAdminOverrideOnly);

		string getDebugStats(std::map < int, string > &factionDebugInfo);

		void renderVideoPlayer();

		void updateNetworkMarkedCells();
		void updateNetworkUnMarkedCells();
		void updateNetworkHighligtedCells();

		virtual void processInputText(string text, bool cancelled);

		void startMarkCell();
		void startCameraFollowUnit();

		bool
			switchSetupForSlots(ServerInterface * &serverInterface,
				int startIndex, int endIndex);
		void processNetworkSynchChecksIfRequired();
		Stats getEndGameStats();
		void checkWinnerStandardHeadlessOrObserver();
		void checkWinnerStandardPlayer();
		std::map < int, int > getTeamsAlive();
		void initCamera(Map * map);

		virtual bool
			clientLagHandler(int slotIndex,
				bool networkPauseGameForLaggedClients);
	};

} //end namespace

#endif
