/*  DS
 *  Copyright (C) Anders Asplund and Joakim Kolsjö 2005
 *	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.
 *
 *  This program 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 
 */

#include "scriptparser.h"

using namespace std;

namespace DS
{	
	ScriptParser::ScriptParser()
	{
		m_pDataReader = 0;
	}
	
	ScriptParser::~ScriptParser()
	{
		std::map <std::string, Factory<Handler>*>::iterator fiter = m_factories.begin();
		while(fiter != m_factories.end())
		{
			delete fiter->second;
			++fiter;
		}	

		for(int i = 0; i < (int)m_scenes.size(); i++)
			delete m_scenes[i];
		
		delete m_pDataReader;
		m_scenes.clear();
		m_factories.clear();
	}
	
	void ScriptParser::Parse()
	{
		static char* fn = "[ScriptParser::Parse]";
		
		// Open and parse
		XMLParser* pXMLParser = new XMLParser;
		XMLElement* pRootElement = pXMLParser->Parse(m_pScriptFile->GetData(), m_pScriptFile->GetLength());
		
		// Get demo title, info and music file
		m_demotitle = GetAttr(pRootElement, "title");
		m_musicfile = GetAttr(pRootElement, "music");
		
		// Temporary bindinfo storage (cleared after each scene)
		Map* pBindMap = new Map();
		
		// Parse scenes
		std::vector<XMLElement*> scenes = pRootElement->GetChildElements();
		for(int i = 0; i < (int)scenes.size(); i++)
		{		
			const char* pSceneID = 0;
			const char* pSceneRuntime = 0;
			const char* pSceneActive = 0;
			
			// Get scene attributes
			if((string)scenes[i]->GetValue() == "scene")
			{
				pSceneID = GetAttr(scenes[i], "id");
				pSceneRuntime = GetAttr(scenes[i], "runtime");
				pSceneActive = GetAttr(scenes[i], "active");
			}
			else
				throw Exception(fn, (string)"\"scene\" expected but not found at line "
					+ itoa(scenes[i]->GetRow()) + ".");
			
			Scene* pScene = new Scene;
			pScene->SetID(pSceneID);
			pScene->SetRange(0, atol(pSceneRuntime));
			pScene->SetActive(atoi(pSceneActive));
			m_scenes.push_back(pScene);
						
			// Look for scene contents
			std::vector<XMLElement*> contents = scenes[i]->GetChildElements();
			for(int j = 0; j < (int)contents.size(); j++)
			{
				string current = contents[j]->GetValue();
				
				// Check if it's a bind, else look for a handler
				if((string)contents[j]->GetValue() == "bind") {
					pBindMap->Insert(GetAttr(contents[j], "id"),
						ProcessMaths(GetAttr(contents[j], "value"), pBindMap).c_str());
				}
				else if(m_factories[current])
				{	
					Handler* pHandler = m_factories[current]->Create();
					pScene->AddHandler(pHandler);
					
					Map* pMap = pHandler->GetHandlerAttr();
					ParseAttributes(contents[j], pMap, pBindMap, false);
					
					// Parse (handler-)node(s) attributes
					std::vector<XMLElement*> nodes = contents[j]->GetChildElements();
					if(!nodes.size())
						throw Exception(fn, (string)"Nodes missing for \"" + current
							+ "\" at line "	+ itoa(contents[j]->GetRow()) + ".");
					else if(nodes.size() < 2)
						throw Exception(fn, (string)"Need atleast 2 nodes (start and stop) for \"" + current
							+ "\" at line "	+ itoa(contents[j]->GetRow()) + ".");
					
					long c = -1;
					for(int k = 0; k < (int)nodes.size(); k++)
					{
						Map* pNodeMap = pHandler->CreateNodeAttr();
						ParseAttributes(nodes[k], pNodeMap, pBindMap, true);

						// Check that time is usable
						long next = atol(pNodeMap->Get("time"));
						if(next < 0 || next <= c)
							throw Exception(fn, (string)"Unusable time value at line " + itoa(nodes[k]->GetRow()) + ".");
						c = next;
					}
					
					pHandler->Init();
				}
				else
					throw Exception(fn, (string)"There is no handler for \""
						+ current + "\" at line " + itoa(contents[j]->GetRow()) + ".");
			}
			
			pBindMap->Clear();
		}
		
		delete pBindMap;
		delete pXMLParser;
	}
		
	const char* ScriptParser::GetAttr(XMLElement* pElement, const char* pAttribute)
	{
		static char *fn = "[ScriptParser::GetAttr]";
		const char* pA = pElement->GetAttribute(pAttribute);
		if(pA)
			return pA;
		else
			throw Exception(fn, (string)"\"" + pAttribute
				+ "\" is missing at line " + itoa(pElement->GetRow()) + ".");
	}
	
	void ScriptParser::ParseAttributes(XMLElement* pElement, Map* pMap, Map* pBindMap, bool bNode)
	{
		static char* fn = "[ScriptParser::ParseAttributes]";
		vector<string> index = pMap->GetList();
		
		// Browse attribute list and get values
		for(int i = 0; i < (int)index.size(); i++)
		{
			// Check for a value
			const char* pA = pElement->GetAttribute(index[i].c_str());
			if(pA) {
				if(bNode)
					pMap->Insert(index[i].c_str(),
						ProcessMaths(pElement->GetAttribute(index[i].c_str()), pBindMap).c_str());
				else
					pMap->Insert(index[i].c_str(), GetAttr(pElement, index[i].c_str()));
			}
			else { // Check if a default value exists
				const char* pValue = pMap->Get(index[i].c_str());
				if((string)pValue != "required")
					pMap->Insert(index[i].c_str(), pValue);
				else				
					throw Exception(fn, (string)" \"" + pElement->GetValue()
						+ "\" requires a value for \"" + index[i]
						+ "\" at line " + itoa(pElement->GetRow()) + ".");
			}
		}
	}
	
	float ScriptParser::Calc(const char* pNumberString, float sum, char op)
	{
		float n = atof(pNumberString);
		if(op == '+')
			sum += n;
		else if(op == '-')
			sum -= n;
		else if(op == '*')
			sum *= n;
		else if(op == '/')
			sum /= n;
			
		return sum;
	}
	
	std::string ScriptParser::ProcessMaths(const char* pText, Map* pBindMap)
	{
		static char* fn = "[ScriptParser::ProcessMaths]";
		string str = pText;

		vector<string> bindindex = pBindMap->GetList();
		
		for(int i = 0; i < (int)bindindex.size(); i++)
		{
			// Translate binds
			string current = bindindex[i];
			unsigned int loc = str.find(current);
			if(loc != string::npos)
			{
				str = str.substr(0, loc) + pBindMap->Get(current.c_str()) +
					str.substr(loc + current.size(), str.size() - loc + current.size());
				break;
			}	
		}
		
		bindindex.clear();
		
		// Calculate sum
		float sum = 0;
		vector<int> signs;
		for(int j = 1; j < (int)str.size(); j++)
			if(str[j] == '+' || str[j] == '-' || str[j] == '*' || str[j] == '/')
				signs.push_back(j);

		if(signs.size() == 1) {
			sum = atof(str.substr(0, signs[0]).c_str());
			sum = Calc(str.substr(signs[0]+1, str.size() - signs[0] - 1).c_str(), sum, str[signs[0]]);
			str = ftoa(sum);
		} else if(signs.size() > 1)
			throw Exception(fn, (string)"More than one operator used in \""
				+ pText + "\".");

		// Calculate sum
	/*	int strnum = 0;
		float sum = 0;
		map<int, char> signs;
		vector<int> sindex;
		vector<string> snumbers;
		snumbers....
		for(int i = 1; i < (int)str.size(); i++) {
			if(str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/') {
				signs[i] = str[i];
				sindex.push_back(i);
				if(i)
					strnum++;
			}
			snumbers[strnum] += str[i];
		}

		cout << str << endl;
		for(int i = 0; i < (int)sindex.size(); i++)
		{
			// -2*5
			cout << sindex[i] << endl;
			cout << signs[sindex[i]] << endl;
			cout << snumbers[0] << endl;
			//if(!i && !sindex[0])
//				sum = Calc(snumbers[0], 0, signs[0]);
			*/
			
			/*sum = atof(str.substr(0, signs[]).c_str());
			float n = atof(str.substr(signs[0]+1, str.size() - signs[0] - 1).c_str());
			if(str[signs[0]] == '+')
				sum += n;
			else if(str[signs[0]] == '-')
				sum -= n;
			else if(str[signs[0]] == '*')
				sum *= n;
			else if(str[signs[0]] == '/')
				sum /= n;
			
			stringstream ss; ss << sum;
			str = ss.str();*/
		//}
/*		else if(signs.size() > 1)
			throw Exception(fn, (string)"More than one operator used in \""
				+ pText + "\".");*/
		return str;
	}

	void ScriptParser::AddHandlerFactory(const char* pKey, Factory<Handler>* pHandlerFactory)
	{
		static char* fn = "[ScriptParser::AddHandlerFactory]";
		if(m_factories[pKey])
			throw Exception(fn, (string)"\"" + pKey + "\" already exists.");
		
		m_factories[pKey] = pHandlerFactory;
	}
}
