#include "fftfile.h"
#include <stack>

char PHYSFS_getc(PHYSFS_file *fp)
{
	char buffer = 0;
	if(PHYSFS_read(fp, &buffer, 1, 1) == 1) {
		return buffer;
	}
	else Log::WriteVerbose(PHYSFS_getLastError());
	return 0;
}
std::string PHYSFS_getline(PHYSFS_file *fp)
{
	std::string result;
	char c = 0;
	c = PHYSFS_getc(fp);
	int done=0;
	while(!done && (c != 0x0A)) {
		if(c != 0x0D) {
			result += c;
		}
		c = PHYSFS_getc(fp);
		if (PHYSFS_eof(fp) && c != 0x0A && c != 0x0D)
		{
			result += c;
			done=1;
		}
	}
	return result;
}
bool isWhiteSpace(char c)
{
	return (c==' ' || c=='\t' || c=='\n' || c=='\r');
}

std::string stripString(const std::string& str)
{
	unsigned int start = 0;
	unsigned int end = str.length();

	while (isWhiteSpace(str[start]) && start < str.length()) start++;
	while (isWhiteSpace(str[end - 1]) && end > 0) end--;
	if (start >= end)
		return "";
	else
		return str.substr(start, end - start);
}


std::string getNextName(PHYSFS_file *fp)
{
	int done=0;
	char c;
	std::string retval="";
	while (!done)
	{
		c=PHYSFS_getc(fp);
		if (isWhiteSpace(c)) done=1;
		else retval+=c;
	}
	return retval;
}

DataNode *ReadDoc(PHYSFS_file *fp)
{
	std::stack<DataNode*> docStack;

	DataNode *rootNode=new DataNode("root");
	docStack.push(rootNode);
	char c;
	int done=0;
	while (!PHYSFS_eof(fp) || !done)
	{
		c=PHYSFS_getc(fp);
		if (PHYSFS_eof(fp)) done=1;
		else if (c == '(')
		{
			std::string nodeName = stripString(getNextName(fp));
			DataNode* newNode = new DataNode(nodeName);
			docStack.top()->addChild(newNode);
			docStack.push(newNode);
		}
		else if (c == ')')
		{
			docStack.pop();
		}
		else
		{
			std::string ss="";
			while (c != '(' && c != ')' && !PHYSFS_eof(fp))
			{
				ss += c;
				c = PHYSFS_getc(fp);
			}
			PHYSFS_seek(fp, (PHYSFS_uint64)(PHYSFS_tell(fp)-1));

			std::string s = stripString(ss);

			if (s.length())
				docStack.top()->addChild(s);
		}
	}
	return rootNode;
}
DataNode *ReadString(std::string str)
{
	std::stack<DataNode*> docStack;

	DataNode *rootNode=new DataNode("root");
	docStack.push(rootNode);
	char c;
	int done=0;
	unsigned int i=0;
	while (!done)
	{
		c=str[i];
		i++;
		if (i>=str.size()) done=1;
		else if (c == '(')
		{
			int done1=0;
			std::string temp="";
			char c2;
			while (!done1)
			{
				c2=str[i];
				i++;
				if (isWhiteSpace(c2)) done1=1;
				else temp+=c2;
			}
			std::string nodeName = stripString(temp);
			DataNode* newNode = new DataNode(nodeName);
			docStack.top()->addChild(newNode);
			docStack.push(newNode);
		}
		else if (c == ')')
		{
			docStack.pop();
		}
		else
		{
			std::string ss="";
			while (c != '(' && c != ')' && i<str.size())
			{
				ss += c;
				c = str[i];
				i++;
			}
			i--;

			std::string s = stripString(ss);

			if (s.length())
				docStack.top()->addChild(s);
		}
	}
	return rootNode;
}
int physfsrwops_seek(SDL_RWops *rw, int offset, int whence)
{
    PHYSFS_file *handle = (PHYSFS_file *) rw->hidden.unknown.data1;
    int pos = 0;

    if (whence == SEEK_SET)
    {
        pos = offset;
    } /* if */

    else if (whence == SEEK_CUR)
    {
        PHYSFS_sint64 current = PHYSFS_tell(handle);
        if (current == -1)
        {
            SDL_SetError("Can't find position in file: %s",
                          PHYSFS_getLastError());
            return(-1);
        } /* if */

        pos = (int) current;
        if ( ((PHYSFS_sint64) pos) != current )
        {
            SDL_SetError("Can't fit current file position in an int!");
            return(-1);
        } /* if */

        if (offset == 0)  /* this is a "tell" call. We're done. */
            return(pos);

        pos += offset;
    } /* else if */

    else if (whence == SEEK_END)
    {
        PHYSFS_sint64 len = PHYSFS_fileLength(handle);
        if (len == -1)
        {
            SDL_SetError("Can't find end of file: %s", PHYSFS_getLastError());
            return(-1);
        } /* if */

        pos = (int) len;
        if ( ((PHYSFS_sint64) pos) != len )
        {
            SDL_SetError("Can't fit end-of-file position in an int!");
            return(-1);
        } /* if */

        pos += offset;
    } /* else if */

    else
    {
        SDL_SetError("Invalid 'whence' parameter.");
        return(-1);
    } /* else */

    if ( pos < 0 )
    {
        SDL_SetError("Attempt to seek past start of file.");
        return(-1);
    } /* if */

    if (!PHYSFS_seek(handle, (PHYSFS_uint64) pos))
    {
        SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
        return(-1);
    } /* if */

    return(pos);
} /* physfsrwops_seek */
int physfsrwops_read(SDL_RWops *rw, void *ptr, int size, int maxnum)
{
    PHYSFS_file *handle = (PHYSFS_file *) rw->hidden.unknown.data1;
    PHYSFS_sint64 rc = PHYSFS_read(handle, ptr, size, maxnum);
    if (rc != maxnum)
    {
        if (!PHYSFS_eof(handle)) /* not EOF? Must be an error. */
            SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
    } /* if */

    return((int) rc);
} /* physfsrwops_read */
int physfsrwops_write(SDL_RWops *rw, const void *ptr, int size, int num)
{
    PHYSFS_file *handle = (PHYSFS_file *) rw->hidden.unknown.data1;
    PHYSFS_sint64 rc = PHYSFS_write(handle, ptr, size, num);
    if (rc != num)
        SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());

    return((int) rc);
} /* physfsrwops_write */
int physfsrwops_close(SDL_RWops *rw)
{
    PHYSFS_file *handle = (PHYSFS_file *) rw->hidden.unknown.data1;
    if (!PHYSFS_close(handle))
    {
        SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
        return(-1);
    } /* if */

    SDL_FreeRW(rw);
    return(0);
} /* physfsrwops_close */
SDL_RWops *create_rwops(PHYSFS_file *handle)
{
    SDL_RWops *retval = NULL;

    if (handle == NULL)
        SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
    else
    {
        retval = SDL_AllocRW();
        if (retval != NULL)
        {
            retval->seek  = physfsrwops_seek;
            retval->read  = physfsrwops_read;
            retval->write = physfsrwops_write;
            retval->close = physfsrwops_close;
            retval->hidden.unknown.data1 = handle;
        } /* if */
    } /* else */

    return(retval);
} /* create_rwops */
SDL_Surface *LoadIMG(std::string imgfile)
{
	PHYSFS_file *temp=PHYSFS_openRead(imgfile.c_str());
	if (!temp)
	{
		std::string temp2="Could not open file ";
		temp2+=imgfile;
		FPK_Invalid("Unknown FPK!", temp2);
	}
	SDL_RWops *rw=create_rwops(temp);
	SDL_Surface *tempsurf=IMG_Load_RW(rw,0);
	PHYSFS_close(temp);
	SDL_FreeRW(rw);

	SDL_Surface *surface=SDL_DisplayFormatAlpha(tempsurf);
	SDL_SetAlpha(surface, SDL_SRCALPHA | SDL_RLEACCEL, 255);
	SDL_FreeSurface(tempsurf);
	return surface;
}
SDL_Surface *LoadICON(std::string imgfile)
{
	PHYSFS_file *temp=PHYSFS_openRead(imgfile.c_str());
	if (!temp)
	{
		std::string temp2="Could not open file ";
		temp2+=imgfile;
		FPK_Invalid("Unknown FPK!", temp2);
	}
	SDL_RWops *rw=create_rwops(temp);
	SDL_Surface *tempsurf=IMG_Load_RW(rw,0);
	PHYSFS_close(temp);
	SDL_FreeRW(rw);
	return tempsurf;
}
TTF_Font *LoadTTF(std::string ttffile, int ptsize)
{
	//This SHOULD work, but physfs doesn't load these properly
	/*
	PHYSFS_file *temp=PHYSFS_openRead(ttffile);
	SDL_RWops *rw=create_rwops(temp);
	TTF_Font *ttf=TTF_OpenFontRW(rw, 0, ptsize);
	PHYSFS_close(temp);
	*/
	TTF_Font *ttf=TTF_OpenFont(ttffile.c_str(), ptsize);
	return ttf;
}
Mix_Chunk *LoadSound(std::string soundfile)
{
	PHYSFS_file *temp=PHYSFS_openRead(soundfile.c_str());
	if (!temp)
	{
		std::string temp2="Could not open file ";
		temp2+=soundfile;
		FPK_Invalid("Unknown FPK!", temp2);
	}
	SDL_RWops *rw=create_rwops(temp);
	Mix_Chunk *sound=Mix_LoadWAV_RW(rw,1);
	return sound;
}

void PathSaver::clearPath() {
	char **spath = PHYSFS_getSearchPath();
	for (char **t = spath; *t != NULL; ++t) {
		PHYSFS_removeFromSearchPath(*t);
	}
	PHYSFS_freeList(spath);
}
PathSaver::PathSaver() {
	saved = PHYSFS_getSearchPath();
	clearPath();
}
PathSaver::~PathSaver() {
	clearPath();
	for (char** t = saved; *t != NULL; ++t) {
		PHYSFS_addToSearchPath(*t, 1);
	}
	PHYSFS_freeList(saved);
}

bool IsFPK(std::string str)
{
	unsigned int start=str.size()-4;
	char temp[5];
	unsigned int i;
	for(i=0; i<str.size()-start; i++)
	{
		temp[i]=str[i+start];
	}
	temp[4]='\0';
	if (!strcmp(temp,".fpk")) return true;
	else return false;
}

/* Given a path, find the last filename in it.
 * Assumes separator is '/', as used in physfs.
 */
std::string lastPathElement(const std::string &path) {
	std::string::size_type start = 0;

	std::string::size_type sep = path.rfind('/');
	if (sep != std::string::npos) {
		start = sep + 1;
	}

	return path.substr(start);
}

/* Validate an FPK, and return the fft-pack node. Return through a parameter the root
 * node, that needs to be deleted after the caller is done with the pack node.
 * On failure, call FPK_Invalid().
 */
DataNode *FPK_validatedPack(std::string fpk_path, DataNode **toDelete) {
	PathSaver ps;	// backup the search path

	PHYSFS_addToSearchPath(fpk_path.c_str(), 0);
	PHYSFS_file *fp = PHYSFS_openRead("main.fs");
	if (!fp) {
		FPK_Invalid(fpk_path, "main.fs not found in FPK");
	}

	DataNode *rootNode = ReadDoc(fp);
	PHYSFS_close(fp);
	if (!rootNode->hasChild("fft-pack")) {
		/* FIXME: If FPK_Invalid is ever changed to not abort the program,
		 * rootNode needs to be deleted. */
		FPK_Invalid(fpk_path, "main.fs is not an fft-pack file");
	}

	DataNode *packNode = rootNode->getChild("fft-pack");
	if (!packNode->hasChild("objects")) {
		FPK_Invalid(fpk_path, "objects command not found in main.fs");
	}

	*toDelete = rootNode;
	return packNode;
	// search path is auto-restored
}

int FPK_count()
{
	char **i;
	int count;
	count=0;
	char **fpklist=PHYSFS_enumerateFiles("fpk");
	for (i = fpklist; *i != NULL; i++)
	{
		if(IsFPK(*i))
		{
			count++;
		}
	}
	PHYSFS_freeList(fpklist);
	return count;
}

/* If no fpk with the desired name is found, return zero. */
float FPK_versionByName(std::string want_name)
{
	char **spath = PHYSFS_getSearchPath();

	for (char **p = spath; *p != NULL; p++)
	{
		if (!IsFPK(*p)) {
			continue;
		}

		std::string fpk_name;
		DataNode *packNode, *deleteNode;
		packNode = FPK_validatedPack(*p, &deleteNode);

		if (packNode->hasChild("name")) {
			fpk_name = packNode->getChild("name")->getString();
		} else {
			FPK_Invalid(*p, "name command not found in main.fs");
		}

		if ( !strcmp(fpk_name.c_str(),want_name.c_str()) )
		{
			if (packNode->hasChild("version")) {	// OK to return
				std::string version = packNode->getChild("version")->getString();
				float retversion = atof(version.c_str());

				delete deleteNode;
				PHYSFS_freeList(spath);

				return retversion;
			} else {
				FPK_Invalid(*p, "version command not found in main.fs");
			}
		}

		delete deleteNode;
	}

	PHYSFS_freeList(spath);
	return 0;
}

/* If no fpk with the desired name is found, return a blank string. */
std::string FPK_ByName(std::string want_name)
{
	char **spath = PHYSFS_getSearchPath();

	for (char **p = spath; *p != NULL; p++)
	{
		if (!IsFPK(*p)) {
			continue;
		}

		std::string fpk_name;
		DataNode *packNode, *deleteNode;
		packNode = FPK_validatedPack(*p, &deleteNode);

		if (packNode->hasChild("name")) {
			fpk_name = packNode->getChild("name")->getString();
		} else {
			FPK_Invalid(*p, "name command not found in main.fs");
		}

		if ( !strcmp(fpk_name.c_str(),want_name.c_str()) ) {
			std::string retval = lastPathElement(*p);

			delete deleteNode;
			PHYSFS_freeList(spath);

			return retval;
		}

		delete deleteNode;
	}

	PHYSFS_freeList(spath);
	return std::string();
}
void FPK_Invalid(std::string fpk, std::string message)
{
	TTF_Font *font=TTF_OpenFont("fonts/arial.ttf", 12);
	SDL_Color white = { 0xFF, 0xFF, 0xFF, 0 };
	SDL_Surface *screen=SDL_GetVideoSurface();
	std::string msg[20];
	char c;
	Uint16 i=0;
	int end=0;
	int done=0;
	std::string msghead;
	c=message[i];
	while(!done) {
		if(c != 0x0A) {
			msg[end] += c;
		}
		else end++;
		i++;
		if (i>=message.size()) done=1;
		c=message[i];
	}
	msghead=fpk;
	msghead+=" was invalid for the following reasons:";
	Uint32 ticks=SDL_GetTicks();
	SDL_Event event;
	bool logged=false;
	while (SDL_GetTicks()<ticks+30000)
	{
		while (SDL_PollEvent(&event))
		{
			// an event was found
			switch (event.type) {
			// close button clicked
			case SDL_QUIT:
				exit(1);
				break;
			}
		}
		SDL_FillRect(screen,0,0);
		drawString(font, screen->w/2-200, 20, msghead, white);
		if (!logged) Log::WriteVerbose(msghead);
		for (i=0; i<=end; i++)
		{
			drawString(font, screen->w/2-200, 40+(i*20), msg[i], white);
			if (!logged) Log::WriteVerbose(msg[i]);
		}
		drawString(font, screen->w/2-200, 40+((i+1)*20), "This game will autoclose in 30 seconds", white);
		SDL_Flip(screen);
		logged=true;
	}
	exit(1);
}

/* If no fpk with the desired name is found, return zero. */
int FPK_countPlayerShips(std::string fpk)
{
	char **spath = PHYSFS_getSearchPath();

	for (char **p = spath; *p != NULL; p++) {
		if ( !strcmp(lastPathElement(*p).c_str(), fpk.c_str()) ) {
			DataNode *packNode, *deleteNode;
			packNode = FPK_validatedPack(*p, &deleteNode);

			if (!packNode->hasChild("objects")) {
				FPK_Invalid(*p, "objects command not found in main.fs");
			}

			int count = packNode->getChild("objects")->getChildren("playership").size();

			delete deleteNode;
			PHYSFS_freeList(spath);

			return count;
		}
	}

	PHYSFS_freeList(spath);
	return 0;
}

/* If no fpk with the desired name is found, return NULL. */
SDL_Surface *FPK_LoadIMG(std::string fpk, std::string img)
{
	char **spath = PHYSFS_getSearchPath();

	for (char **p = spath; *p != NULL; p++) {
		if (!strcmp(lastPathElement(*p).c_str(),fpk.c_str())) {
			PathSaver ps;
			PHYSFS_addToSearchPath(*p, 0);
			PHYSFS_freeList(spath);

			return LoadIMG(img);
		}
	}

	PHYSFS_freeList(spath);
	return NULL;
}

int SWarpToSpeed(std::string swarp)
{
	double temp=atof(swarp.c_str());
	//int temp2=(int)(temp*10)+40;
	int temp2=(int)((20*pow(1.4,(temp-1)))+30);
	return temp2;
}
std::string SpeedToSWarp(int speed)
{
	char temp[300];
	//float warp=((float)speed-40)/10;
	float warp=SpeedToFWarp(speed);
	if (speed<50) warp=(float)speed/50;
	sprintf(temp, "%.1f", warp);
	std::string s1(temp);
	return s1;
}
float SpeedToFWarp(int speed)
{
	float warp=(float)(((log(((float)speed-30.0)/20.0))/log(1.4))+1.0);
	if (speed<50) warp=(float)speed/50;
	return warp;
}

FPKShip::FPKShip(int listindex, int num)
{
	Log::WriteVerbose("New FPKShip");
	int i;
	for(i=0; i<__MAXSHIPSLOTS; i++)
	{
		slots[i].image=0;
	}
	shield=0;
	image=0;
	shootx=0;
	shooty=0;
	//Log::WriteVerbose("FPKShip CP");
	this->ZeroSlots();
	//Log::WriteVerbose("FPKShip CP");
	this->GetFileInfo(listindex, num);
	//Log::WriteVerbose("FPKShip CP");
	image=LoadIMG(imagefn);
	//Log::WriteVerbose("FPKShip CP");
	if (hasShield) shield=LoadIMG(shieldfn);
	//Log::WriteVerbose("FPKShip CP");
}
FPKShip::FPKShip(std::string sfpk, std::string shipname)
{
	Log::WriteVerbose("New FPKShip");
	int i;
	for(i=0; i<__MAXSHIPSLOTS; i++)
	{
		slots[i].image=0;
	}
	shield=0;
	image=0;
	shootx=0;
	shooty=0;
	price=0;
	this->ZeroSlots();
	this->GetFileInfo(sfpk, shipname);
	image=LoadIMG(imagefn);
	if (hasShield) shield=LoadIMG(shieldfn);
}
FPKShip::~FPKShip()
{
	if (image) SDL_FreeSurface(image);
	if (shield) SDL_FreeSurface(shield);
}

void FPKShip::GetFileInfo(int listindex, int num)
{
	Log::WriteVerbose("FPKShip::GetFileInfo Begin");

	char **spath = PHYSFS_getSearchPath();
	int cur = 0;
	bool found = false;
	std::string fpk;

	for (char **p = spath; *p != NULL; p++)
	{
		if (!IsFPK(*p)) {
			continue;
		}

		if (cur++ == listindex) {
			found = true;
			fpk = *p;
			break;
		}
	}
	PHYSFS_freeList(spath);

	if (!found) {
		FPK_Invalid("'unknown'", "An FPK that was designated by number was not found");
		return;
	}

	DataNode *packNode, *deleteNode;
	packNode = FPK_validatedPack(fpk, &deleteNode);

	if (!packNode->hasChild("objects")) {
		FPK_Invalid(fpk, "objects command not found in main.fs");
	}

	DataNodeList ships = packNode->getChild("objects")->getChildren("playership");

	std::string shipname;
	if (ships[num]->hasChild("displayname")) {
		shipname = ships[num]->getChild("displayname")->getString();
	} else {
		FPK_Invalid(fpk, "displayname command not found in a playership in FPK");
	}

	delete deleteNode;
	GetFileInfo(lastPathElement(fpk), shipname);
}

void FPKShip::GetFileInfo(std::string sfpk, std::string shipname)
{
	Log::WriteVerbose("FPKShip::GetFileInfo Begin");
	fpk = sfpk;

	char **spath = PHYSFS_getSearchPath();
	bool found = false;
	DataNode *packNode, *deleteNode;
	packNode=0;
	deleteNode=0;
	std::string temp;

	for (char **p = spath; *p != NULL; p++) {
		if (!strcmp(lastPathElement(*p).c_str(),fpk.c_str())) {
			PathSaver ps;
			PHYSFS_addToSearchPath(*p, 0);

			found = true;
			temp = *p;
			packNode = FPK_validatedPack(*p, &deleteNode);
		}
	}
	PHYSFS_freeList(spath);

	if (!found) {
		FPK_Invalid(sfpk, "FPK not found");
		return;
	}

	int cur = 0, num = 0;
	DataNodeList ships = packNode->getChild("objects")->getChildren("playership");
	DataNodeList::iterator iter;
	if (!ships.empty())
	{
		for (iter = ships.begin(); iter != ships.end(); iter++)
		{
			if ((*iter)->hasChild("displayname"))
			{
				std::string temp3=(*iter)->getChild("displayname")->getString();
				if (!strcmp(temp3.c_str(),shipname.c_str()))
				{
					num=cur;
				}
			}
			else FPK_Invalid(temp, "displayname command not found in a playership in FPK");
			cur++;
		}
	}

	if (ships[num]->hasChild("displayname")) name=ships[num]->getChild("displayname")->getString();
	else FPK_Invalid(temp, "displayname command not found in a playership in FPK");
	//Log::WriteVerbose("FPKShip::GetFileInfo CP");

	if (ships[num]->hasChild("image")) imagefn=ships[num]->getChild("image")->getString();
	else FPK_Invalid(temp, "image command not found in a playership in FPK");
	//Log::WriteVerbose("FPKShip::GetFileInfo CP");

	if (ships[num]->hasChild("shield"))
	{
		shieldfn=ships[num]->getChild("shield")->getString();
		hasShield=true;
	}
	else hasShield=false;
	//Log::WriteVerbose("FPKShip::GetFileInfo CP");

	if (ships[num]->hasChild("price"))
	{
		sprice=ships[num]->getChild("price")->getString();
		price=atoi(sprice.c_str());
	}
	else FPK_Invalid(temp, "price command not found in a playership in FPK");

	if (ships[num]->hasChild("hullhitmax"))
	{
		shullhitmax=ships[num]->getChild("hullhitmax")->getString();
		hullhitmax=atoi(shullhitmax.c_str());
	}
	else FPK_Invalid(temp, "hullhitmax command not found in a playership in FPK");

	if (ships[num]->hasChild("dimensions"))
	{
		if (!ships[num]->getChild("dimensions")->hasChild("w") ||
		!ships[num]->getChild("dimensions")->hasChild("l") ||
		!ships[num]->getChild("dimensions")->hasChild("d"))
			FPK_Invalid(temp, "dimensions command does not have all dimensions in a playership in FPK");
		else
		{
			sw=ships[num]->getChild("dimensions")->getChild("w")->getString();
			w=atoi(sw.c_str());
			sl=ships[num]->getChild("dimensions")->getChild("l")->getString();
			l=atoi(sl.c_str());
			sd=ships[num]->getChild("dimensions")->getChild("d")->getString();
			d=atoi(sd.c_str());
		}
	}
	else FPK_Invalid(temp, "dimensions command not found in a playership in FPK");
	//Log::WriteVerbose("FPKShip::GetFileInfo CP");

	if (ships[num]->hasChild("shootpoint"))
	{
		if (!ships[num]->getChild("shootpoint")->hasChild("x") ||
		!ships[num]->getChild("shootpoint")->hasChild("y"))
			FPK_Invalid(temp, "shootpoint command does not have both x and y commands in a playership in FPK");
		else
		{
			shootx=atoi(ships[num]->getChild("shootpoint")->getChild("x")->getString().c_str());
			shooty=atoi(ships[num]->getChild("shootpoint")->getChild("y")->getString().c_str());
		}
	}
	else
	{
		shootx=image->w/2;
		shooty=image->h/2;
	}
	//Log::WriteVerbose("FPKShip::GetFileInfo CP shootpoint");

	DataNodeList shipslots = ships[num]->getChildren("slot");
	//Log::WriteVerbose("FPKShip::GetFileInfo CP");
	slotcount=shipslots.size();
	int j=0;
	if (!shipslots.empty())
	{
		for (iter = shipslots.begin(); iter != shipslots.end(); iter++)
		{
			if (!(*iter)->hasChild("id")) FPK_Invalid(temp, "slot does not have an id command in a playership in FPK");
			if (!(*iter)->hasChild("w")) FPK_Invalid(temp, "slot does not have a w command in a playership in FPK");
			if (!(*iter)->hasChild("l")) FPK_Invalid(temp, "slot does not have a l command in a playership in FPK");
			if (!(*iter)->hasChild("d")) FPK_Invalid(temp, "slot does not have a d command in a playership in FPK");
			if (!(*iter)->hasChild("type")) FPK_Invalid(temp, "slot does not have a type command (it can be blank) in a playership in FPK");

			slots[j].id=atoi((*iter)->getChild("id")->getString().c_str());
			slots[j].w=atoi((*iter)->getChild("w")->getString().c_str());
			slots[j].l=atoi((*iter)->getChild("l")->getString().c_str());
			slots[j].d=atoi((*iter)->getChild("d")->getString().c_str());
			slots[j].holdamount=0;
			slots[j].ammoamount=0;
			slots[j].type=(*iter)->getChild("type")->getString();
			slots[j].full=false;
			slots[j].weaponfull=false;
			slots[j].cargounits=0;
			j++;
		}
	}

	delete deleteNode;
}
void FPKShip::Reload(int listindex, int num)
{
	if (image) SDL_FreeSurface(image);
	if (shield) SDL_FreeSurface(shield);
	shield=0;
	image=0;
	this->ZeroSlots();
	this->GetFileInfo(listindex, num);
	image=LoadIMG(imagefn);
	shield=LoadIMG(shieldfn);
}

std::string FPKShip::GetName()
{
	return name;
}
std::string FPKShip::GetFPK()
{
	return fpk;
}
std::string FPKShip::GetImageFN()
{
	return imagefn;
}
std::string FPKShip::GetShieldFN()
{
	return shieldfn;
}
int FPKShip::GetW()
{
	return w;
}
int FPKShip::GetL()
{
	return l;
}
int FPKShip::GetD()
{
	return d;
}
std::string FPKShip::GetSW()
{
	return sw;
}
std::string FPKShip::GetSL()
{
	return sl;
}
std::string FPKShip::GetSD()
{
	return sd;
}
int FPKShip::GetShootX()
{
	return shootx;
}
int FPKShip::GetShootY()
{
	return shooty;
}
unsigned int FPKShip::GetPrice()
{
	return price;
}
std::string FPKShip::GetSPrice()
{
	return sprice;
}
int FPKShip::GetHullHitMax()
{
	return hullhitmax;
}
std::string FPKShip::GetSHullHitMax()
{
	return shullhitmax;
}
SDL_Surface *FPKShip::GetImage()
{
	return image;
}
bool FPKShip::HasShield()
{
	return hasShield;
}
SDL_Surface *FPKShip::GetShield()
{
	return shield;
}

void FPKShip::Colorize(Uint8 r, Uint8 g, Uint8 b)
{
	SDL_FreeSurface(image);
	image=FPK_LoadIMG(fpk, imagefn);
	if (r>0 || g>0 || b>0) ColorizeSurface(image, r, g, b);
}

int FPKShip::GetSlotCount()
{
	return slotcount;
}
std::string FPKShip::GetSSlotCount()
{
	char c[300];
	sprintf(c, "%d", slotcount);
	std::string s1(c);
	return s1;
}
void FPKShip::ZeroSlots()
{
	int i;
	for (i=0; i<__MAXSHIPSLOTS; i++)
	{
		slots[i].id=0;
		slots[i].w=0;
		slots[i].l=0;
		slots[i].d=0;
		slots[i].holdamount=0;
		slots[i].ammoamount=0;
		slots[i].cargounits=0;
		slots[i].type="";
		slots[i].full=false;
		slots[i].weaponfull=false;
		slots[i].fulltype="";
		slots[i].fpk="";
		slots[i].fn="";
		if (slots[i].image) SDL_FreeSurface(slots[i].image);
		slots[i].image=0;
	}
}
__ship_slot *FPKShip::GetSlots()
{
	return slots;
}

FPKEngine::FPKEngine(std::string sfpk, std::string sengine)
{
	Log::WriteVerbose("New FPKShip");
	image=0;
	price=0;
	this->GetFileInfo(sfpk, sengine);
	image=LoadIMG(imagefn);
}
FPKEngine::~FPKEngine()
{
	if (image) SDL_FreeSurface(image);
}
void FPKEngine::GetFileInfo(std::string sfpk, std::string sengine)
{
	Log::WriteVerbose("FPKEngine::GetFileInfo Begin");
	fpk = sfpk;

	char **spath = PHYSFS_getSearchPath();
	bool found = false;
	DataNode *packNode, *deleteNode;
	packNode=0;
	deleteNode=0;
	std::string temp;

	for (char **p = spath; *p != NULL; p++) {
		if (!strcmp(lastPathElement(*p).c_str(),fpk.c_str())) {
			PathSaver ps;
			PHYSFS_addToSearchPath(*p, 0);

			found = true;
			temp = *p;
			packNode = FPK_validatedPack(*p, &deleteNode);
		}
	}
	PHYSFS_freeList(spath);

	if (!found) {
		FPK_Invalid(sfpk, "FPK not found");
		return;
	}

	int cur = 0, num = 0;
	DataNodeList engines = packNode->getChild("objects")->getChildren("engine");
	DataNodeList::iterator iter;
	if (!engines.empty())
	{
		for (iter = engines.begin(); iter != engines.end(); iter++)
		{
			if ((*iter)->hasChild("name"))
			{
				std::string temp3=(*iter)->getChild("name")->getString();
				if (!strcmp(temp3.c_str(),sengine.c_str()))
				{
					num=cur;
				}
			}
			else FPK_Invalid(temp, "name command not found in an engine in FPK");
			cur++;
		}
	}

	name=engines[num]->getChild("name")->getString();

	if (engines[num]->hasChild("image")) imagefn=engines[num]->getChild("image")->getString();
	else FPK_Invalid(temp, "image command not found in an engine in FPK");

	if (engines[num]->hasChild("price"))
	{
		sprice=engines[num]->getChild("price")->getString();
		price=atoi(sprice.c_str());
	}
	else FPK_Invalid(temp, "price command not found in an engine in FPK");

	if (engines[num]->hasChild("maxwarp")) sMaxwarp=engines[num]->getChild("maxwarp")->getString();
	else FPK_Invalid(temp, "maxwarp command not found in an engine in FPK");
	maxwarp=SWarpToSpeed(sMaxwarp);
	if (maxwarp>SWarpToSpeed("9.999"))
	{
		maxwarp=SWarpToSpeed("9.999");
		sMaxwarp="9.999";
	}
	if (maxwarp<SWarpToSpeed("1.0"))
	{
		maxwarp=SWarpToSpeed("1.0");
		sMaxwarp="1.0";
	}

	if (engines[num]->hasChild("maxsafewarp")) sMaxsafewarp=engines[num]->getChild("maxsafewarp")->getString();
	else FPK_Invalid(temp, "maxsafewarp command not found in an engine in FPK");
	maxsafewarp=SWarpToSpeed(sMaxsafewarp);
	if (maxsafewarp>SWarpToSpeed("9.9"))
	{
		maxsafewarp=SWarpToSpeed("9.9");
		sMaxsafewarp="9.9";
	}
	if (maxsafewarp<SWarpToSpeed("1.0"))
	{
		maxsafewarp=SWarpToSpeed("1.0");
		sMaxsafewarp="1.0";
	}
	if (maxsafewarp>maxwarp)
	{
		maxsafewarp=maxwarp;
		sMaxsafewarp=sMaxwarp;
	}

	if (engines[num]->hasChild("maxspeed")) maxspeed=atoi(engines[num]->getChild("maxspeed")->getString().c_str());
	else FPK_Invalid(temp, "maxspeed command not found in an engine in FPK");
	if (maxspeed<1) maxspeed=1;
	if (maxspeed>20) maxspeed=20;

	if (engines[num]->hasChild("turndelay")) turndelay=atoi(engines[num]->getChild("turndelay")->getString().c_str());
	else FPK_Invalid(temp, "turndelay command not found in an engine in FPK");
	//Log::WriteVerbose("FPKShip::GetFileInfo CP");

	if (engines[num]->hasChild("turndelaywarp")) warpturndelay=atoi(engines[num]->getChild("turndelaywarp")->getString().c_str());
	else FPK_Invalid(temp, "turndelaywarp command not found in an engine in FPK");
	if (warpturndelay<turndelay) warpturndelay=turndelay;

	if (engines[num]->hasChild("overheatmax")) overheatmax=atoi(engines[num]->getChild("overheatmax")->getString().c_str());
	else FPK_Invalid(temp, "overheatmax command not found in an engine in FPK");

	delete deleteNode;
}
void FPKEngine::Reload(std::string sfpk, std::string sengine)
{
	if (image) SDL_FreeSurface(image);
	image=0;
	this->GetFileInfo(sfpk, sengine);
	image=LoadIMG(imagefn);
}
std::string FPKEngine::GetName()
{
	return name;
}
std::string FPKEngine::GetFPK()
{
	return fpk;
}
std::string FPKEngine::GetImageFN()
{
	return imagefn;
}
int FPKEngine::GetMaxSpeed()
{
	return maxspeed;
}
std::string FPKEngine::GetMaxWarp()
{
	return sMaxwarp;
}
int FPKEngine::GetMaxWarpSpeed()
{
	return maxwarp;
}
std::string FPKEngine::GetMaxSafeWarp()
{
	return sMaxsafewarp;
}
int FPKEngine::GetMaxSafeWarpSpeed()
{
	return maxsafewarp;
}
int FPKEngine::GetTurnDelay()
{
	return turndelay;
}
int FPKEngine::GetWarpTurnDelay()
{
	return warpturndelay;
}
unsigned int FPKEngine::GetPrice()
{
	return price;
}
std::string FPKEngine::GetSPrice()
{
	return sprice;
}
int FPKEngine::GetOverHeatMax()
{
	return overheatmax;
}
SDL_Surface *FPKEngine::GetImage()
{
	return image;
}

Preferences::Preferences(std::string fn)
{
	filename=fn;
	if(PHYSFS_exists(filename.c_str()))
	{
		this->Load();
	}
	else
	{
		std::string filecontent="(fft-prefs \r\n\t(controls \r\n\t\t";
		filecontent+="(fire1 right ctrl)\r\n\t\t";
		filecontent+="(fire2 left ctrl)\r\n\t\t";
		filecontent+="(warp1 right alt)\r\n\t\t";
		filecontent+="(warp2 left alt)\r\n\t\t";
		filecontent+="(up up)\r\n\t\t";
		filecontent+="(down down)\r\n\t\t";
		filecontent+="(left left)\r\n\t\t";
		filecontent+="(right right)\r\n\t\t";
		filecontent+="(allstop space)\r\n\t\t";
		filecontent+="(cancel escape)\r\n\t\t";
		filecontent+="(minimapzoomin keypad +)\r\n\t\t";
		filecontent+="(minimapzoomout keypad -)\r\n\t\t";
		filecontent+="(pause p)\r\n\t\t";
		filecontent+="(zoomin z)\r\n\t\t";
		filecontent+="(zoomout x)\r\n\t\t";
		filecontent+="(prevweapon /)\r\n\t\t";
		filecontent+="(nextweapon right shift)\r\n\t\t";
		filecontent+="(dock d)\r\n\t";
		filecontent+="(prevtarget r)\r\n\t\t";
		filecontent+="(nexttarget t)\r\n\t\t";
		filecontent+=")\r\n)";
		PHYSFS_file *fp=PHYSFS_openWrite(filename.c_str());
		PHYSFS_write(fp,filecontent.c_str(),1,filecontent.size());
		PHYSFS_close(fp);
		this->Load();
	}
}
Preferences::~Preferences()
{
	delete root;
}
void Preferences::Load()
{
	PHYSFS_file *fp=PHYSFS_openRead(filename.c_str());
	root=ReadDoc(fp);
	PHYSFS_close(fp);
}
void Preferences::Save()
{
	DataNode *controlRoot=root->getChild("fft-prefs")->getChild("controls");
	std::string filecontent="(fft-prefs \r\n\t(controls \r\n\t\t";
	filecontent+="(fire1 ";
	filecontent+=controlRoot->getChild("fire1")->getString();
	filecontent+=")\r\n\t\t(fire2 ";
	filecontent+=controlRoot->getChild("fire2")->getString();
	filecontent+=")\r\n\t\t(warp1 ";
	filecontent+=controlRoot->getChild("warp1")->getString();
	filecontent+=")\r\n\t\t(warp2 ";
	filecontent+=controlRoot->getChild("warp2")->getString();
	filecontent+=")\r\n\t\t(up ";
	filecontent+=controlRoot->getChild("up")->getString();
	filecontent+=")\r\n\t\t(down ";
	filecontent+=controlRoot->getChild("down")->getString();
	filecontent+=")\r\n\t\t(left ";
	filecontent+=controlRoot->getChild("left")->getString();
	filecontent+=")\r\n\t\t(right ";
	filecontent+=controlRoot->getChild("right")->getString();
	filecontent+=")\r\n\t\t(allstop ";
	filecontent+=controlRoot->getChild("allstop")->getString();
	filecontent+=")\r\n\t\t(cancel ";
	filecontent+=controlRoot->getChild("cancel")->getString();
	filecontent+=")\r\n\t\t(minimapzoomin ";
	filecontent+=controlRoot->getChild("minimapzoomin")->getString();
	filecontent+=")\r\n\t\t(minimapzoomout ";
	filecontent+=controlRoot->getChild("minimapzoomout")->getString();
	filecontent+=")\r\n\t\t(pause ";
	filecontent+=controlRoot->getChild("pause")->getString();
	filecontent+=")\r\n\t\t(zoomin ";
	filecontent+=controlRoot->getChild("zoomin")->getString();
	filecontent+=")\r\n\t\t(zoomout ";
	filecontent+=controlRoot->getChild("zoomout")->getString();
	filecontent+=")\r\n\t\t(prevweapon ";
	filecontent+=controlRoot->getChild("prevweapon")->getString();
	filecontent+=")\r\n\t\t(nextweapon ";
	filecontent+=controlRoot->getChild("nextweapon")->getString();
	filecontent+=")\r\n\t\t(dock ";
	filecontent+=controlRoot->getChild("dock")->getString();
	filecontent+=")\r\n\t\t(prevtarget ";
	filecontent+=controlRoot->getChild("prevtarget")->getString();
	filecontent+=")\r\n\t\t(nexttarget ";
	filecontent+=controlRoot->getChild("nexttarget")->getString();
	filecontent+=")\r\n\t)\r\n)";
	PHYSFS_file *fp=PHYSFS_openWrite(filename.c_str());
	PHYSFS_write(fp,filecontent.c_str(),1,filecontent.size());
	PHYSFS_close(fp);
}
void Preferences::ChangeInput(FFTInput *input)
{
	std::string filecontent="(fft-prefs \r\n\t(controls \r\n\t\t";
	filecontent+="(fire1 ";
	filecontent+=input->GetFire1Str();
	filecontent+=")\r\n\t\t(fire2 ";
	filecontent+=input->GetFire2Str();
	filecontent+=")\r\n\t\t(warp1 ";
	filecontent+=input->GetWarp1Str();
	filecontent+=")\r\n\t\t(warp2 ";
	filecontent+=input->GetWarp2Str();
	filecontent+=")\r\n\t\t(up ";
	filecontent+=input->GetUpStr();
	filecontent+=")\r\n\t\t(down ";
	filecontent+=input->GetDownStr();
	filecontent+=")\r\n\t\t(left ";
	filecontent+=input->GetLeftStr();
	filecontent+=")\r\n\t\t(right ";
	filecontent+=input->GetRightStr();
	filecontent+=")\r\n\t\t(allstop ";
	filecontent+=input->GetAllStopStr();
	filecontent+=")\r\n\t\t(cancel ";
	filecontent+=input->GetCancelStr();
	filecontent+=")\r\n\t\t(minimapzoomin ";
	filecontent+=input->GetMiniMapZoomInStr();
	filecontent+=")\r\n\t\t(minimapzoomout ";
	filecontent+=input->GetMiniMapZoomOutStr();
	filecontent+=")\r\n\t\t(pause ";
	filecontent+=input->GetPauseStr();
	filecontent+=")\r\n\t\t(zoomin ";
	filecontent+=input->GetZoomInStr();
	filecontent+=")\r\n\t\t(zoomout ";
	filecontent+=input->GetZoomOutStr();
	filecontent+=")\r\n\t\t(prevweapon ";
	filecontent+=input->GetPrevWeaponStr();
	filecontent+=")\r\n\t\t(nextweapon ";
	filecontent+=input->GetNextWeaponStr();
	filecontent+=")\r\n\t\t(dock ";
	filecontent+=input->GetDockStr();
	filecontent+=")\r\n\t\t(prevtarget ";
	filecontent+=input->GetPrevTargetStr();
	filecontent+=")\r\n\t\t(nexttarget ";
	filecontent+=input->GetNextTargetStr();
	filecontent+=")\r\n\t)\r\n)";
	delete root;
	root=ReadString(filecontent);
}
FFTInput *Preferences::GetInput()
{
	FFTInput *input=new FFTInput;
	DataNode *controlRoot=root->getChild("fft-prefs")->getChild("controls");
	input->SetFire1(input->StringToKeyCode(controlRoot->getChild("fire1")->getString()));
	input->SetFire2(input->StringToKeyCode(controlRoot->getChild("fire2")->getString()));
	input->SetWarp1(input->StringToKeyCode(controlRoot->getChild("warp1")->getString()));
	input->SetWarp2(input->StringToKeyCode(controlRoot->getChild("warp2")->getString()));
	input->SetUp(input->StringToKeyCode(controlRoot->getChild("up")->getString()));
	input->SetDown(input->StringToKeyCode(controlRoot->getChild("down")->getString()));
	input->SetLeft(input->StringToKeyCode(controlRoot->getChild("left")->getString()));
	input->SetRight(input->StringToKeyCode(controlRoot->getChild("right")->getString()));
	input->SetAllStop(input->StringToKeyCode(controlRoot->getChild("allstop")->getString()));
	input->SetCancel(input->StringToKeyCode(controlRoot->getChild("cancel")->getString()));
	input->SetMiniMapZoomIn(input->StringToKeyCode(controlRoot->getChild("minimapzoomin")->getString()));
	input->SetMiniMapZoomOut(input->StringToKeyCode(controlRoot->getChild("minimapzoomout")->getString()));
	input->SetPause(input->StringToKeyCode(controlRoot->getChild("pause")->getString()));
	input->SetZoomIn(input->StringToKeyCode(controlRoot->getChild("zoomin")->getString()));
	input->SetZoomOut(input->StringToKeyCode(controlRoot->getChild("zoomout")->getString()));
	input->SetPrevWeapon(input->StringToKeyCode(controlRoot->getChild("prevweapon")->getString()));
	input->SetNextWeapon(input->StringToKeyCode(controlRoot->getChild("nextweapon")->getString()));
	input->SetDock(input->StringToKeyCode(controlRoot->getChild("dock")->getString()));
	input->SetPrevTarget(input->StringToKeyCode(controlRoot->getChild("prevtarget")->getString()));
	input->SetNextTarget(input->StringToKeyCode(controlRoot->getChild("nexttarget")->getString()));
	return input;
}


