/*
   Copyright (C) 2004 by James Gregory
   Part of the GalaxyHack project
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License.
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY.
 
   See the COPYING file for more details.
*/

#include "ForceSelect.h"
#include "Globals.h"
#include "Logging.h"
#include "RTSInlines.h"

#include <fstream>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/exception.hpp>

using std::ofstream;
using std::endl;
using std::find;

namespace ForceSelect {

ForceSelect_State::ForceSelect_State() {
	//if we are returning from position forces...
	if (sides.size() != 0)
		ToStageTwo();

	else {
		myWindows.push_back(GenWindow(0, 0, FS_LoadSideDW, 0, 0, 0));
		myWindows.push_back(GenWindow(0, 0, FS_LoadSideMenu, windowIDs, 0, 0));
	}
}

ForceSelect_State::~ForceSelect_State() {
	if (gsTo != GST_PreBattle)
		sides.clear();

	KillAllWindows();
}

void ForceSelect_State::Main() {
	DrawWorld();
}


void ForceSelect_State::MouseD(Uint8 button, Uint16 x, Uint16 y) {
	if (button == SDL_BUTTON_RIGHT && sides.size())
		myWindows.push_back(GenWindow(x, y, FS_BasePU, 0, 0, 0));
}

void ForceSelect_State::Keyboard(SDL_keysym& keysym) {
	if (keysym.sym == SDLK_ESCAPE) {
		if (sides.size())
			TrySaveAndExit();
		else
			gsTo = GST_MainMenu;
	}
}
	
void DrawWorld() {
	JSDL.BltFill(screenRect, 0);

	DrawAllWindows();
}

void LoadSide(string& iName, bool newSide) {
	try {
		sides.push_back(Side(iName));
		
		if (newSide) {
			CreateNewSideDirs();
		} else {
			sides[0].FilesToSDStruct();
			sides[0].LoadData(true);
			sides[0].ClearDataStructs();
		}
	} catch (boost::filesystem::filesystem_error e) {
		string errorStr = "\n";
		errorStr += e.what();
		ErrorBackToMM(errorStr);
		return;
	} catch(runtime_error e) {
		ErrorBackToMM(e.what());
		return;
	}

	ToStageTwo();
}

void ToStageTwo() {
	myWindows.push_back(GenWindow(0, 0, FS_SideOptions, 0, 0, WFLAG_STATIC | WFLAG_CANTCLOSE));

	JSDL.LoadMusic(globalSettings.bdp + "music/11radsat.it");
	JSDL.PlayMusic();
}

void SideToFile() {
	sides[0].CheckOverData(false);

	//then the unit data
	AllUnitsToFile();

	//now the actual side
	string concatString = GetFleetDir(0) + sides[0].name + ".dat";

	ofstream output(concatString.c_str(), std::ios::trunc | std::ios::out);

	output << "Version: " << myVersionNumber << endl << endl;

	output << "Commander: " << sides[0].commander << endl;

	output << "Global saved groups: ";
	
	for (int i = 0; i != nAIVars - 1; ++i)
		output << sides[0].saveGroups[i].y << ", ";
	//endl not comma
	output << sides[0].saveGroups[nAIVars - 1].y << endl;

	output << endl;

	output << "Groups:" << endl << endl;

	for (int i = 0; i != sides[0].groups.size(); ++ i) {
		output << uTypeToString[sides[0].groups[i].GetType()] << ":" << endl;

		output << "Data: " << sides[0].groups[i].GetUnitName() << endl;

		output << "AI: " << sides[0].groups[i].GetAIFilename() << endl;

		output << "Parent: " << sides[0].groups[i].GetParentCaSh() << endl;
		
		output << "Saved groups: ";
		for (int j = 0; j != nAIVars - 1; ++j)
			output << sides[0].groups[i].GetSaveGroup(j).y << ", ";
		//endl not comma
		output << sides[0].groups[i].GetSaveGroup(nAIVars - 1).y << endl;

		output << "Starting coords: " << sides[0].groups[i].GetStartingCoords().x << ", " << sides[0].groups[i].GetStartingCoords().y << endl;

		output << endl;
	}

	//an extra blank line on the end for luck
	output << endl;
}

void AddGroups(const string& iName, UnitType iType, int number, CapShipType csType, int parentCaSh) {
	//stable sort doesn't guarantee being put on the end of relevant
	//group of values, plus in any case we need to find the group's number
	int groupNumber;

	if (parentCaSh != -1)
		groupNumber = parentCaSh + 1;
	else
		groupNumber = 0;

	while (groupNumber != sides[0].groups.size()
	        && UnitGTOrEqual(sides[0].groups[groupNumber].GetType(), sides[0].groups[groupNumber].GetUnitName(), iType, iName))
		++groupNumber;

	vector<Group>::iterator groupIter = sides[0].groups.begin();
	groupIter += groupNumber;

	CoordsInt startingCoords = {startingRectDim, startingRectDim};

	try {
		for (int i = groupNumber; i != groupNumber + number; ++i) {			
			//inform must come before actual adding
			GroupAddDelInform(i, 1);
			
			switch (iType) {
			case UT_CaShUnit:
				groupIter = sides[0].groups.insert(groupIter, Group(iType, 0, i, iName, "", csType, vector<int>(nAIVars, 0), startingCoords)) + 1;
				break;

			case UT_FrUnit:
			case UT_SmShUnit:
				groupIter = sides[0].groups.insert(groupIter, Group(iType, 0, i, iName, "", parentCaSh, vector<int>(nAIVars, 0), startingCoords)) + 1;
				break;
			}
		}

		if (!DoesFileExist(GetFleetDir(0) + iName + ".dat"))
			UnitToFile(groupNumber);

	} catch(runtime_error e) {
		const char* error = e.what();
		WriteLog(error);
		globalErrorString = error;
		gsTo = GST_MainMenu;
		return;
	}
}

void UnitToFile(int nGroup) {
	UnitType uType = sides[0].groups[nGroup].GetType();

	string concatString = GetFleetDir(0) + sides[0].groups[nGroup].GetUnitName() + ".dat";

	ofstream output(concatString.c_str(), std::ios::trunc | std::ios::out);

	output << "Type: " << GetFullTypeName(0, nGroup) << endl;

	output << "Picture: " << sides[0].groups[nGroup].GetRealPictureName() << endl;

	output << endl;

	if (sides[0].groups[nGroup].GetType() != UT_CaShUnit) {
		output << "Engine: " << sides[0].groups[nGroup].GetEngineName() << endl;
		output << "ArmourType: " << sides[0].groups[nGroup].GetArmourName() << endl;
		output << "ShieldType: " << sides[0].groups[nGroup].GetShieldName() << endl;

		output << endl;
	}

	output << "SmallType: " << weaponLookup[sides[0].groups[nGroup].GetSmallType()].name << endl;

	output << endl;

	output << "BigType: " << weaponLookup[sides[0].groups[nGroup].GetBigType()].name << endl;

	output << endl;

	if (uType != UT_SmShUnit) {
		vector<CoordsInt> smallPositions;
		CoordsInt bigPosition;
		sides[0].groups[nGroup].GetWeapCoords(smallPositions, bigPosition);

		output << "SmallPositions:" << endl << "{" << endl;

		for (int i = 0; i != smallPositions.size(); ++i)
			output << smallPositions[i].x << ", " << smallPositions[i].y << ";" << endl;

		output << "}" << endl;

		output << endl;

		output << "BigPosition: " << bigPosition.x << ", " << bigPosition.y << endl;
	}

	output << endl;
}

void AllUnitsToFile() {
	vector<string> alreadySavedQ;

	for (int i = 0; i != sides[0].groups.size(); ++i) {
		string unitName = sides[0].groups[i].GetUnitName();
		if (find(alreadySavedQ.begin(), alreadySavedQ.end(), unitName) == alreadySavedQ.end()) {
			alreadySavedQ.push_back(unitName);
			UnitToFile(i);
		}
	}
}

void DeleteGroupCheck(int nGroup) {
	if (WhichGSharesName(nGroup) == -1)
		UnitToFile(nGroup);
		
	DeleteGroup(nGroup);
}

void DeleteGroup(int nGroup) {
	//kill contents if we are deleting a capital ship
	if (sides[0].groups[nGroup].GetType() == UT_CaShUnit) {
		for (int i = 0; i != sides[0].groups.size(); ) {
			if (sides[0].groups[i].GetParentCaSh() == nGroup)
				DeleteGroup(i);
			else
				++i;
		}
	}

	//inform must come before delete
	GroupAddDelInform(nGroup, -1);

	vector<Group>::iterator groupIter = sides[0].groups.begin();

	groupIter+= nGroup;

	sides[0].groups.erase(groupIter);
}

int WhichGSharesName(int nGroup) {
	string unitName = sides[0].groups[nGroup].GetUnitName();

	for (int i = 0; i != sides[0].groups.size(); ++i) {
		if (i == nGroup)
			continue;

		if (sides[0].groups[i].GetUnitName() == unitName)
			return i;
	}

	return -1;
}

bool UnitNameSharedQ(int& currentGroup, int changedGroup, int addOrDelete) {
	int newGroup;

	if (changedGroup == currentGroup && addOrDelete == 0) {
		newGroup = WhichGSharesName(currentGroup);

		if (newGroup != -1)
			currentGroup = newGroup;

		else
			return 0;
	}
	else if (currentGroup >= changedGroup) {
		//add
		if (addOrDelete)
			++currentGroup;
		//minus
		else
			--currentGroup;
	}

	return 1;
}

//for sorting, small ships worth most, then cap ships, then frigates
bool UnitGTOrEqual(UnitType typeOne, const string& nameOne, UnitType typeTwo, const string& nameTwo) {
	switch (typeOne) {
	case UT_SmShUnit:
		if (typeTwo == UT_SmShUnit)
			return NameGTOrEqual(nameOne, nameTwo);
		else
			return 1;
		break;

		//no sorting on name
	case UT_CaShUnit:
		if (typeTwo == UT_SmShUnit)
			return 0;
		else
			return 1;
		break;

	case UT_FrUnit:
		if (typeTwo == UT_FrUnit)
			return NameGTOrEqual(nameOne, nameTwo);
		else
			return 0;
		break;
	}
}

bool NameGTOrEqual(const string& nameOne, const string& nameTwo) {
	if (nameOne[0] < nameTwo[0])
		return 1;
	else
		return 0;
}

void ChangeUnitPic(const string& unitName, const string& newPic) {
	for (int i = 0; i != sides[0].groups.size(); ++i) {
		if (unitName == sides[0].groups[i].GetUnitName())
			sides[0].groups[i].ChangeUnitPic(newPic);
	}
}

void ChangeUnitSmallWeapons(const string& unitName, const string& newType) {
	for (int i = 0; i != sides[0].groups.size(); ++i) {
		if (unitName == sides[0].groups[i].GetUnitName())
			sides[0].groups[i].ChangeSmallType(weaponLoadLookup[newType]);
	}
}

void ChangeUnitBigWeapons(const string& unitName, const string& newType) {
	for (int i = 0; i != sides[0].groups.size(); ++i) {
		if (unitName == sides[0].groups[i].GetUnitName())
			sides[0].groups[i].ChangeBigType(weaponLoadLookup[newType]);
	}
}

void ChangeUnitEngine(const string& unitName, const string& newStat) {
	for (int i = 0; i != sides[0].groups.size(); ++i) {
		if (unitName == sides[0].groups[i].GetUnitName())
			sides[0].groups[i].ChangeEngine(newStat);
	}
}

void ChangeUnitArmour(const string& unitName, const string& newStat) {
	for (int i = 0; i != sides[0].groups.size(); ++i) {
		if (unitName == sides[0].groups[i].GetUnitName())
			sides[0].groups[i].ChangeArmour(newStat);
	}
}

void ChangeUnitShield(const string& unitName, const string& newStat) {
	for (int i = 0; i != sides[0].groups.size(); ++i) {
		if (unitName == sides[0].groups[i].GetUnitName())
			sides[0].groups[i].ChangeShield(newStat);
	}
}

void ChangeUnitAI(const string& unitName, const string& newStat) {
	for (int i = 0; i != sides[0].groups.size(); ++i) {
		if (unitName == sides[0].groups[i].GetUnitName())
			sides[0].groups[i].ChangeAIScript(newStat);
	}
}

void ChangeUnitCSType(const string& unitName, CapShipType newStat) {
	for (int i = 0; i != sides[0].groups.size(); ++i) {
		if (unitName == sides[0].groups[i].GetUnitName())
			sides[0].groups[i].ChangeCSType(newStat);
	}
}

void GroupAddDelInform(int groupNumber, int howMany) {
	for (int i = 0; i != sides[0].groups.size(); ++i)
		sides[0].groups[i].GroupAddDelInform(groupNumber, howMany);

	MessageWindows(FS_GroupNumChange, groupNumber, howMany, all_constant, none_constant);
}

void TrySaveAndExit() {
	try {
		SideToFile();
	} catch (runtime_error e) {
		string errorStr = e.what();
		myWindows.push_back(GenWindow(200, 200, FS_ForceExitQ, errorStr.c_str(), 0, 0));

		return;
	}

	gsTo = GST_MainMenu;
}

void CreateNewGroupWins(const string& iName, UnitType iType, bool newFlag) {
	//we also check for having too many groups in AddGroups option of BasePU
	int maxAllowedToAdd = maxGroups - sides[0].groups.size();
	if (maxAllowedToAdd > 10)
		maxAllowedToAdd = 10;

	string title;
	if (maxAllowedToAdd != 1)
		title = "Add new " + iName + " group/s";
	else
		title = "Add a new " + iName + " group";

	myWindows.push_back(GenWindow(0, 0, WC_LargeBlankDW, title, 0, 0));

	int mainParentID = windowIDs;
	int sliderIDOne = none_constant;
	int sliderIDTwo = none_constant;

	//how many
	if (maxAllowedToAdd != 1) {
		myWindows.push_back(GenWindow(220, 220, WC_SliderWithUnits, 1, 1, maxAllowedToAdd, "Add", "groups", mainParentID, WFLAG_STATIC | WFLAG_CANTCLOSE));
		sliderIDOne = windowIDs;
	}

	if (iType == UT_CaShUnit && newFlag) {
		//CaSh type
		myWindows.push_back(GenWindow(220, 260 + sliderWinHeight, FS_CapShipTypeSlider, mainParentID, 0, WFLAG_STATIC | WFLAG_CANTCLOSE));
		sliderIDTwo = windowIDs;
	} else
		sliderIDTwo = none_constant;

	myWindows.push_back(GenWindow(700, 500, FS_NewGroupChoicesOK, iName, iType, sliderIDOne, sliderIDTwo, mainParentID));
}

void CreateNewSideDirs() {
	string fleetDir = GetFleetDir(0);
	namespace fs = boost::filesystem;
	fs::path tempPath(fleetDir);
	fs::create_directory(tempPath);
	fleetDir += "unitpictures/";
	tempPath = fleetDir;
	fs::create_directory(tempPath);
	
	CreatePictureDirs(fleetDir + "blue");
	CreatePictureDirs(fleetDir + "green");
	CreatePictureDirs(fleetDir + "red");
	CreatePictureDirs(fleetDir + "yellow");
}

void CreatePictureDirs(const string& baseDir) {
	namespace fs = boost::filesystem;
	fs::path tempPath(baseDir);
	fs::create_directory(tempPath);
	tempPath = baseDir + "/capship";
	fs::create_directory(tempPath);
	tempPath = baseDir + "/frigate";
	fs::create_directory(tempPath);
	tempPath = baseDir + "/smallship";
	fs::create_directory(tempPath);
}

} //end namespace


