/**
* FX Parse
* @version: 12-28-2004
* @author Joseph 'Pcjoe' Florencio
*/

#include "stdafx.h"
#include "fxsys.h"

CParseMain g_ParseMain;

/************************
 * Parse Main
 ************************/

/*
* Initialize
*/
void CParseMain::Initialize()
{	// Reset & Parse
	Reset();
	parseXML();
}

/*
* Reset
*/
void CParseMain::Reset( void )
{	// Reset
	m_Precache.clear();
	m_ParseFX.Reset();
}

/*
* Get Precache
*/
CFXPrecache *CParseMain::getPrecache( std::string strPrecache )
{	std::map<std::string,CFXPrecache>::iterator iterSearch;
	ForEachIn(iterSearch, m_Precache)
	{	if(iterSearch->first == strPrecache)
		{	return (&iterSearch->second);
		}
	}
	return NULL;
}

/*
* Get Path
*/
std::string CParseMain::getPath( TiXmlNode* pPath )
{	return getAttribute(pPath, "path");
}

/*
* Parse XML File(s)
*/
void CParseMain::parseXML( void )
{	bool ok = true;

	// Add XML Interface
	addInterface();

	// Parse index file
	ok = getInterface()->Parse(getBaseDir() + FX_SCRIPT_INDEX);
	if(ok)
	{	ok = parseIndex( getInterface()->getDocElem() );
	}
	
	// Remove interface
	popInterface();

	// Check for error
	if(errorParse(ok, getBaseDir() + FX_SCRIPT_INDEX))
		return;

	// Manage Precache (precache actual elements in index)
	managePrecache(getIndexList("precache"));

	// Manage FX (save FX data)
	m_ParseFX.setFileList(getIndexList("fx"));
	m_ParseFX.manageFiles("");
//	m_ParseFX.Print();
}

/*
* Parse XML Index Fle
*/
bool CParseMain::parseIndex( TiXmlNode* pIndexNode )
{	TiXmlNode* pGroups=NULL;
	TiXmlNode* pElements=NULL;
	precache_list_s list;

	// Iterate through index groups
	IterateXML(pGroups, pIndexNode)
	{
		// clear list
		list.vecList.clear();

		// Get path files are stored in
		std::string strPath = getPath(pGroups);

		// Iterate through elements and add to list
		IterateXML(pElements, pGroups)
		{	list.vecList.push_back(strPath + getPath(pElements));
		}

		// Copy over vector list to index map
		addIndexList(pGroups->Value(), list);
	}

	return true;
}

/************************
 * Index List
 ************************/

/*
* Get Index List
*/
std::vector<std::string> CParseMain::getIndexList( std::string strIndex )
{	std::map<std::string,precache_list_s>::iterator iterIndex = m_IndexList.find(strIndex);
	if(iterIndex != m_IndexList.end())
	{	return iterIndex->second.vecList;
	}
	std::vector<std::string> vec;
	return vec;
}

/*
* Add Index List
*/
void CParseMain::addIndexList( std::string strPath, precache_list_s list )
{	m_IndexList[strPath] = list;
}

/************************
 * Parse Base
 ************************/

/*
* Print Error Parse
*/
bool CParseBase::errorParse( bool ok, std::string strFile )
{	if(!ok)
	{	std::string strPrint = "ERROR: Can't parse \"";
		strPrint += strFile;
		strPrint += "\"!\n";
		ConsolePrint(strPrint.c_str());
		return true;
	}
	return false;
}

/*
* Get Base Dir
*/
std::string CParseBase::getBaseDir( void )
{
	std::string strDir = gEngfuncs.pfnGetGameDirectory();
	strDir += "/";
	strDir += FX_SCRIPT_MAIN_DIR;
	strDir += "/";
	return strDir;
}

/************************
 * Precache
 ************************/

/*
* Manage Precache
*/
bool CParseMain::managePrecache( std::vector<std::string> &precacheList )
{	std::vector<std::string>::iterator iterPrecache;
	// Iterate through precache list
	ForEachIn(iterPrecache, precacheList)
	{	// Precache respective element
		CFXPrecache precache_element((*iterPrecache));

		// save if valid
		if(precache_element.isValid())
		{	m_Precache[(*iterPrecache)] = precache_element;
		}
	}

	return true;
}

/************************
 * XML Interface
 ************************/

/*
* Get Attribute
*/
std::string CParseBase::getAttribute( TiXmlNode* pNode, std::string strAttributeName )
{	TiXmlElement *pElem = pNode->ToElement();
	if(pElem)
	{	char *pAttri = (char*)pElem->Attribute(strAttributeName.c_str());
		if(pAttri)
			return std::string(pAttri);
	}
	return std::string("");
}

/*
* Add interface
*/
void CParseBase::addInterface( void )
{	// Create
	CXML_Interface xmlInterface;
	m_XMLInterface.push_back(xmlInterface);
	// Update iterator
	m_iterXMLInterface = m_XMLInterface.end()-1;
}

/*
* Pop interface (remove latest interface)
*/
void CParseBase::popInterface( void )
{	if(!m_XMLInterface.empty())
	{	// Clean up
		m_iterXMLInterface->deleteDocument();

		// Remove
		m_XMLInterface.pop_back();

		// Empty, no more interfaces. Kill iterator
		if(m_XMLInterface.empty())
		{	/*removed by harSens, compile fix
			m_iterXMLInterface = (std::vector<CXML_Interface>::iterator)NULL;			
			*/
		}
		// Update interface
		else
		{	m_iterXMLInterface = m_XMLInterface.end()-1;
		}
	}
}

/*
* Clear interface(s)
*/
void CParseBase::clearInterfaces( void )
{	while(!m_XMLInterface.empty())
	{	popInterface();
	}
}

/*
* Get Current Interface
*/
CXML_Interface *CParseBase::getInterface( void )
{	/*modified by harSens: compile fix
	if(m_iterXMLInterface != NULL)
	{	return &(*m_iterXMLInterface);
	}
	*/
	if(!m_XMLInterface.empty())
	{	return &(*m_iterXMLInterface);
	}
	return NULL;
}


/************************
 * Parse Generic
 ************************/

// Constructor
CParseGeneric::CParseGeneric()
{	Reset();
}

/*
* Reset
*/
void CParseGeneric::Reset( void )
{	m_Elements.clear();
	clearFileList();
}


/************************
 * File Iterator
 ************************/

/*
* Clear File List
*/
void CParseGeneric::clearFileList( void )
{	m_fileList.clear();
	/* removed by harSens, compile fix
	m_fileListIter = NULL;	
	*/
}

/*
* Set File List
*/
void CParseGeneric::setFileList( std::vector<std::string> fileList )
{	m_fileList = fileList;
	m_fileListIter = m_fileList.begin();
}

/************************
 * Element Management
 ************************/

 /*
* Find Script Element
*/
CFXStorage *CParseGeneric::findScriptElement( std::string strFind )
{	CFXStorage *pFind = NULL;
	std::map<std::string,CFXStorage>::iterator iterFind = m_Elements.find(strFind);
	if(iterFind != m_Elements.end())
	{	pFind = &(iterFind->second);
	}
	return pFind;
}


/*
* Add System
*/
void CParseGeneric::addSystem( std::string strName, CFXStorage fxStorage )
{	fxStorage.setName(strName);
	m_Elements[strName] = fxStorage;
}

/*
* Print
*/
void CParseGeneric::Print( void )
{	std::map<std::string,CFXStorage>::iterator iterElem;
	// Print script elements
	ConsolePrint("++++Script Elements++++\n");
	ForEachIn(iterElem, m_Elements)
	{	iterElem->second.printData();
	}
}

/************************
 * Generic Load System
 ************************/

/*
* Manage Files
*/
bool CParseGeneric::manageFiles( std::string strSearch )
{	bool search = !strSearch.empty();
	bool ok = true;
	std::vector<std::string>::iterator iterFX;


	// Iterate through files
	for(iterFX = getFileIter(); iterFX != m_fileList.end(); iterFX++)
	{	
		// Only update iterator when we're not searching
		if(!search)
			updateFileIter(iterFX);

		// Add XML Interface
		addInterface();

		// Parse and load systems
		if(ok = getInterface()->Parse(getBaseDir() + (*iterFX)))
		{	ok = iterateSystems( getInterface()->getDocElem(), strSearch );
		}
		
		// Terminate XML Interface
		popInterface();

		// Looking for something specific
		if(search)
		{	// Didn't find match, go to next file
			if(!ok)
				continue;
			// Found result + loaded, stop searching
			return true;
		}

		// Check for error
		if(errorParse(ok, (*iterFX)))
			continue;
	}
	return ok;
}

/*
* Load Element
*/
CFXStorage CParseGeneric::loadElement( TiXmlNode* pSystem, CFXStorage *pDerive )
{	TiXmlNode* pOptions = NULL;

	// Save implementation name
	CFXStorage fxStorage(getAttribute(pSystem, "implement"));

	// Copy over options if derived
	fxStorage.saveOptions(pDerive);

	// Iterate through options & add
	IterateXML(pOptions, pSystem)
	{	CFXData fxData;
		if(fxData.setData(pOptions))
		{	fxStorage.addOption(fxData.getName(), fxData);
		}
	}

	return fxStorage;
}


/*
* Manage Derive
*/
CFXStorage *CParseGeneric::loadDerive( TiXmlNode* pSystem, std::string strDerive )
{	CFXStorage *pDerive = NULL;
	// Can't find
	if(iterateSystems(pSystem, strDerive) ? false : !manageFiles(strDerive))
	{	ConsolePrint("CANT PARSE DERIVED CLASS \"");
		ConsolePrint(strDerive.c_str());
		ConsolePrint("\"!\n");
	}
	// Found, just load
	else
	{	pDerive = findScriptElement(strDerive);
	}
	return pDerive;
}

/*
* Setup Derive
*/
CFXStorage *CParseGeneric::setupDerive( TiXmlNode* pSystem )
{	CFXStorage *pDerive = NULL;

	if(canDerive())
	{	std::string strDerive = getAttribute(pSystem, "derive");
		// Load derived system(s) (if any)
		if(!strDerive.empty() && !(pDerive=findScriptElement(strDerive)))
		{	pDerive=loadDerive(pSystem, strDerive);
		}
	}
	return pDerive;
}

/*
* Setup System
*/
void CParseGeneric::setupSystem( TiXmlNode* pSystem, std::string strName )
{	// Get derived data (if any)
	CFXStorage *pDerive = setupDerive(pSystem);

	// Setup & add FX
	addSystem(strName, loadElement(pSystem, pDerive));
}

/*
* Iterate Systems
*/
bool CParseGeneric::iterateSystems( TiXmlNode* pIndexNode, std::string strSearch )
{	TiXmlNode* pSystems=NULL;
	TiXmlNode* pElements=NULL;
	bool ok = false;
	bool search = !strSearch.empty();

	// Iterate through systems
	IterateXML(pSystems, pIndexNode)
	{	// Grab name
		std::string strName = getAttribute(pSystems, "name");

		// If we're not looking for anything, or what we've been looking for was found
		if((search && strSearch == strName) || !search)
		{	ok=true;

			// Already loaded, exit
			if(findScriptElement(strName))
				continue;

			// Setup System
			setupSystem(pSystems, strName);

			// We searched, found, and setup the fx element. exit
			if(search)
				return true;
		}
	}

	return ok;
}
