/*
   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 "GenWindow.h"
#include "Globals.h"
#include "MainMenu.h"
#include "ForceSelect.h"
#include "SetupBattle.h"
#include "RTS.h"
#include "Score.h"

#include <string>
#include <list>

using std::string;
using std::list;

/*IMPORTANT NOTE:
WinMouseD() and WinKeyboard() rely upon windows returning true if they are going to do anything. The fact that a window can create another window in the same list as itself can otherwise cause issues. It would seem adding a window mid loop moves the reverse iterator back one somehow, meaning a window can accidently get input twice.
 
For mouse input this is automatically handled by GenWindow_Base::MouseD(), which will always return true if the mouse is over a given window.

Another way of doing this would be to make a copy of the myWindows list each frame, make it so windows only push back new windows on to this copy, and then at the end of each frame have the copied (and maybe altered) list assigned back to the real list. The fact that GenWindows are a handle class means this wouldn't actually literally copy the entire contents of the window list twice each frame, but it would seem rather inefficient to me, nonetheless.
*/

/*ANOTHER NOTE:
Any pushing back of keyboard-reading windows must come after WinKeyboard(), or the new window will get the keyboard input too
*/

void UpdateWindows() {
	if (globalSettings.batch)
		return;	

	//altering list whilst iterating through it, dodgy, see note above, some hackery here
	list<GenWindow>::iterator endIter = myWindows.end();

	for (list<GenWindow>::iterator iter = myWindows.begin(); iter != endIter; ) {
		if (iter->TakeActivate()) {
			myWindows.splice(myWindows.end(), myWindows, iter);
			iter = myWindows.begin();
			endIter = myWindows.end();
		}
		else
			++iter;
	}

	for (list<GenWindow>::iterator iter = myWindows.begin(); iter != myWindows.end();) {
		if (iter->GetClosed())
			iter = myWindows.erase(iter);
		else
			++iter;
	}
	
	for (list<GenWindow>::reverse_iterator iter = myWindows.rbegin(); iter != myWindows.rend(); ++iter)
		iter->Update();
}

bool WinMouseD(Uint8 button, Uint16 x, Uint16 y) {
	//means no windowed input
	bool ret = false;

	//break as soon as the front most window returns true
	for (list<GenWindow>::reverse_iterator iter = myWindows.rbegin(); iter != myWindows.rend(); ++iter) {
		if (iter->MouseD(button, x, y)) {
			ret = true;
			break;
		}
	}

	return ret;
}

void WinMouseM(Uint8 state, Uint16 x, Uint16 y) {
	//break as soon as the front most window deals with motion
	for (list<GenWindow>::reverse_iterator iter = myWindows.rbegin(); iter != myWindows.rend(); ++iter) {
		if (iter->MouseM(state, x, y))
			return;
	}
}

bool WinKeyboard(SDL_keysym& keysym) {
	bool ret = false;
	
	//break as soon as the front most window has some keyboard input
	for (list<GenWindow>::reverse_iterator iter = myWindows.rbegin(); iter != myWindows.rend(); ++iter) {
		if (iter->Keyboard(keysym)) {
			ret = true;
			break;
		}
	}

	return ret;
}

void MessageWindows(WindowChoice theMessage, int paremOne, int paremTwo, int targetID, int sourceID) {
	for (list<GenWindow>::iterator iter = myWindows.begin(); iter != myWindows.end(); ++iter)
		iter->WinMessage(theMessage, paremOne, paremTwo, targetID, sourceID);
}

void CreateInfoString(const string& iTheString, bool force) {
	if (!anInfoString || force)
		myWindows.push_back(GenWindow(iTheString));
}

void DrawAllWindows() {
	if (globalSettings.batch)
		return;

	for (list<GenWindow>::iterator iter = myWindows.begin(); iter != myWindows.end(); ++iter)
		iter->DrawSelf();
}

void KillAllWindows() {
	//we can't use clear because with clear the size of the list
	//isn't set to 0 until the clearing has finished, yet
	//the base window destructor has to find the size
	for (list<GenWindow>::iterator iter = myWindows.begin(); iter != myWindows.end();)
		iter = myWindows.erase(iter);
}

GenWindow_Base* LocateWindow(int findID) {
	for (list<GenWindow>::iterator iter = myWindows.begin(); iter != myWindows.end(); ++iter) {
		GenWindow_Base* winPointer = iter->GetPointer();
		if (winPointer->myID == findID)
			return winPointer;
	}

	char output[80];
	sprintf(output, "Unable to find window with ID %d", findID);
	throw runtime_error(output);

	return 0;
}

GenWindow::GenWindow(int ix, int iy, WindowChoice iType, int paremOne, int paremTwo, int flags) {
	switch (iType) {
	case MM_MainSM:
		hWindow = new MainMenu::MainSM();
		break;
		
	case MM_Options:
		hWindow = new MainMenu::MainOptions(ix, iy);
		break;

	case Opt_StandardOptions:
		hWindow = new StandardOptions(ix, iy, none_constant, flags);
		break;

	case Opt_SetResolution:
		hWindow = new ChooseResolution();
		break;

	case Opt_SetWindowColor:
		hWindow = new WindowColorSlider(ix, iy, flags);
		break;

	case FS_LoadSideDW:
		hWindow = new ForceSelect::LoadSideDW();
		break;

	case FS_LoadSideMenu:
		hWindow = new ForceSelect::LoadSideSM(paremOne);
		break;

	case FS_NewSideName:
		hWindow = new ForceSelect::NewSideName();
		break;

	case FS_Commander:
		hWindow = new ForceSelect::ChangeCommanderName();
		break;

	case FS_WhichSaveGroup:
		hWindow = new ForceSelect::WhichSaveGroup(paremOne, static_cast<bool>(paremTwo));
		break;

	case FS_BasePU:
		hWindow = new ForceSelect::BasePU(ix, iy);
		break;

	case FS_AddGroupMenu:
		hWindow = new ForceSelect::AddGroupMenu(paremOne);
		break;

	case FS_CapShipTypeSlider:
		hWindow = new ForceSelect::CapShipTypeSlider(ix, iy, paremOne, flags);
		break;

	case FS_SideOptions:
		hWindow = new ForceSelect::SideOptions(ix, iy, flags);
		break;

	case FS_SideOptionsPU:
		hWindow = new ForceSelect::SideOptionsPU(ix, iy);
		break;
		
	case FS_ExitNoSaveQ:
		hWindow = new ForceSelect::ExitNoSaveQ();
		break;

	case FS_EditGroup:
		hWindow = new ForceSelect::EditGroup(ix, iy, paremOne, flags & WFLAG_TILED);
		break;

	case FS_EditGroupPU:
		hWindow = new ForceSelect::EditGroupPU(ix, iy, paremOne);
		break;

	case FS_AIScriptMenu:
		hWindow = new ForceSelect::SetAIScriptMenu(paremOne, paremTwo);
		break;

	case FS_EditUnit:
		hWindow = new ForceSelect::EditUnit(ix, iy, paremOne);
		break;

	case FS_EditUnitPU:
		hWindow = new ForceSelect::EditUnitPU(ix, iy, paremOne);
		break;

	case FS_PictureMenu:
		hWindow = new ForceSelect::SetUnitPicMenu(paremOne, paremTwo);
		break;

		//0 is small weapons, 1 is big weapons
	case FS_WeaponsDW:
		hWindow = new ForceSelect::WeaponsDW(ix, ix, paremOne, paremTwo);
		break;

	case FS_SmallWeaponsMenu:
		hWindow = new ForceSelect::SmallWeaponsMenu(ix, ix, paremOne, paremTwo);
		break;

	case FS_BigWeaponsMenu:
		hWindow = new ForceSelect::BigWeaponsMenu(ix, ix, paremOne, paremTwo);
		break;

	case FS_EquipmentDW:
		hWindow = new ForceSelect::EquipmentDW(ix, ix, paremOne, paremTwo);
		break;

	case FS_EngineMenu:
		hWindow = new ForceSelect::EngineMenu(ix, ix, paremOne, paremTwo);
		break;

	case FS_ArmourMenu:
		hWindow = new ForceSelect::ArmourMenu(ix, ix, paremOne, paremTwo);
		break;

	case FS_ShieldMenu:
		hWindow = new ForceSelect::ShieldMenu(ix, ix, paremOne, paremTwo);
		break;

	case FS_CSType:
		hWindow = new ForceSelect::CSTypeMenu(ix, ix, paremOne);
		break;
		
	case SB_BasePU:
		hWindow = new SetupBattle::BasePU(ix, iy);
		break;

	case SB_LoadSideMenu:
		hWindow = new SetupBattle::LoadSideMenu(paremOne, paremTwo);
		break;

	case SB_PlayerList:
		hWindow = new SetupBattle::PlayerList(ix, iy, flags);
		break;
		
	case SB_RandomSeed:
		hWindow = new SetupBattle::RandomSeed(ix, iy, flags);
		break;

	case RTS_BasePU:
		hWindow = new RTS::BasePU(ix, iy);
		break;

	case RTS_OptPU:
		hWindow = new RTS::OptPU(ix, iy);
		break;

	case RTS_GameInfo:
		hWindow = new RTS::GameInfo(ix, iy, flags);
		break;
	
	case RTS_SideStats:
		hWindow = new RTS::SideStatsInfo(ix, iy, paremOne);
		break;
		
	case RTS_SideVars:
		hWindow = new RTS::SideVarsInfo(ix, iy, paremOne);
		break;
		
	case RTS_SideSaveGroups:
		hWindow = new RTS::SideSaveGroupsInfo(ix, iy, paremOne);
		break;
		
	case RTS_SideAIDebug:
		hWindow = new RTS::SideAIErrors(ix, iy, paremOne);
		break;

	case RTS_InfoChoicePU:
		hWindow = new RTS::InfoChoicePU(ix, iy, paremOne, paremTwo);
		break;

	case RTS_GroupStats:
		hWindow = new RTS::GroupStatsInfo(ix, iy, paremOne, paremTwo, flags);
		break;

	case RTS_GroupShieldsInfo:
		hWindow = new RTS::GroupShieldsInfo(ix, iy, paremOne, paremTwo, flags);
		break;

	case RTS_GroupStatusReport:
		hWindow = new RTS::GroupStatusReport(ix, iy, paremOne, paremTwo, flags);
		break;

	case RTS_GroupAIReport:
		hWindow = new RTS::GroupAIReport(ix, iy, paremOne, paremTwo, flags);
		break;
		
	case RTS_GroupVars:
		hWindow = new RTS::GroupVarsInfo(ix, iy, paremOne, paremTwo, flags);
		break;
		
	case RTS_GroupSaveGroups:
		hWindow = new RTS::GroupSaveGroupsInfo(ix, iy, paremOne, paremTwo, flags);
		break;
		
	case RTS_GroupTimers:
		hWindow = new RTS::GroupTimersInfo(ix, iy, paremOne, paremTwo, flags);
		break;

	case RTS_RestartQ:
		hWindow = new RTS::RestartQ();
		break;
		
	case RTS_SetGameSpeed:
		hWindow = new RTS::GameSpeedSlider(ix, iy, flags);
		break;
		
	case RTS_SetScrollSpeed:
		hWindow = new RTS::ScrollSpeedSlider(ix, iy, flags);
		break;

	case Score_BasePU:
		hWindow = new Score::BasePU(ix, iy);
		break;

	default:
		throw runtime_error("GenWindow standard constructor didn't recognise window type");
		break;
	}
}

GenWindow::GenWindow(int ix, int iy, WindowChoice iType, int paremOne, int paremTwo, int paremThree, int paremFour, int flags) {
	switch (iType) {
	case RTS_InfoChoicePU:
		hWindow = new RTS::InfoChoicePU(ix, iy, paremOne, paremTwo, paremThree, paremFour);
		break;

	default:
		throw runtime_error("GenWindow four parems constructor didn't recognise window type");
		break;
	}
}

GenWindow::GenWindow(WindowChoice iType, int iMySide, int iMyGroup, WindowChoice iChoiceType) {
	hWindow = new SidePU(iMySide, iMyGroup, iChoiceType);
}

GenWindow::GenWindow(WindowChoice iType, int iMySide, int iMyGroup, WindowChoice iChoiceType, int iWhichSaveGroup) {
	hWindow = new SidePU(iMySide, iMyGroup, iChoiceType, iWhichSaveGroup);
}


GenWindow::GenWindow(int ix, int iy, WindowChoice iType, const string& iString, int paremOne, int paremTwo) {
	switch (iType) {
	case WC_LargeBlankDW:
		hWindow = new LargeBlankDW(iString);
		break;
		
	case FS_LoadSidePU:
		hWindow = new ForceSelect::LoadSidePU(ix, iy, iString, paremOne);
		break;
		
	case FS_DeleteFleet:
		hWindow = new ForceSelect::DeleteFleet(iString, paremOne);
		break;
		
	case FS_CopyFleet:
		hWindow = new ForceSelect::CopyFleet(iString, paremOne);
		break;

	case FS_AddGroupPU:
		hWindow = new ForceSelect::AddGroupPU(ix, iy, iString, paremOne);
		break;

	case FS_ForceExitQ:
		hWindow = new ForceSelect::ForceExitQ(iString);
		break;

	case FS_ChangeAIScriptPU:
		hWindow = new ForceSelect::SetAIScriptPU(iString, paremOne, paremTwo);
		break;

	default:
		throw runtime_error("GenWindow int, int, WC, string, int constructor didn't recognise window type");
		break;
	}
}

GenWindow::GenWindow(int ix, int iy, WindowChoice iType, const string& iString, UnitType iUnitType, int paremOne, int paremTwo, int paremThree) {
	switch (iType) {
	case FS_NewGroupChoicesOK:
		hWindow = new ForceSelect::NewGroupChoicesOK(ix, iy, iString, iUnitType, paremOne, paremTwo, paremThree);
		break;

	case FS_NewGroupParentPU:
		hWindow = new ForceSelect::NewGroupParentPU(iString, iUnitType, paremOne);
		break;

	default:
		throw runtime_error("int, int, WC, string, UT, int, int, int windows constructor didn't recognise window type");
		break;
	}
}

GenWindow::GenWindow(int ix, int iy, WindowChoice iType, int iSliderVar, int iVarMin, int iVarMax, const string& iVarName, const string& iVarUnits, int iParentID, int flags) {
	hWindow = new SliderWithUnits(ix, iy, iSliderVar, iVarMin, iVarMax, iVarName, iVarUnits, iParentID, flags);
}

GenWindow::GenWindow(int ix, int iy, WindowChoice iType, int* iVarPointer, int iVarMin, int iVarMax, const string& iVarName, const string& iVarUnits, int iParentID, int flags) {
	hWindow = new SliderWithUnits(ix, iy, iVarPointer, iVarMin, iVarMax, iVarName, iVarUnits, iParentID, flags);
}

GenWindow::GenWindow(const string& iTheString) {
	hWindow = new InfoString(iTheString);
}

GenWindow::GenWindow(WindowChoice iType, UnitType iUnitType, int paremOne) {
	switch (iType) {
	case FS_NewUnitName:
		hWindow = new ForceSelect::NewUnitName(iUnitType, paremOne);
		break;

	default:
		throw runtime_error("GenWindow WC, UT, int constructor didn't recognise window type");
		break;
	}
}

GenWindow::GenWindow(WindowChoice iType, const string& stringOne, const string& stringTwo, int iParentID) {
	switch (iType) {
	case WC_DeleteBox:
		hWindow = new DeleteBox(stringOne, stringTwo, iParentID);
		break;

	case WC_CopyBox:
		hWindow = new CopyBox(stringOne, stringTwo, iParentID);
		break;

	default:
		throw runtime_error("GenWindow Copy/DeleteBox constructor didn't recognise window type");
		break;
	}
}

////

bool GenWindow::TakeActivate() {
	bool ret = hWindow->activate;
	hWindow->activate = 0;
	return ret;
}

void GenWindow::WinMessage(WindowChoice theMessage, int paremOne, int paremTwo, int targetID, int sourceID) {
	if (sourceID != hWindow->myID)
		hWindow->WinMessage(theMessage, paremOne, paremTwo, targetID, sourceID);
}

