/*
	BinMaker program
	Copyright 2007 Jonathan Wilson
	Portions lifted from BinOpener copyright 2007 booto (temptemp91 at hotmail dot com)

	This file is part of binmaker.

	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; either version 2 of the License, or
	(at your option) any later version.

	binmaker 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/
#include <stdio.h>
#include "tinyxml.h"
#include "manifestitem.h"
#include "gamescriptlist.h"
GameScriptList::GameScriptList(TiXmlNode *node)
{
	const char *id = node->ToElement()->Attribute("id");
	char *stream = new char[strlen(id)+16];
	StreamSize = sizeof(GSLHeader);
	offset = 0;
	currentimport = 0;
	ScriptGroup *sg = 0;
	sprintf(stream,"GameScriptList:%s",id);
	StreamName = stream;
	SourceName = _strdup("DATA:map.xml");
	_strlwr((char *)id);
	Header.filenamehash1 = 0xdebf8788;
	Header.filenamehash2 = hash_function(id,strlen(id));
	Header.filenamehash3 = 0x5ac6fa18;
	Header.filenamehash4 = Header.filenamehash2;
	for (TiXmlNode *child = node->FirstChild(); child; child = child->NextSibling())
	{
		if (child->Type() != TiXmlNode::ELEMENT)
		{
			continue;
		}
		if (child->ValueStr() == "ScriptSet")
		{
			sg = ParseScriptGroup(child);
		}
	}
	StreamData = new char[StreamSize];
	memset(StreamData,0,StreamSize);
	GSLHeader *g = (GSLHeader *)(StreamData+offset);
	g->rootoffset = sizeof(GSLHeader);
	RelocationEntries.push_back((char *)(&(g->rootoffset)) - StreamData);
	g->zero = 0;
	offset = sizeof(GSLHeader) + sizeof(GSLScriptGroup);
	StoreScriptGroup(sg,g->rootoffset);
}

ScriptGroup *GameScriptList::ParseScriptGroup(TiXmlNode *node)
{
	ScriptGroup *sg = new ScriptGroup;
	StreamSize += sizeof(GSLScriptGroup);
	ScriptGroup *sg2;
	Script *scr;
	if (node->ValueStr() == "ScriptSet")
	{
		sg->hash = 0;
	}
	else
	{
		const char *id = node->ToElement()->Attribute("id");
		_strlwr((char *)id);
		sg->hash = hash_function(id,strlen(id));
	}
	for (TiXmlNode *child = node->FirstChild(); child; child = child->NextSibling())
	{
		if (child->Type() != TiXmlNode::ELEMENT)
		{
			continue;
		}
		if (child->ValueStr() == "ScriptGroup")
		{
			sg2 = ParseScriptGroup(child);
			sg->scriptgroups.push_back(sg2);
		}
		if (child->ValueStr() == "Script")
		{
			scr = ParseScript(child);
			sg->scripts.push_back(scr);
		}
	}
	return sg;
}

Script *GameScriptList::ParseScript(TiXmlNode *node)
{
	Script *scr = new Script;
	StreamSize += sizeof(GSLScript);
	Action *act;
	const char *id = node->ToElement()->Attribute("id");
	_strlwr((char *)id);
	scr->hash = hash_function(id,strlen(id));
	for (TiXmlNode *child = node->FirstChild(); child; child = child->NextSibling())
	{
		if (child->Type() != TiXmlNode::ELEMENT)
		{
			continue;
		}
		if (child->ValueStr() == "Action")
		{
			act = ParseAction(child);
			scr->actions.push_back(act);
		}
	}
	return scr;
}

Action *GameScriptList::ParseAction(TiXmlNode *node)
{
	Action *act = new Action;
	StreamSize += sizeof(GSLAction);
	StreamSize += sizeof(GSLParameterList);
	StreamSize += sizeof(GSLObject);
	const char *type = node->ToElement()->Attribute("Type");
	act->hash = hash_function(type,strlen(type));
	act->object.hash = 0;
	act->object.value = 0;
	for (TiXmlNode *child = node->FirstChild(); child; child = child->NextSibling())
	{
		if (child->Type() != TiXmlNode::ELEMENT)
		{
			continue;
		}
		if (child->ValueStr() == "ParameterList")
		{
			for (TiXmlNode *child2 = child->FirstChild(); child2; child2 = child2->NextSibling())
			{
				if (child2->Type() != TiXmlNode::ELEMENT)
				{
					continue;
				}
				if (child2->ValueStr() == "ObjectType")
				{
					act->object.hash = 0x0383BAC7;
					act->object.value = 0;
					const char *obj = child2->ToElement()->Attribute("Object");
					_strlwr((char *)obj);
					act->e.filehash1 = 0x942fff2d;
					act->e.filehash2 = hash_function(obj,strlen(obj));
				}
			}
		}
	}
	return act;
}

void GameScriptList::StoreScriptGroup(ScriptGroup *scriptgroup,unsigned int sgoffset)
{
	GSLScriptGroup *sg = (GSLScriptGroup *)(StreamData+sgoffset);
	sg->hash = scriptgroup->hash;
	if (scriptgroup->scriptgroups.size())
	{
		sg->scriptgroups.length = scriptgroup->scriptgroups.size();
		sg->scriptgroups.offset = offset;
		RelocationEntries.push_back((char *)(&(sg->scriptgroups.offset)) - StreamData);
		offset += sizeof(GSLScriptGroup) * sg->scriptgroups.length;
	}
	else
	{
		sg->scriptgroups.length = 0;
		sg->scriptgroups.offset = 0;
	}
	if (scriptgroup->scripts.size())
	{
		sg->scripts.length = scriptgroup->scripts.size();
		sg->scripts.offset = offset;
		RelocationEntries.push_back((char *)(&(sg->scripts.offset)) - StreamData);
		offset += sizeof(GSLScript) * sg->scripts.length;
	}
	else
	{
		sg->scripts.length = 0;
		sg->scripts.offset = 0;
	}
	for (unsigned int i = 0;i < scriptgroup->scripts.size();i++)
	{
		int ofs = sg->scripts.offset + (i*sizeof(GSLScript));
		StoreScript(scriptgroup->scripts[i],ofs);
	}
	for (unsigned int i = 0;i < scriptgroup->scriptgroups.size();i++)
	{
		int ofs = sg->scriptgroups.offset + (i*sizeof(GSLScriptGroup));
		StoreScriptGroup(scriptgroup->scriptgroups[i],ofs);
	}
}

void GameScriptList::StoreScript(Script *script,unsigned int scoffset)
{
	GSLScript *sc = (GSLScript *)(StreamData+scoffset);
	sc->hash = script->hash;
	sc->actions.length = script->actions.size();
	sc->actions.offset = offset;
	RelocationEntries.push_back((char *)(&(sc->actions.offset)) - StreamData);
	offset += sizeof(GSLScript) * sc->actions.length;
	for (unsigned int i = 0;i < script->actions.size();i++)
	{
		int ofs = sc->actions.offset + (i*sizeof(GSLScript));
		StoreAction(script->actions[i],ofs);
	}
}

void GameScriptList::StoreAction(Action *action,unsigned int aoffset)
{
	GSLAction *act = (GSLAction *)(StreamData+aoffset);
	act->hash = action->hash;
	if (action->object.hash != 0)
	{
		act->parameterlist.length = 1;
		act->parameterlist.offset = offset;
		RelocationEntries.push_back((char *)(&(act->parameterlist.offset)) - StreamData);
		GSLParameterList *ofs = (GSLParameterList *)(StreamData+offset);
		offset += sizeof(GSLParameterList);
		ofs->offset = offset;
		RelocationEntries.push_back((char *)(&(ofs->offset)) - StreamData);
		GSLObject *obj = (GSLObject *)(StreamData+offset);
		offset += sizeof(GSLObject);
		obj->hash = action->object.hash;
		unsigned int imp = ((char *)(&(obj->value)) - StreamData);
		unsigned int hash1 = action->e.filehash1;
		unsigned int hash2 = action->e.filehash2;
		AddImport(hash1,hash2,imp);
		obj->value = currentimport;
		currentimport++;
	}
	else
	{
		act->parameterlist.length = 0;
		act->parameterlist.offset = 0;
	}
}
