/*
   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 "PreBattle.h"
#include "Globals.h"
#include "Group.h"
#include "Menu_Base.h"
#include "Stuff.h"
#include "RTS.h"
#include "Inlines.h"
#include "TerrainTile.h"
#include "Random.h"

#include <string>
#include <algorithm>

using std::string;
using std::find;

namespace PreBattle {
//global to this namespace
PBState pbState;

PreBattle_State::PreBattle_State() {
	radarDragging = false;
	viewSide = -1;

	SeedRandom(globalSettings.randomSeed);

	globalSettings.rememberFleets.clear();
	for (int i = 0; i != sides.size(); ++i)
		globalSettings.rememberFleets.push_back(sides[i].name);
	while (globalSettings.rememberFleets.size() < maxPlayers)
		globalSettings.rememberFleets.push_back("");

	SetStartingRects();

	SetupTerrain();

	//FIXME
	for (int i = 0; i != sides.size(); ++i)
		sides[i].myFlag = i;

	for (int i = 0; i != sides.size(); ++i) {
		for (int j = 0; j != sides[i].groups.size(); ++j)
			sides[i].groups[j].SetLaunchWait();
	}

	for (int i = 0; i != sides.size(); ++i) {
		for (int j = 0; j != sides[i].groups.size(); ++j) {
			sides[i].groups[j].ToggleDrawBound();
			sides[i].groups[j].ToggleDrawNumber();
		}
	}

	int totalGroups = 0;

	for (int i = 0; i != sides.size(); ++i)
		totalGroups += sides[i].groups.size();

	int groupsPerFrame = totalGroups / staggerFrames;

	//ensure scripts all start within the first 10 frames
	//also ensure that groupsPerFrame is not 0 (i.e. if there are less than staggerFrames groups)
	while (staggerFrames * groupsPerFrame < totalGroups)
		++groupsPerFrame;

	int currentOpenFrame = 0;

	vector<CoordsInt> doneGroups;

	while (doneGroups.size() != totalGroups) {
		int ranSide = Random() % sides.size();
		int ranGroup = Random() % sides[ranSide].groups.size();

		CoordsInt thisGroup = {ranSide, ranGroup};

		if (find(doneGroups.begin(), doneGroups.end(), thisGroup) != doneGroups.end())
			continue;
		else {
			sides[ranSide].groups[ranGroup].InitAI(currentOpenFrame);
			doneGroups.push_back(thisGroup);
		}

		if (doneGroups.size() % groupsPerFrame == 0)
			++currentOpenFrame;
	}

	topRightBoxRect.x = globalSettings.screenWidth - 210;
	topRightBoxRect.y = 10;
	topRightBoxRect.w = 200;
	topRightBoxRect.h = 200;

	radarRect.x = topRightBoxRect.x + smallBorderSize;
	radarRect.y = topRightBoxRect.y + smallBorderSize;
	radarRect.w = topRightBoxRect.w - (smallBorderSize << 1);
	radarRect.h = topRightBoxRect.h - (smallBorderSize << 1);

	radarSurface = SDL_CreateRGBSurface(SDL_HWSURFACE, radarRect.w, radarRect.h, screenBPP,  0, 0, 0, 0);

	SetStartingPositions();

	SDL_Rect& myStartRect = sides[0].startingRect;

	viewx = myStartRect.x + (myStartRect.w >> 1);
	viewy = myStartRect.y + (myStartRect.h >> 1);

	frameCounter = 0;

	JSDL.LoadMusic(globalSettings.bdp + "music/corruptor.ogg");
	JSDL.PlayMusic();
}

PreBattle_State::~PreBattle_State() {
	for (int i = 0; i != sides.size(); ++i) {
		for (int j = 0; j != sides[i].groups.size(); ++j)
			sides[i].groups[j].ResetForBattle();
	}
	
	//GST_PreBattle case to catch exceptions
	if (gsTo == GST_MainMenu || gsTo == GST_PreBattle)
		Unload();
}

void PreBattle_State::Main() {
	if (globalSettings.batch)
		gsTo = GST_Battle;
	RTS::ScrollAndDrag();

	RTS::DrawWorld();

	DrawAllWindows();
}

void PreBattle_State::MouseD(Uint8 button, Uint16 x, Uint16 y) {
	RTS::RTSMouseD(button, x, y);
}

void PreBattle_State::MouseU(Uint8 button, Uint16 x, Uint16 y) {
	RTS::RTSMouseU(button, x, y);
}

void PreBattle_State::MouseM(Uint8 state, Uint16 x, Uint16 y) {
	RTS::RTSMouseM(state, x, y);
}

void PreBattle_State::Keyboard(SDL_keysym& keysym) {
	RTS::RTSKeyboard(keysym);
	
	if (keysym.sym == SDLK_s)
		gsTo = GST_Battle;
}

void SetStartingRects() {
	worldWidth = startingRectDim * 6;
	worldHeight = startingRectDim * 6;

	sides[0].startingRect.x = startingRectDim;
	if (sides.size() < 3)
		sides[0].startingRect.y = static_cast<float>(worldHeight * 0.5) - (startingRectDim / 2);
	else
		sides[0].startingRect.y = startingRectDim;

	if (sides.size() > 1) {
		sides[1].startingRect.x = worldWidth - startingRectDim * 2;
		if (sides.size() < 3)
			sides[1].startingRect.y = static_cast<float>(worldHeight * 0.5) - (startingRectDim / 2);
		else
			sides[1].startingRect.y = startingRectDim;
	}

	if (sides.size() == 3) {
		sides[2].startingRect.x = static_cast<float>(worldWidth * 0.5) - (startingRectDim / 2);
		sides[2].startingRect.y = worldHeight - startingRectDim * 2;
	}
	else if (sides.size() == 4) {
		sides[2].startingRect.x = startingRectDim;
		sides[2].startingRect.y = worldHeight - startingRectDim * 2;

		sides[3].startingRect.x = worldWidth - startingRectDim * 2;
		sides[3].startingRect.y = worldHeight - startingRectDim * 2;
	}
}

void SetStartingPositions() {
	//start off by placing everyone outside world so we don't get false collisions with as yet unplaced groups
	for (int i = 0; i != sides.size(); ++i) {
		for (int j = 0; j != sides[i].groups.size(); ++j)
  			sides[i].groups[j].SetPos(worldWidth, worldHeight);
	}
	
	for (int i = 0; i != sides.size(); ++i) {
 		vector<bool> placedGroups(sides[i].groups.size(), false);
 		
		for (int j = 0; j != sides[i].groups.size(); ++j) {
	   	if (sides[i].groups[j].GoToStartCoords())
         	placedGroups[j] = true;
    	}

    	for (int j = 0; j != placedGroups.size(); ++j) {
      	if (placedGroups[j] == false) {
				if (pbState == PBS_Position)
					FitGroupsInRemainingSpace(placedGroups);
				else {
					string errorMsg = sides[i].name + " have illegally positioned units";
					throw runtime_error(errorMsg.c_str());
				}
			}
		}
	}
}

void FitGroupsInRemainingSpace(vector<bool> placedGroups, bool ignoreOverlap) {
	/*
	starting at offset (100, 100) try to place new groups
	wherever they fit. After each placement check for overlap
	or outside rect. if so, re-place. If we end up at
	bottom of rect still with leftover groups then just
	place all remaining evenly spaced from top left 
	of rectangle, tough luck if they overlap
	*/

	int tempWidth, tempHeight;
	int border = 100;

	int x = sides[0].startingRect.x + border;
	int y = sides[0].startingRect.y + border;

	for (int i = 0; i != placedGroups.size(); ++i) {
		//deal with ships with parents first. They always return 1
		//to set pos, even if they/their parent is invalidly placed
		if (sides[0].groups[i].GetParentCaSh() != -1) {
			sides[0].groups[i].GoToStartCoords();
			continue;
		}

		if (placedGroups[i] == true)
			continue;

		if (x > sides[0].startingRect.x + sides[0].startingRect.w - HCSWidth - border) {
			y += 300;
			x = sides[0].startingRect.x + border;
		}

		if (y > sides[0].startingRect.y + sides[0].startingRect.h - HCSHeight - border) {
			if (ignoreOverlap)
				throw runtime_error("Couldn't fit all the groups in the starting rect");
			else
				break;
		}

		if (sides[0].groups[i].SetPos(x, y) || ignoreOverlap) {
			placedGroups[i] = true;
			sides[0].groups[i].ChangeStartCoords(x, y);
		} else
			--i;

		if (i > -1)
			sides[0].groups[i].GetDimensions(tempWidth, tempHeight);
		else
			tempWidth = FrWidth;

		x += tempWidth + border;
	}

	//any left over are placed ignoring any overlapping
	for (int i = 0; i != placedGroups.size(); ++i) {
		if (placedGroups[i] == false)
			FitGroupsInRemainingSpace(placedGroups, 1);
	}
}

void SetupTerrain() {
	//load bitmaps into surfaces
	terrainPictures.push_back(JSDL.BitmapHDtoSurface(globalSettings.bdp + "graphics/spacetile.png", 0, 0, 4, 4, lightBlue));
	terrainPictures.push_back(JSDL.BitmapHDtoSurface(globalSettings.bdp + "graphics/spacetile2.png", 0, 0, 4, 4, lightBlue));

	int total_tiles = (worldWidth/100)*(worldHeight/100);
	int num_areas = std::sqrt(static_cast<float>(total_tiles));
	int areas_per_side = std::sqrt(static_cast<float>(num_areas));
	int area_dim = worldWidth / areas_per_side;
	//not num_areas, because num_areas may well not have an exact square root so we need slightly more terrain areas to cover
	//everywhere
	int vector_size = (areas_per_side + 1) * (areas_per_side + 1);
	terrainTree.resize(vector_size);

	int x = 0;
	int y = 0;

	for (int i = 0; i != terrainTree.size(); ++i) {
		terrainTree[i].rect.x = x;
		terrainTree[i].rect.y = y;
		terrainTree[i].rect.w = area_dim;
		terrainTree[i].rect.h = area_dim;

		terrainTree[i].tiles.reserve(vector_size);

		for (int j = 0; j != vector_size; ++j) {
			SDL_Rect tile_rect;
			tile_rect.x = Random() % area_dim + x;
			tile_rect.y = Random() % area_dim + y;
			tile_rect.w = 4;
			tile_rect.h = 4;
			terrainTree[i].tiles.push_back(TerrainTile(terrainPictures[Random() % 2], tile_rect));
		}

		x += area_dim;
		if (x > worldWidth) {
			x = 0;
			y += area_dim;
		}
	}
}


void Unload() {
	UnloadGraphics();
	sides.clear();
	KillAllWindows();
}

void UnloadGraphics() {
	for (int i = 0; i != terrainPictures.size(); ++i)
		SDL_FreeSurface(terrainPictures[i]);

	terrainPictures.clear();
	terrainTree.clear();

	SDL_FreeSurface(radarSurface);
	radarSurface = 0;
}

} //end namespace


