/*
   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 "DragWindow.h"
#include "Globals.h"

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

DragWindow::DragWindow(int ix, int iy, int iParentID, int flags):
GenWindow_Base(ix, iy, iParentID, flags), bDrag(0) {}

void DragWindow::InitRects() {
	//shuffle away from edges if we have been created partially off screen
	if (rect.x < smallBorderSize)
		rect.x = smallBorderSize;
	if (rect.y < smallBorderSize)
		rect.y = smallBorderSize;
	if (rect.x + rect.w + smallBorderSize > globalSettings.screenWidth)
		rect.x = globalSettings.screenWidth - rect.w - smallBorderSize;
	if (rect.y + rect.h + smallBorderSize > globalSettings.screenHeight)
		rect.y = globalSettings.screenHeight - rect.h - smallBorderSize;

	InitBorder();

	closeRect.x = rect.x + rect.w - genPictures[GENPIC_CLOSEBOX]->w;
	closeRect.y = rect.y;
	closeRect.w = genPictures[GENPIC_CLOSEBOX]->w;
	closeRect.h = genPictures[GENPIC_CLOSEBOX]->h;
}

void DragWindow::AddSideTitle(int nSide) {
	string outputStr = sides[nSide].name + ":";
	
	theText.push_back(WindowText(outputStr, true));
	theText.push_back(WindowText(""));
}

void DragWindow::AddGroupTitle(int nSide, int nGroup) {
	char output[32];
	
	sprintf(output, "%s Group %d:", sides[nSide].name.c_str(), nGroup + 1);
	
	theText.push_back(WindowText(output, true));
	theText.push_back(WindowText(""));
}

void DragWindow::DrawSelf() {
	if (visible) {
		GenWindow_Base::DrawSelf();

		if (!cantClose)
			JSDL.Blt(genPictures[GENPIC_CLOSEBOX], closeRect);
			
		int x = rect.x + smallBorderSize;
		int y = rect.y + smallBorderSize;
		
		for (int i = 0; i != theText.size(); ++i) {
			if (theText[i].bold)
				boldFonts.BlitString(x, y, 0, theText[i]);
			else
				normalFonts.BlitString(x, y, 0, theText[i]);
			y+= lineGap;
		}
	}
}

bool DragWindow::MouseD(Uint8 button, Uint16 x, Uint16 y) {
	if (!visible)
		return false;
		
	bool ret = GenWindow_Base::MouseD(button, x, y);
	
	if (ret == true) {
		if (button == SDL_BUTTON_LEFT) {
			//close box?
			if (!cantClose
		        	&& x >= closeRect.x
		        	&& x < closeRect.x + closeRect.w
		        	&& y >= closeRect.y
		        	&& y < closeRect.y + closeRect.h)
				closed = true;
			else if (!bStatic) { //start drag
				bDrag = true;
				saveMousePosx = x;
				saveMousePosy = y;
			}
		}
	}

	return ret;
}
	
bool DragWindow::MouseM(Uint8 state, Uint16 x, Uint16 y) {
	//drag
	if (bDrag && state & SDL_BUTTON(1)) {
		int distx = x - saveMousePosx;
		int disty = y - saveMousePosy;

		Move(distx, disty);

		bDrag = true;
		saveMousePosx = x;
		saveMousePosy = y;
		return true;
	}
	
	bDrag = false;
	return false;
}

void DragWindow::Move(int distx, int disty) {
	GenWindow_Base::Move(distx, disty);

	closeRect.x+= distx;

	closeRect.y+= disty;
}


InfoString::InfoString(const string& iTheString):
DragWindow(0, 0, none_constant, 0), theString(iTheString) {
	//width
	int longest = 0;
	int currentLength = 0;
	int newlines = 0;

	for (int i = 0; i != theString.size(); ++i) {
		if (theString[i] == '\n') {
			if (currentLength > longest)
				longest = currentLength;

			currentLength = 0;
			++newlines;
		}
		else
			++currentLength;
	}

	//one last time
	if (currentLength > longest)
		longest = currentLength;

	//overall dimensions
	rect.w = (longest + 1) * normalLetterWidth;
	rect.h = (newlines + 1) * lineGap;

	//if just one line, make room for the close cross
	if (newlines == 0)
		rect.w+= genPictures[GENPIC_CLOSEBOX]->w;

	CentreWindow();

	InitRects();
	anInfoString = 1;
}

InfoString::~InfoString() {
	anInfoString = 0;
}

void InfoString::DrawSelf() {
	DragWindow::DrawSelf();

	int x = rect.x + smallBorderSize;
	int y = rect.y + smallBorderSize;


	normalFonts.BlitString(x, y, 0, theString);
}

///


Slider::Slider(int ix, int iy, int iSliderVar, int iVarMin, int iVarMax, int iParentID, int flags):
DragWindow(ix, iy, iParentID, flags), varPointer(&sliderVar), sliderVar(iSliderVar) {
	Init(iVarMin, iVarMax);
}

Slider::Slider(int ix, int iy, int* iVarPointer, int iVarMin, int iVarMax, int iParentID, int flags):
DragWindow(ix, iy, iParentID, flags), varPointer(iVarPointer) {
	Init(iVarMin, iVarMax);
}

void Slider::Init(int iVarMin, int iVarMax) {
	shadowVar = *varPointer;
	bSliderDrag = 0;
	varMin = iVarMin;
	varMax = iVarMax;

	rect.w = sliderWinWidth;
	rect.h = sliderWinHeight;
	InitRects();

	ruleLength = rect.w - (lineGap << 1);

	middle = rect.y + (rect.h >> 1) + (lineGap >> 1);
}

bool Slider::MouseD(Uint8 button, Uint16 x, Uint16 y) {
	//note not DragWindow::MouseD
	bool ret = GenWindow_Base::MouseD(button, x, y);

	//slider start drag?
	if (button == SDL_BUTTON_LEFT
	         && x > sliderRect.x
	         && x < sliderRect.x + sliderRect.w
	         && y > sliderRect.y
	         && y < sliderRect.y + sliderRect.h) {
		bSliderDrag = true;
		saveMousePosx = x;
	} else
		//check for the window start drag
		ret = DragWindow::MouseD(button, x, y);
		
	return ret;
}

bool Slider::MouseM(Uint8 state, Uint16 x, Uint16 y) {
	bool ret = false;
	
	//slider drag
	if (bSliderDrag && state & SDL_BUTTON(1)) {
		int dPos = x - saveMousePosx;
		sliderRect.x+= dPos;

		//add to the value
		shadowVar += dPos * (varMax - varMin) / ruleLength;

		if (shadowVar > varMax)
			shadowVar = varMax;
		else if (shadowVar < varMin)
			shadowVar = varMin;

		*varPointer = static_cast<int>(shadowVar);

		bSliderDrag = true;
		saveMousePosx = x;
		ret = true;
	} else {
		bSliderDrag = false;
		ret = DragWindow::MouseM(state, x, y);
	}
		
	//work out middle /after/ any dragging of us or a parent
	middle = rect.y + (rect.h >> 1) + (lineGap >> 1);
	
	return ret;
}

void Slider::DrawSelf() {
	DragWindow::DrawSelf();

	//draw a line down the centre
	SDL_Rect tempRect;

	tempRect.x = rect.x + lineGap;
	tempRect.y = middle;
	tempRect.w = static_cast<Uint16>(ruleLength);
	tempRect.h = smallBorderSize;

	JSDL.BltFill(tempRect,veryDarkGold);

	//we have to rework out position each time because something
	//other than us might have changed it
	//strange ordering to avoid casting, theVar/varMax * ruleLength makes more sense
	int leftPos = rect.x + lineGap + (ruleLength * (*varPointer - varMin) / (varMax - varMin));
	sliderRect.x = leftPos;
	sliderRect.y = middle - (sliderHeight >> 1);
	sliderRect.w = sliderWidth;
	sliderRect.h = sliderHeight;

	JSDL.BltFill(sliderRect, gold);
}


SliderWithUnits::SliderWithUnits(int ix, int iy, int iSliderVar, int iVarMin, int iVarMax, const string& iVarName, const string& iVarUnits, int iParentID, int flags):
Slider(ix, iy, iSliderVar, iVarMin, iVarMax, iParentID, flags), varName(iVarName), varUnits(iVarUnits) {}

SliderWithUnits::SliderWithUnits(int ix, int iy, int* iVarPointer, int iVarMin, int iVarMax, const string& iVarName, const string& iVarUnits, int iParentID, int flags):
Slider(ix, iy, iVarPointer, iVarMin, iVarMax, iParentID, flags), varName(iVarName), varUnits(iVarUnits) {}

void SliderWithUnits::DrawSelf() {
	Slider::DrawSelf();

	//in text
	int x = rect.x + smallBorderSize;
	int y = rect.y + smallBorderSize;
	char output[48];
	sprintf(output, "%s: %d %s", varName.c_str(), *varPointer, varUnits.c_str());
	normalFonts.BlitString(x, y, 0, output);
}

void StringInputBox::DrawSelf() {
	DragWindow::DrawSelf();

	int x = rect.x + bigBorderSize;
	int y = rect.y + lineGap;
	normalFonts.BlitString(x, y, 0, title);

	x+= lineGap;
	y+= lineGap;

	normalFonts.BlitString(x, y, 0, theInput);
}

bool StringInputBox::Keyboard(SDL_keysym& keysym) {
	switch (StringInput(keysym, theInput, maxTextInputLength)) {
	//user escaped
	case -1:
		closed = true;
		break;

	//user hit enter
	case 0:
		userHitEnter = 1;
		closed = true;
		break;

	//keep going
	case 1:
		break;
	}

	return true;
}

CopyBox::CopyBox(const string& iOldName, const string& iDirectory, int iParentID):
StringInputBox(maxNameLength, iParentID, 0), oldName(iOldName), directoryName(iDirectory) {
	title = "What would you like to call the copy?";
	rect.x = 200;
	rect.y = 200;
	rect.w = 400;
	rect.h = 100;

	InitRects();
}

bool CopyBox::Keyboard(SDL_keysym& keysym) {
	StringInputBox::Keyboard(keysym);

	namespace fs = boost::filesystem;

	if (userHitEnter) {
		try {
			fs::path sourcePath(directoryName + oldName + ".dat");
			fs::path destPath(directoryName + theInput + ".dat");

			fs::copy_file(sourcePath, destPath);
			MessageWindows(WC_ExpensiveUpdate, 0, 0, parentID, myID);
		} catch (fs::filesystem_error e) {
			string errorStr = "\n";
			errorStr += e.what();
			CreateInfoString(errorStr);
		}
	}

	return true;
}


LargeBlankDW::LargeBlankDW(const string& iTitle):
DragWindow(0, 0, none_constant, 0) {
	rect.w = 600;
	rect.h = lineGap * 22;

	CentreWindow();
	InitRects();
	
	theText.push_back(WindowText(iTitle, true));
}

