#pragma once

/** Main game file, mainly contains various setup functions.
*/

namespace Gigalomania {
	class Screen;
	class Image;
	class PanelPage;
	class Sample;
}

using Gigalomania::Sample;

using std::vector;
using std::stringstream;
using std::string;

class Invention;
class Weapon;
class Element;
class Sector;
class GameState;
class PlayingGameState;
class Player;
class Application;
class TextEffect;
class Map;
class Tutorial;

#include "common.h"
#include "image.h"
#include "TinyXML/tinyxml.h"

enum GameStateID {
	GAMESTATEID_UNDEFINED = -1,
	GAMESTATEID_CHOOSEGAMETYPE = 0,
	GAMESTATEID_CHOOSEDIFFICULTY,
	GAMESTATEID_CHOOSEPLAYER,
	GAMESTATEID_CHOOSETUTORIAL,
	GAMESTATEID_PLACEMEN,
	GAMESTATEID_PLAYING,
	GAMESTATEID_ENDISLAND,
	GAMESTATEID_GAMECOMPLETE
};

enum GameResult {
	GAMERESULT_UNDEFINED = 0,
	GAMERESULT_WON,
	GAMERESULT_QUIT,
	GAMERESULT_LOST
};

enum DifficultyLevel {
	// don't change the numbers, as will break saved state and saved game compatibility!
	DIFFICULTY_EASY = 0,
	DIFFICULTY_MEDIUM = 1,
	DIFFICULTY_HARD = 2,
	DIFFICULTY_ULTRA = 3,
	DIFFICULTY_N_LEVELS = 4
};

enum GameMode {
	GAMEMODE_SINGLEPLAYER = 0,
	GAMEMODE_MULTIPLAYER_SERVER = 1,
	GAMEMODE_MULTIPLAYER_CLIENT = 2
};

enum GameType {
	// don't change the numbers, as will break saved state compatibility!
	GAMETYPE_SINGLEISLAND = 0,
	GAMETYPE_ALLISLANDS = 1,
	GAMETYPE_TUTORIAL = 2
};

const int default_width_c = 320;
const int default_height_c = 240;

const int infinity_c = 31;
//const int end_epoch_c = 9; // use this to have the last epoch game
const int end_epoch_c = -1; // use this to have the last epoch as being 2100AD
const int cannon_epoch_c = 5;
const int biplane_epoch_c = 6;
const int jetplane_epoch_c = 7;
const int nuclear_epoch_c = 8;
const int spaceship_epoch_c = 9;
const int laser_epoch_c = 9;
const int n_shields_c = 4;
const int n_playershields_c = 16;
const int n_flag_frames_c = 4;
const int max_defender_frames_c = 11;
const int n_attacker_directions_c = 4;
const int max_attacker_frames_c = 9;
const int n_trees_c = 4;
const int n_tree_frames_c = 4;
const int n_nuke_frames_c = 2;
const int n_saucer_frames_c = 4;
const int n_death_flashes_c = 3;
const int n_blue_flashes_c = 7;
const int n_explosions_c = 59;
const int n_coast_c = 8;
const int n_map_sq_c = 16;
const int max_islands_per_epoch_c = 3;

class Game {
	float scale_factor_w; // how much the input graphics are scaled
	float scale_factor_h;
	float scale_width; // the scale of the logical resolution or graphics size wrt the default 320x240 coordinate system
	float scale_height;
	bool onemousebutton;
	bool mobile_ui;
	bool using_old_gfx;
	bool is_testing;

	Application *application;
	Gigalomania::Screen *screen;
	bool paused;
	GameState *gamestate;
	GameState *dispose_gamestate;
	unsigned int lastmousepress_time;

	int frame_counter;
	int time_rate; // time factor
	int real_time;
	int real_loop_time;
	int game_time;
	int loop_time;
	float accumulated_time;
	int mouseTime;

	bool pref_sound_on;
	bool pref_music_on;
	bool pref_disallow_nukes;

	GameMode gameMode;
	GameType gameType;
	DifficultyLevel difficulty_level;
	int human_player;
	Tutorial *tutorial;

	GameStateID gameStateID;
	bool state_changed;
	GameResult gameResult;

	int start_epoch;
	int n_sub_epochs;
	int selected_island;
	bool completed_island[max_islands_per_epoch_c];
	Map *maps[n_epochs_c][max_islands_per_epoch_c];
	Map *map;
	int n_men_store;
	int n_player_suspended;

	void calculateScale(const Gigalomania::Image *image);
	void convertToHiColor(Gigalomania::Image *image) const;
	void processImage(Gigalomania::Image *image, bool old_smooth = true) const;
	bool loadAttackersWalkingImages(const string &gfx_dir, int epoch);
	bool loadOldImages();
	void getDesktopResolution(int *user_width, int *user_height) const;

	const char *getFilename(int slot) const;
	bool readMapProcessLine(int *epoch, int *index, Map **l_map, char *line, const int MAX_LINE, const char *filename);
	bool readMap(const char *filename);
	bool loadGameInfo(DifficultyLevel *difficulty, int *player, int *n_men, int suspended[n_players_c], int *epoch, bool completed[max_islands_per_epoch_c], const char *filename) const;
	bool loadGame(const char *filename);
	GameState *loadStateParseXMLNode(const TiXmlNode *parent);
	void copyFile(const char *src, const char *dst) const;

	bool testFindSoldiersBuildingNewTower(const Sector *sector, int *total, int *squares) const;

	void disposeGameState();

	int getMenPerEpoch() const;
	void updatedEpoch();
	void setEpoch(int epoch);
	void cleanupPlayers();
public:
	Gigalomania::Image *background;
	Gigalomania::Image *background_stars;
	Gigalomania::Image *player_heads_select[n_players_c];
	Gigalomania::Image *player_heads_alliance[n_players_c];
	Gigalomania::Image *grave;
	Gigalomania::Image *land[MAP_N_COLOURS];
	Gigalomania::Image *fortress[n_epochs_c];
	Gigalomania::Image *mine[n_epochs_c];
	Gigalomania::Image *factory[n_epochs_c];
	Gigalomania::Image *lab[n_epochs_c];
	Gigalomania::Image *men[n_epochs_c];
	Gigalomania::Image *unarmed_man;
	Gigalomania::Image *flags[n_players_c][n_flag_frames_c];
	Gigalomania::Image *panel_design;
	Gigalomania::Image *panel_lab;
	Gigalomania::Image *panel_factory;
	Gigalomania::Image *panel_shield;
	Gigalomania::Image *panel_defence;
	Gigalomania::Image *panel_attack;
	Gigalomania::Image *panel_bloody_attack;
	Gigalomania::Image *panel_twoattack;
	Gigalomania::Image *panel_build[N_BUILDINGS];
	Gigalomania::Image *panel_building[N_BUILDINGS];
	Gigalomania::Image *panel_knowndesigns;
	Gigalomania::Image *panel_bigdesign;
	Gigalomania::Image *panel_biglab;
	Gigalomania::Image *panel_bigfactory;
	Gigalomania::Image *panel_bigshield;
	Gigalomania::Image *panel_bigdefence;
	Gigalomania::Image *panel_bigattack;
	Gigalomania::Image *panel_bigbuild;
	Gigalomania::Image *panel_bigknowndesigns;
	Gigalomania::Image *numbers_blue[10];
	Gigalomania::Image *numbers_grey[10];
	Gigalomania::Image *numbers_white[10];
	Gigalomania::Image *numbers_orange[10];
	Gigalomania::Image *numbers_yellow[10];
	Gigalomania::Image *numbers_largegrey[10];
	Gigalomania::Image *numbers_largeshiny[10];
	Gigalomania::Image *numbers_small[n_players_c][10];
	Gigalomania::Image *numbers_half;
	Gigalomania::Image *letters_large[n_font_chars_c];
	Gigalomania::Image *letters_small[n_font_chars_c];
	Gigalomania::Image *mouse_pointers[n_players_c];
	Gigalomania::Image *playershields[n_playershields_c];
	Gigalomania::Image *building_health;
	Gigalomania::Image *dash_grey;
	Gigalomania::Image *icon_shield;
	Gigalomania::Image *icon_defence;
	Gigalomania::Image *icon_weapon;
	Gigalomania::Image *icon_shields[n_shields_c];
	Gigalomania::Image *icon_defences[n_epochs_c];
	Gigalomania::Image *icon_weapons[n_epochs_c];
	Gigalomania::Image *numbered_defences[n_epochs_c];
	Gigalomania::Image *numbered_weapons[n_epochs_c];
	Gigalomania::Image *icon_elements[N_ID];
	Gigalomania::Image *icon_clocks[13];
	Gigalomania::Image *icon_infinity;
	Gigalomania::Image *icon_bc;
	Gigalomania::Image *icon_ad;
	Gigalomania::Image *icon_ad_shiny;
	Gigalomania::Image *icon_towers[n_players_c];
	Gigalomania::Image *icon_armies[n_players_c];
	Gigalomania::Image *icon_nuke_hole;
	Gigalomania::Image *mine_gatherable_small;
	Gigalomania::Image *mine_gatherable_large;
	Gigalomania::Image *icon_ergo;
	Gigalomania::Image *icon_trash;
	Gigalomania::Image *coast_icons[n_coast_c];
	int map_sq_offset, map_sq_coast_offset;
	Gigalomania::Image *map_sq[MAP_N_COLOURS][n_map_sq_c];
	int n_defender_frames[n_epochs_c];
	Gigalomania::Image *defenders[n_players_c][n_epochs_c][max_defender_frames_c];
	Gigalomania::Image *nuke_defences[n_players_c];
	int n_attacker_frames[n_epochs_c+1][n_attacker_directions_c];
	Gigalomania::Image *attackers_walking[n_players_c][n_epochs_c+1][n_attacker_directions_c][max_attacker_frames_c]; // epochs 6-9 are special case!
	Gigalomania::Image *planes[n_players_c][n_epochs_c];
	Gigalomania::Image *nukes[n_players_c][n_nuke_frames_c];
	Gigalomania::Image *saucers[n_players_c][n_saucer_frames_c];
	Gigalomania::Image *attackers_ammo[n_epochs_c][N_ATTACKER_AMMO_DIRS];
	Gigalomania::Image *icon_openpitmine;
	Gigalomania::Image *icon_trees[n_trees_c][n_tree_frames_c];
	vector<Gigalomania::Image *> icon_clutter;
	vector<Gigalomania::Image *> icon_clutter_nuked;
	Gigalomania::Image *flashingmapsquare;
	Gigalomania::Image *mapsquare;
	Gigalomania::Image *arrow_left;
	Gigalomania::Image *arrow_right;
	Gigalomania::Image *death_flashes[n_death_flashes_c];
	Gigalomania::Image *blue_flashes[n_blue_flashes_c];
	Gigalomania::Image *explosions[n_explosions_c];
	Gigalomania::Image *icon_mice[2];
	Gigalomania::Image *icon_speeds[3];
	Gigalomania::Image *smoke_image;
	Gigalomania::Image *background_islands;

	// speech
	Sample *s_design_is_ready;
	Sample *s_ergo;
	Sample *s_advanced_tech;
	Sample *s_fcompleted;
	Sample *s_on_hold;
	Sample *s_running_out_of_elements;
	Sample *s_tower_critical;
	Sample *s_sector_destroyed;
	Sample *s_mine_destroyed;
	Sample *s_factory_destroyed;
	Sample *s_lab_destroyed;
	Sample *s_itis_all_over;
	Sample *s_conquered;
	Sample *s_won;
	Sample *s_weve_nuked_them;
	Sample *s_weve_been_nuked;
	Sample *s_alliance_yes[n_players_c];
	Sample *s_alliance_no[n_players_c];
	Sample *s_alliance_ask[n_players_c];
	Sample *s_quit[n_players_c];
	Sample *s_cant_nuke_ally;

	// effects
	Sample *s_explosion;
	Sample *s_scream;
	Sample *s_buildingdestroyed;
	Sample *s_guiclick;
	Sample *s_biplane;
	Sample *s_jetplane;
	Sample *s_spaceship;

	Sample *music;

	Invention *invention_shields[n_epochs_c];
	Invention *invention_defences[n_epochs_c];
	Weapon *invention_weapons[n_epochs_c];
	Element *elements[N_ID];
	Player *players[n_players_c];

	Game();
	~Game();

	bool loadImages();
	bool loadSamples();
	bool createMaps();

	float getScaleWidth() const {
		return this->scale_width;
	}
	float getScaleHeight() const {
		return this->scale_height;
	}
	void setOneMouseButton(bool onemousebutton) {
		this->onemousebutton = onemousebutton;
	}
	bool isOneMouseButton() const {
		return this->onemousebutton;
	}
	bool oneMouseButtonMode() const;
	void setMobileUI(bool mobile_ui) {
		this->mobile_ui = mobile_ui;
	}
	bool isMobileUI() const {
		return this->mobile_ui;
	}
	bool isUsingOldGfx() const {
		return this->using_old_gfx;
	}
	void setTesting(bool is_testing) {
		this->is_testing = is_testing;
	}
	bool isTesting() const {
		return this->is_testing;
	}
	
	bool createApplication();
	Application *getApplication() {
		return this->application;
	}
	const Application *getApplication() const {
		return this->application;
	}
	bool openScreen(bool fullscreen);
	Gigalomania::Screen *getScreen() {
		return this->screen;
	}
	const Gigalomania::Screen *getScreen() const {
		return this->screen;
	}
	bool isPaused() const;

	void cycleTimeRate() {
		time_rate++;
		if( time_rate > 3 )
			time_rate = 1;
	}
	void increaseTimeRate() {
		if( time_rate > 1 )
			time_rate--;
	}
	void decreaseTimeRate() {
		if( time_rate < 3 )
			time_rate++;
	}
	void setTimeRate(int time_rate);
	int getTimeRate() const {
		return this->time_rate;
	}
	void setRealTime(int real_time);
	int getRealTime() const;
	int getRealLoopTime() const;
	void setGameTime(int game_time);
	int getGameTime() const;
	int getLoopTime() const;
	int getFrameCounter() const {
		return this->frame_counter;
	}
	void updateTime(int time);
	void resetMouseClick();
	int getNClicks();

	void setGameMode(GameMode gameMode) {
		this->gameMode = gameMode;
	}
	GameMode getGameMode() const {
		return this->gameMode;
	}
	void setGameType(GameType gameType) {
		this->gameType = gameType;
	}
	GameType getGameType() const {
		return this->gameType;
	}
	void setDifficultyLevel(DifficultyLevel difficulty_level) {
		this->difficulty_level = difficulty_level;
	}
	DifficultyLevel getDifficultyLevel() const {
		return this->difficulty_level;
	}
	void setGameStateID(GameStateID state, GameState *new_gamestate = NULL);
	GameStateID getGameStateID() const {
		return this->gameStateID;
	}
	void setStateChanged(bool state_changed) {
		this->state_changed = state_changed;
	}
	bool isStateChanged() const {
		return this->state_changed;
	}
	void setGameResult(GameResult gameResult) {
		this->gameResult = gameResult;
	}
	GameResult getGameResult() const {
		return this->gameResult;
	}
	void setupTutorial(const string &id);
	const Tutorial *getTutorial() const {
		return this->tutorial;
	}
	Tutorial *getTutorial() {
		return this->tutorial;
	}
	void setCurrentMap() {
		map = maps[start_epoch][selected_island];
	}
	void setCurrentIsand(int start_epoch, int selected_island);
	const Map *getMap() const;
	Map *getMap();
	const Map *getMap(int i, int j) const {
		return this->maps[i][j];
	}
	int getStartEpoch() const {
		return this->start_epoch;
	}
	int getNSubEpochs() const {
		return this->n_sub_epochs;
	}
	void setPrefSoundOn(bool pref_sound_on) {
		this->pref_sound_on = pref_sound_on;
	}
	bool isPrefSoundOn() const {
		return this->pref_sound_on;
	}
	void setPrefMusicOn(bool pref_music_on) {
		this->pref_music_on = pref_music_on;
	}
	bool isPrefMusicOn() const {
		return this->pref_music_on;
	}
	void setPrefDisallowNukes(bool pref_disallow_nukes) {
		this->pref_disallow_nukes = pref_disallow_nukes;
	}
	bool isPrefDisallowNukes() const {
		return this->pref_disallow_nukes;
	}

	int getMapSqOffset() const {
		return map_sq_offset;
	}
	int getMapSqCoastOffset() const {
		return map_sq_coast_offset;
	}
	void loadPrefs();
	void savePrefs() const;
	bool isDemo() const;

	void deleteState() const;
	void saveState() const;
	bool loadState();

	int getMenAvailable() const;
	int getNSuspended() const;
	void nextEpoch();
	void nextIsland();
	void startIsland();
	void endIsland();
	void returnToChooseIsland();
	void startNewGame();
	void placeTower();
	void newGame();
	void setClientPlayer(int set_client_player);
	bool validPlayer(int player) const;
	void requestQuit(bool force_quit);
	void keypressReturn();
	void togglePause();
	void activate();
	void deactivate();
	void mouseClick(int m_x, int m_y, bool m_left, bool m_middle, bool m_right, bool click);
	void updateGame();
	void drawGame() const;
	void addTextEffect(TextEffect *effect);
	void drawProgress(int percentage) const;

	bool readLineFromRWOps(bool &ok, SDL_RWops *file, char *buffer, char *line, int MAX_LINE, int &buffer_offset, int &newline_index, bool &reached_end);
	bool loadGameInfo(DifficultyLevel *difficulty, int *player, int *n_men, int suspended[n_players_c], int *epoch, bool completed[max_islands_per_epoch_c], int slot);
	bool loadGame(int slot);
	void saveGame(int slot) const;

	void stopMusic();
	void fadeMusic(int duration_ms) const;
	void playMusic();

	void setupPlayers();
	void setupInventions();
	void setupElements();
	bool playerAlive(int player) const;

	void runTests();
};

extern Game *game_g;

void startIsland_g();
void endIsland_g();
void returnToChooseIsland_g();
void startNewGame_g();

// DATADIR is place to look if data not present in application's folder
//#define DATADIR "C:/temp/glmdata" // test on Windows

extern string maps_dirname;
#ifdef DATADIR
extern string alt_maps_dirname;
#endif

extern const int epoch_dates[];
extern const char *epoch_names[];

enum PlayerMode {
	PLAYER_DEMO = -2,
	PLAYER_NONE = -1
};

class Map {
	string name;
	string filename;
	MapColour colour;
	int n_opponents;
	Sector *sectors[map_width_c][map_height_c];
	bool sector_at[map_width_c][map_height_c];
	bool reserved[map_width_c][map_height_c]; // if true, don't use for starting players - used for testing

public:

	Map(MapColour colour,int n_opponents,const char *name);
	~Map();

	MapColour getColour() const {
		return this->colour;
	}
	int getNOpponents() const {
		return this->n_opponents;
	}
	const Sector *getSector(int x, int y) const;
	Sector *getSector(int x, int y);
	bool isSectorAt(int x, int y) const;

	void newSquareAt(int x,int y);
	void createSectors(PlayingGameState *gamestate, int epoch);
#if 0
	void checkSectors() const;
#endif
	void freeSectors();
	const char *getName() const {
		return name.c_str();
	}
	const char *getFilename() const {
		return filename.c_str();
	}
	void setFilename(const char *filename) {
		this->filename = filename;
	}
	int getNSquares() const;
	void draw(int offset_x, int offset_y) const;
	void findRandomSector(int *rx,int *ry) const;
	bool isReserved(int x, int y) const {
		return this->reserved[x][y];
	}
	void setReserved(int x, int y, bool r) {
		this->reserved[x][y] = r;
	}
	void canMoveTo(bool temp[map_width_c][map_height_c], int sx,int sy,int player) const;
	void calculateStats() const;

	void saveStateSectors(stringstream &stream) const;
};

void playGame(int n_args, char *args[]);

#if defined(__ANDROID__)

// JNI for Android

void requestStoragePermission();
void launchUrl(string url);
void showToast(string toast);

#endif

// game constants

const int SHORT_DELAY = 4000;
const int nuke_delay_c = 250;

const int gameticks_per_hour_c = 200;
const int hours_per_day_c = 12;

const int mine_epoch_c = 3;
const int factory_epoch_c = 4;
const int lab_epoch_c = 5;
//const int air_epoch_c = 6;

const int DESIGNTIME_M3 = 20;
const int DESIGNTIME_M2 = 30;
const int DESIGNTIME_M1 = 40;
const int DESIGNTIME_0 = 50;
const int DESIGNTIME_1 = 100;
const int DESIGNTIME_2 = 200;
const int DESIGNTIME_3 = 400;

const int MANUFACTURETIME_0 = 15;
const int MANUFACTURETIME_1 = 30;
const int MANUFACTURETIME_2 = 45;
const int MANUFACTURETIME_3 = 60;

const int BUILDTIME_TOWER = 80;
const int BUILDTIME_MINE = 40;
const int BUILDTIME_FACTORY = 40;
const int BUILDTIME_LAB = 40;

const int ticks_per_frame_c = 100; // game time ticks per frame rate (used for various animated sprites)
const float time_ratio_c = 0.15f; // game time ticks per time ticks
