#include "common/IFileStream.h"
#include "common/IDirectoryIterator.h"
#include "VMScript.h"
#include <map>
#include <list>

IDebugLog	gLog("scriptdump.log");

bool g_swap;

struct ScriptInfo
{
	ScriptInfo()
		:parent(NULL) { }

	typedef std::list <ScriptInfo *>	ScriptList;

	VMScript	script;
	VMScript	* parent;
	ScriptList	children;
};

void DumpRoot(ScriptInfo * info)
{
	_MESSAGE("%s", info->script.stringTable.Get(info->script.objectTable.m_data[0].name).c_str());
	gLog.Indent();
	for(ScriptInfo::ScriptList::iterator iter = info->children.begin(); iter != info->children.end(); ++iter)
	{
		DumpRoot(*iter);
	}
	gLog.Outdent();
}

std::string tolower(std::string src)
{
	for(int i = 0; i < src.size(); i++)
		src[i] = tolower(src[i]);

	return src;
}

void DumpHierarchy(const char * rootPath)
{
	typedef std::map <std::string, ScriptInfo *>	ScriptTable;
	ScriptTable	scripts;

	_MESSAGE("reading scripts");

	for(IDirectoryIterator iter(rootPath, "*.pex"); !iter.Done(); iter.Next())
	{
		IFileStream	src;
		if(src.Open(iter.GetFullPath().c_str()))
		{
			ScriptInfo	* info = new ScriptInfo;

			info->script.Read(&src);

			if(info->script.objectTable.m_data.size() != 1)
			{
				_MESSAGE("%s contains multiple objects", iter.Get()->cFileName);
			}
			else
			{
				scripts[tolower(info->script.stringTable.Get(info->script.objectTable.m_data[0].name))] = info;
			}
		}
		else
		{
			_ERROR("couldn't open %s", iter.GetFullPath().c_str());
		}
	}

	_MESSAGE("analyzing");

	typedef std::list <ScriptInfo *>	RootClassList;
	RootClassList	rootClasses;

	for(ScriptTable::iterator iter = scripts.begin(); iter != scripts.end(); ++iter)
	{
		ObjectTable::Info	* classInfo = &iter->second->script.objectTable.m_data[0];
		const std::string	& parentName = iter->second->script.stringTable.Get(classInfo->parentClassName);

		if(parentName.empty())
		{
			rootClasses.push_back(iter->second);
		}
		else
		{
			ScriptTable::iterator	parentInfo = scripts.find(tolower(parentName));
			if(parentInfo == scripts.end())
			{
				_ERROR("parent %s for class %s not found", parentName.c_str(), iter->first.c_str());
			}
			else
			{
				// hook it up
				iter->second->parent = &parentInfo->second->script;
				parentInfo->second->children.push_back(iter->second);
			}
		}
	}

	_MESSAGE("dumping roots");

	for(RootClassList::iterator iter = rootClasses.begin(); iter != rootClasses.end(); ++iter)
	{
		DumpRoot(*iter);
	}
}

int main(int argc, char ** argv)
{
	if(argc < 2)
	{
		_ERROR("usage: %s [src] <dst>", argv[0]);
		return 1;
	}

	if(argc >= 3)
	{
		gLog.Open(argv[2]);
		gLog.SetPrintLevel(IDebugLog::kLevel_Warning);
	}

	// right now it's big-endian but the loader supports both
	// future versions might change this?
	g_swap = false;

	const char	* srcPath = argv[1];

#if 1
	IFileStream	src;
	if(src.Open(srcPath))
	{
		VMScript	script;

		script.Read(&src);
		script.Dump();

		_MESSAGE("remain: %016I64X", src.GetRemain());
	}

#else

	DumpHierarchy(srcPath);
#endif
}
