//     ____                _       __               
//    / __ )____  _____   | |     / /___ ___________
//   / __  / __ \/ ___/   | | /| / / __ `/ ___/ ___/
//  / /_/ / /_/ (__  )    | |/ |/ / /_/ / /  (__  ) 
// /_____/\____/____/     |__/|__/\__,_/_/  /____/  
//                                              
//       A futuristic real-time strategy game.
//          This file is part of Bos Wars.
//
/**@name ui.cpp - The user interface globals. */
//
//      (c) Copyright 1999-2010 by Lutz Sammer, Andreas Arens, and
//                                 Jimmy Salmon
//
//      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; only version 2 of the License.
//
//      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, write to the Free Software
//      Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
//      02111-1307, USA.
//

//@{

/*----------------------------------------------------------------------------
--  Includes
----------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "stratagus.h"
#include "video.h"
#include "font.h"
#include "interface.h"
#include "map.h"
#include "ui.h"
#include "menus.h"
#include "iolib.h"
#include "unit.h"
#include "title.h"

/*----------------------------------------------------------------------------
-- Variables
----------------------------------------------------------------------------*/

bool RightButtonAttacks;                   /// right button attacks


/**
**  The user interface configuration
*/
CUserInterface UI;

/*----------------------------------------------------------------------------
-- Functions
----------------------------------------------------------------------------*/

CUserInterface::CUserInterface() :
	MouseScroll(false), KeyScroll(false),
	MouseScrollSpeedDefault(0), MouseScrollSpeedControl(0),
	MouseWarpX(0), MouseWarpY(0),
	SingleSelectedButton(NULL),
	MaxSelectedFont(NULL), MaxSelectedTextX(0), MaxSelectedTextY(0),
	SingleTrainingButton(NULL),
	SingleTrainingFont(NULL), SingleTrainingTextX(0), SingleTrainingTextY(0),
	TrainingFont(NULL), TrainingTextX(0), TrainingTextY(0),
	CompletedBarColor(0), CompletedBarShadow(0),
	ViewportMode(VIEWPORT_SINGLE), MouseViewport(NULL),
	SelectedViewport(NULL), NumViewports(0),
	MessageFont(NULL), MessageScrollSpeed(5),
	ViewportCursorColor(0), Offset640X(0), Offset480Y(0)
{
	memset(&CompletedBarColorRGB, 0, sizeof(CompletedBarColorRGB));

	memset(SavedMapPositionX, 0, sizeof(SavedMapPositionX));
	memset(SavedMapPositionY, 0, sizeof(SavedMapPositionY));

	Point.Name = "cursor-point";
	Glass.Name = "cursor-glass";
	Cross.Name = "cursor-cross";
	YellowHair.Name = "cursor-yellow-hair";
	GreenHair.Name = "cursor-green-hair";
	RedHair.Name = "cursor-red-hair";
	Scroll.Name = "cursor-scroll";

	ArrowE.Name = "cursor-arrow-e";
	ArrowNE.Name = "cursor-arrow-ne";
	ArrowN.Name = "cursor-arrow-n";
	ArrowNW.Name = "cursor-arrow-nw";
	ArrowW.Name = "cursor-arrow-w";
	ArrowSW.Name = "cursor-arrow-sw";
	ArrowS.Name = "cursor-arrow-s";
	ArrowSE.Name = "cursor-arrow-se";

	NormalFontColor = "light-blue";
	ReverseFontColor = "yellow";
}


/**
**  Initialize the user interface.
*/
void InitUserInterface(void)
{
	UI.Offset640X = (Video.Width - 640) / 2;
	UI.Offset480Y = (Video.Height - 480) / 2;

	//
	// Calculations
	//
	if (Map.Info.MapWidth) {
		if (UI.MapArea.EndX > Map.Info.MapWidth * TileSizeX - 1) {
			UI.MapArea.EndX = Map.Info.MapWidth * TileSizeX - 1;
		}
		if (UI.MapArea.EndY > Map.Info.MapHeight * TileSizeY - 1) {
			UI.MapArea.EndY = Map.Info.MapHeight * TileSizeY - 1;
		}
	}

	UI.SelectedViewport = UI.Viewports;

	SetViewportMode(VIEWPORT_SINGLE);

	UI.CompletedBarColor = Video.MapRGB(TheScreen->format,
		UI.CompletedBarColorRGB.r,
		UI.CompletedBarColorRGB.g,
		UI.CompletedBarColorRGB.b);
	UI.ViewportCursorColor = ColorWhite;
}

/**
**  Load Cursor.
*/
void CursorConfig::Load()
{
	Assert(!Name.empty());
	Cursor = CursorByIdent(Name);
	Assert(Name == Cursor->Ident);
}

/**
**  Load the user interface graphics.
*/
void CUserInterface::Load(void)
{
	int i;

	//
	//  Load graphics
	//
	for (i = 0; i < (int)Fillers.size(); ++i) {
		Fillers[i].G->Load();
		Fillers[i].G->UseDisplayFormat();
	}

	for (i = 0; i < MaxCosts; ++i) {
		if (Resources[i].G) {
			Resources[i].G->Load();
			Resources[i].G->UseDisplayFormat();
		}
	}

	if (ButtonPanel.G) {
		ButtonPanel.G->Load();
		ButtonPanel.G->UseDisplayFormat();
	}
	if (PieMenu.G) {
		PieMenu.G->Load();
		PieMenu.G->UseDisplayFormat();
	}

	//
	//  Resolve cursors
	//
	Point.Load();
	Glass.Load();
	Cross.Load();
	YellowHair.Load();
	GreenHair.Load();
	RedHair.Load();
	Scroll.Load();

	ArrowE.Load();
	ArrowNE.Load();
	ArrowN.Load();
	ArrowNW.Load();
	ArrowW.Load();
	ArrowSW.Load();
	ArrowS.Load();
	ArrowSE.Load();
}

/**
**  Save the viewports.
**
**  @param file  Save file handle
**  @param ui    User interface to save
*/
static void SaveViewports(CFile *file, const CUserInterface *ui)
{
	int i;
	const CViewport *vp;

	file->printf("DefineViewports(\"mode\", %d", ui->ViewportMode);
	for (i = 0; i < ui->NumViewports; ++i) {
		vp = &ui->Viewports[i];
		file->printf(",\n  \"viewport\", {%d, %d, %d}", vp->MapX, vp->MapY, 
			vp->Unit ? UnitNumber(vp->Unit) : -1);
	}
	file->printf(")\n\n");
}

/**
**  Save the saved map positions.
**
**  @param file  Save file handle
**  @param ui    User interface to save
*/
static void SaveSavedMapPositions(CFile *file, const CUserInterface *ui)
{
	for (int i = 0; i < MAX_SAVED_MAP_POSITIONS; ++i)
	{
		file->printf("SetSavedMapPosition(%d, %d, %d)\n",
			i,
			ui->SavedMapPositionX[i],
			ui->SavedMapPositionY[i]);
	}
}

/**
**  Save the user interface module.
**
**  @param file  Save file handle
*/
void SaveUserInterface(CFile *file)
{
	SaveViewports(file, &UI);
	SaveSavedMapPositions(file, &UI);
}

/**
**  Clean up a user interface.
*/
CUserInterface::~CUserInterface()
{
}

/**
**  Clean up the user interface module.
*/
void CleanUserInterface(void)
{
	int i;

	// Filler
	for (i = 0; i < (int)UI.Fillers.size(); ++i) {
		CGraphic::Free(UI.Fillers[i].G);
	}
	UI.Fillers.clear();

	// Resource Icons
	for (i = 0; i < MaxCosts; ++i) {
		CGraphic::Free(UI.Resources[i].G);
	}

	delete UI.SingleSelectedButton;
	UI.SelectedButtons.clear();
	delete UI.SingleTrainingButton;
	UI.SingleTrainingText.clear();
	UI.TrainingButtons.clear();
	UI.TrainingText.clear();
	UI.TransportingButtons.clear();

	// Button Panel
	CGraphic::Free(UI.ButtonPanel.G);

	// Pie Menu
	CGraphic::Free(UI.PieMenu.G);

	// Title Screens
	if (TitleScreens) {
		for (i = 0; TitleScreens[i]; ++i) {
			delete TitleScreens[i];
		}
		delete[] TitleScreens;
		TitleScreens = NULL;
	}
}

#ifdef DEBUG
void FreeButtonStyles()
{
	std::map<std::string, ButtonStyle *>::iterator i;
	for (i = ButtonStyleHash.begin(); i != ButtonStyleHash.end(); ++i) {
		delete (*i).second;
	}
	ButtonStyleHash.clear();
}
#endif

/**
**  Takes coordinates of a pixel in stratagus's window and computes
**  the map viewport which contains this pixel.
**
**  @param x  x pixel coordinate with origin at UL corner of screen
**  @param y  y pixel coordinate with origin at UL corner of screen
**
**  @return viewport pointer or NULL if this pixel is not inside
**  any of the viewports.
**
**  @note This functions only works with rectangular viewports, when
**  we support shaped map window, this must be rewritten.
*/
CViewport *GetViewport(int x, int y)
{
	CViewport *vp;

	for (vp = UI.Viewports; vp < UI.Viewports + UI.NumViewports; ++vp) {
		if (x >= vp->X && x <= vp->EndX && y >= vp->Y && y <= vp->EndY) {
			return vp;
		}
	}
	return NULL;
}

/**
**  Takes an array of new Viewports which are supposed to have their
**  pixel geometry (CViewport::[XY] and CViewport::End[XY]) already
**  computed. Using this information as well as old viewport's
**  parameters fills in new viewports' CViewport::Map* parameters.
**  Then it replaces the old viewports with the new ones and finishes
**  the set-up of the new mode.
**
**  @param new_vps  The array of the new viewports
**  @param num_vps  The number of elements in the new_vps[] array.
*/
static void FinishViewportModeConfiguration(CViewport new_vps[], int num_vps)
{
	int i;

	if (UI.NumViewports < num_vps) {
		//  Compute location of the viewport using oldviewport
		for (i = 0; i < num_vps; ++i) {
			const CViewport *vp;

			new_vps[i].MapX = 0;
			new_vps[i].MapY = 0;
			vp = GetViewport(new_vps[i].X, new_vps[i].Y);
			if (vp) {
				new_vps[i].OffsetX = new_vps[i].X - vp->X + vp->MapX * TileSizeX + vp->OffsetX;
				new_vps[i].OffsetY = new_vps[i].Y - vp->Y + vp->MapY * TileSizeY + vp->OffsetY;
			} else {
				new_vps[i].OffsetX = 0;
				new_vps[i].OffsetY = 0;
			}
		}
	} else {
		for (i = 0; i < num_vps; ++i) {
			new_vps[i].MapX = UI.Viewports[i].MapX;
			new_vps[i].MapY = UI.Viewports[i].MapY;
			new_vps[i].OffsetX = UI.Viewports[i].OffsetX;
			new_vps[i].OffsetY = UI.Viewports[i].OffsetY;
		}
	}

	// Affect the old viewport.
	for (i = 0; i < num_vps; ++i) {
		CViewport *vp;

		vp = UI.Viewports + i;
		vp->X = new_vps[i].X;
		vp->EndX = new_vps[i].EndX;
		vp->Y = new_vps[i].Y;
		vp->EndY = new_vps[i].EndY;
		vp->Set(new_vps[i].MapX, new_vps[i].MapY, new_vps[i].OffsetX, new_vps[i].OffsetY);
	}
	UI.NumViewports = num_vps;

	//
	//  Update the viewport pointers
	//
	UI.MouseViewport = GetViewport(CursorX, CursorY);
	if (UI.SelectedViewport > UI.Viewports + UI.NumViewports - 1) {
		UI.SelectedViewport = UI.Viewports + UI.NumViewports - 1;
	}
}

/**
**  Takes a viewport which is supposed to have its CViewport::[XY]
**  correctly filled-in and computes CViewport::End[XY] attributes
**  according to clipping information passed in other two arguments.
**
**  @param vp     The viewport.
**  @param ClipX  Maximum x-coordinate of the viewport's right side
**                as dictated by current UI's geometry and ViewportMode.
**  @param ClipY  Maximum y-coordinate of the viewport's bottom side
**                as dictated by current UI's geometry and ViewportMode.
**
**  @note It is supposed that values passed in Clip[XY] will
**  never be greater than UI::MapArea::End[XY].
**  However, they can be smaller according to the place
**  the viewport vp takes in context of current ViewportMode.
*/
static void ClipViewport(CViewport *vp, int ClipX, int ClipY)
{
	// begin with maximum possible viewport size
	vp->EndX = vp->X + Map.Info.MapWidth * TileSizeX - 1;
	vp->EndY = vp->Y + Map.Info.MapHeight * TileSizeY - 1;

	// first clip it to MapArea size if necessary
	if (vp->EndX > ClipX) {
		vp->EndX = ClipX;
	}

	// the same for y
	if (vp->EndY > ClipY) {
		vp->EndY = ClipY;
	}

	Assert(vp->EndX <= UI.MapArea.EndX);
	Assert(vp->EndY <= UI.MapArea.EndY);
}

/**
**  Compute viewport parameters for single viewport mode.
**
**  The parameters include viewport's width and height expressed
**  in pixels, its position with respect to Stratagus's window
**  origin, and the corresponding map parameters expressed in map
**  tiles with origin at map origin (map tile (0,0)).
*/
static void SetViewportModeSingle(void)
{
	CViewport new_vps[MAX_NUM_VIEWPORTS];

	DebugPrint("Single viewport set\n");

	new_vps[0].X = UI.MapArea.X;
	new_vps[0].Y = UI.MapArea.Y;
	ClipViewport(new_vps, UI.MapArea.EndX, UI.MapArea.EndY);

	FinishViewportModeConfiguration(new_vps, 1);
}

/**
**  Compute viewport parameters for horizontally split viewport mode.
**  This mode splits the UI::MapArea with a horizontal line to
**  2 (approximately) equal parts.
**
**  The parameters include viewport's width and height expressed
**  in pixels, its position with respect to Stratagus's window
**  origin, and the corresponding map parameters expressed in map
**  tiles with origin at map origin (map tile (0,0)).
*/
static void SetViewportModeSplitHoriz(void)
{
	CViewport new_vps[MAX_NUM_VIEWPORTS];

	DebugPrint("Two horizontal viewports set\n");

	new_vps[0].X = UI.MapArea.X;
	new_vps[0].Y = UI.MapArea.Y;
	ClipViewport(new_vps, UI.MapArea.EndX,
		UI.MapArea.Y + (UI.MapArea.EndY - UI.MapArea.Y + 1) / 2);

	new_vps[1].X = UI.MapArea.X;
	new_vps[1].Y = new_vps[0].EndY + 1;
	ClipViewport(new_vps + 1, UI.MapArea.EndX, UI.MapArea.EndY);

	FinishViewportModeConfiguration(new_vps, 2);
}

/**
**  Compute viewport parameters for horizontal 3-way split viewport mode.
**  This mode splits the UI::MapArea with a horizontal line to
**  2 (approximately) equal parts, then splits the bottom part vertically
**  to another 2 parts.
**
**  The parameters include viewport's width and height expressed
**  in pixels, its position with respect to Stratagus's window
**  origin, and the corresponding map parameters expressed in map
**  tiles with origin at map origin (map tile (0,0)).
*/
static void SetViewportModeSplitHoriz3(void)
{
	CViewport new_vps[MAX_NUM_VIEWPORTS];

	DebugPrint("Horizontal 3-way viewport division set\n");

	new_vps[0].X = UI.MapArea.X;
	new_vps[0].Y = UI.MapArea.Y;
	ClipViewport(new_vps, UI.MapArea.EndX,
		UI.MapArea.Y + (UI.MapArea.EndY - UI.MapArea.Y + 1) / 2);

	new_vps[1].X = UI.MapArea.X;
	new_vps[1].Y = new_vps[0].EndY + 1;
	ClipViewport(new_vps + 1,
		UI.MapArea.X + (UI.MapArea.EndX - UI.MapArea.X + 1) / 2,
		UI.MapArea.EndY);

	new_vps[2].X = new_vps[1].EndX + 1;
	new_vps[2].Y = new_vps[0].EndY + 1;
	ClipViewport(new_vps + 2, UI.MapArea.EndX, UI.MapArea.EndY);

	FinishViewportModeConfiguration(new_vps, 3);
}

/**
**  Compute viewport parameters for vertically split viewport mode.
**  This mode splits the UI::MapArea with a vertical line to
**  2 (approximately) equal parts.
**
**  The parameters  include viewport's width and height expressed
**  in pixels, its position with respect to Stratagus's window
**  origin, and the corresponding map parameters expressed in map
**  tiles with origin at map origin (map tile (0,0)).
*/
static void SetViewportModeSplitVert(void)
{
	CViewport new_vps[MAX_NUM_VIEWPORTS];

	DebugPrint("Two vertical viewports set\n");

	new_vps[0].X = UI.MapArea.X;
	new_vps[0].Y = UI.MapArea.Y;
	ClipViewport(new_vps,
		UI.MapArea.X + (UI.MapArea.EndX - UI.MapArea.X + 1) / 2,
		UI.MapArea.EndY);

	new_vps[1].X = new_vps[0].EndX + 1;
	new_vps[1].Y = UI.MapArea.Y;
	ClipViewport(new_vps + 1, UI.MapArea.EndX, UI.MapArea.EndY);

	FinishViewportModeConfiguration(new_vps, 2);
}

/**
**  Compute viewport parameters for 4-way split viewport mode.
**  This mode splits the UI::MapArea vertically *and* horizontally
**  to 4 (approximately) equal parts.
**
**  The parameters  include viewport's width and height expressed
**  in pixels, its position with respect to Stratagus's window
**  origin, and the corresponding map parameters expressed in map
**  tiles with origin at map origin (map tile (0,0)).
*/
static void SetViewportModeQuad(void)
{
	CViewport new_vps[MAX_NUM_VIEWPORTS];

	DebugPrint("Four viewports set\n");

	new_vps[0].X = UI.MapArea.X;
	new_vps[0].Y = UI.MapArea.Y;
	ClipViewport(new_vps,
		UI.MapArea.X + (UI.MapArea.EndX - UI.MapArea.X + 1) / 2,
		UI.MapArea.Y + (UI.MapArea.EndY - UI.MapArea.Y + 1) / 2);

	new_vps[1].X = new_vps[0].EndX + 1;
	new_vps[1].Y = UI.MapArea.Y;
	ClipViewport(new_vps + 1,
		UI.MapArea.EndX,
		UI.MapArea.Y + (UI.MapArea.EndY - UI.MapArea.Y + 1) / 2);

	new_vps[2].X = UI.MapArea.X;
	new_vps[2].Y = new_vps[0].EndY + 1;
	ClipViewport(new_vps + 2,
		UI.MapArea.X + (UI.MapArea.EndX - UI.MapArea.X + 1) / 2,
		UI.MapArea.EndY);

	new_vps[3].X = new_vps[1].X;
	new_vps[3].Y = new_vps[2].Y;
	ClipViewport(new_vps + 3, UI.MapArea.EndX, UI.MapArea.EndY);

	FinishViewportModeConfiguration(new_vps, 4);
}

/**
**  Sets up (calls geometry setup routines for) a new viewport mode.
**
**  @param new_mode  New mode's number.
*/
void SetViewportMode(ViewportModeType new_mode)
{
	switch (UI.ViewportMode = new_mode) {
		case VIEWPORT_SINGLE:
			SetViewportModeSingle();
			break;
		case VIEWPORT_SPLIT_HORIZ:
			SetViewportModeSplitHoriz();
			break;
		case VIEWPORT_SPLIT_HORIZ3:
			SetViewportModeSplitHoriz3();
			break;
		case VIEWPORT_SPLIT_VERT:
			SetViewportModeSplitVert();
			break;
		case VIEWPORT_QUAD:
			SetViewportModeQuad();
			break;
		default:
			DebugPrint("trying to set an unknown mode!!\n");
			break;
	}
}

/**
**  Cycles through predefined viewport modes (geometry configurations)
**  in order defined by the ViewportMode enumerated type.
**
**  @param step   The size of step used for cycling. Values that
**               make sense are mostly 1 (next viewport mode) and
**               -1 (previous viewport mode).
*/
void CycleViewportMode(int step)
{
	int new_mode;

	new_mode = UI.ViewportMode + step;
	if (new_mode >= NUM_VIEWPORT_MODES) {
		new_mode = 0;
	}
	if (new_mode < 0) {
		new_mode = NUM_VIEWPORT_MODES - 1;
	}
	SetViewportMode((ViewportModeType)new_mode);
}

/**
**  Check if mouse scrolling is enabled
*/
bool GetMouseScroll(void)
{
	return UI.MouseScroll;
}

/**
**  Enable/disable scrolling with the mouse
**
**  @param enabled  True to enable mouse scrolling, false to disable
*/
void SetMouseScroll(bool enabled)
{
	UI.MouseScroll = enabled;
}

/**
**  Check if keyboard scrolling is enabled
*/
bool GetKeyScroll()
{
	return UI.KeyScroll;
}

/**
**  Enable/disable scrolling with the keyboard
**
**  @param enabled  True to enable keyboard scrolling, false to disable
*/
void SetKeyScroll(bool enabled)
{
	UI.KeyScroll = enabled;
}

/**
**  Check if mouse grabbing is enabled
*/
bool GetGrabMouse(void)
{
	return SdlGetGrabMouse();
}

/**
**  Enable/disable grabbing the mouse
**
**  @param enabled  True to enable mouse grabbing, false to disable
*/
void SetGrabMouse(bool enabled)
{
	ToggleGrabMouse(enabled ? 1 : -1);
}

/**
**  Check if scrolling stops when leaving the window
*/
bool GetLeaveStops(void)
{
	return LeaveStops;
}

/**
**  Enable/disable leaving the window stops scrolling
**
**  @param enabled  True to stop scrolling, false to disable
*/
void SetLeaveStops(bool enabled)
{
	LeaveStops = enabled;
}

/**
**  Set saved map position
**
**  @param index  Saved map position slot.
**  @param x      X coordinate
**  @param y      Y coordinate
*/
void SetSavedMapPosition(int index, int x, int y)
{
	if (index < 0 || index >= MAX_SAVED_MAP_POSITIONS) {
		return;
	}

	UI.SavedMapPositionX[index] = x;
	UI.SavedMapPositionY[index] = y;
}


/**
**  Recall map position
**
**  @param index  Saved map position slot.
*/
void RecallSavedMapPosition(int index)
{
	if (index < 0 || index >= MAX_SAVED_MAP_POSITIONS) {
		return;
	}

	UI.SelectedViewport->Set(
		UI.SavedMapPositionX[index], UI.SavedMapPositionY[index],
		TileSizeX / 2, TileSizeY / 2);
}

//@}
