/*
 * $Id: environment.cpp 4654 2017-08-21 21:10:48Z antonin.misek-skoda-auto.cz $
 *
 * Description  : One line description of file.
 * Author       : Vaclav Kyba <mail/Jabber: vaseo1@gmail.com>  <ICQ: 98576293>
 *
 * Purpose      :
 *     Long description of what the file is for.
 */

#include "kernel.h"
#include "environment.h"
#include "module.h"

using namespace VRUT;


Environment::Environment()
{
}


Environment::~Environment()
{
}


wxString Environment::GetParam(const Parameter::ParameterIdentifier & paramId)
{
	wxCriticalSectionLocker readLock(registeredParametersCS);
	PoolNameInstParamList::const_iterator poolIt = registeredParameters.find(paramId.poolName);
	if (poolIt != registeredParameters.end())
	{
		NameInstParamList::const_iterator paramIt = poolIt->second.find(paramId.paramName);
		if (paramIt != poolIt->second.end())
		{
			InstParamList::const_iterator instIt = paramIt->second.find(paramId.instName);
			if (instIt != paramIt->second.end())
				return instIt->second.value;
		}
	}

	return wxEmptyString;
}

void Environment::GetParams(const Parameter::ParameterIdentifier & paramId, std::vector<NameValue> &values)
{
	wxCriticalSectionLocker readLock(registeredParametersCS);
	for (PoolNameInstParamList::const_iterator poolIt = registeredParameters.begin(); poolIt != registeredParameters.end(); ++poolIt)
	{
		wxString poolName = poolIt->first;
		if (!poolName.Upper().Matches(paramId.poolName.Upper()))
			continue;
		for (NameInstParamList::const_iterator parIt = poolIt->second.begin(); parIt != poolIt->second.end(); ++parIt)
		{
			wxString parName = parIt->first;
			if (!parName.Upper().Matches(paramId.paramName.Upper()))
				continue;
			for (InstParamList::const_iterator instIt = parIt->second.begin(); instIt != parIt->second.end(); ++instIt)
			{
				wxString instName = instIt->first;
				if (!instName.Upper().Matches(paramId.instName.Upper()))
					continue;
				Parameter::ParameterIdentifier pi(poolName, parName, instName);
				NameValue ret(pi.ToString(), CloneWxString(instIt->second.value));
				values.push_back(ret);
			}
		}
	}
}

void Environment::processName(const wxString &poolName, NameInstParamList::iterator paramIt, const Parameter::ParameterIdentifier &paramId, wxString &value, bool create, const wxString & originId)
{
	if (isMask(paramId.instName))
	{
		if (wxString(wxT("predef")).Matches(paramId.instName.Lower()))
		{
			InstParamList::iterator instIt = paramIt->second.find(wxT("predef"));
			if (instIt == paramIt->second.end())
			{
				if (create)
					paramIt->second.insert(InstParamList::value_type(wxT("predef"), Parameter(value)));
			}
			else
				instIt->second.value = CloneWxString(value);
		}

		for (auto &inst : paramIt->second)
		{
			wxString instName = inst.first;
			if ((instName!=wxT("predef")) && instName.Upper().Matches(paramId.instName.Upper()))
			{
				inst.second.value = CloneWxString(value);
				Parameter::ParameterIdentifier pi(poolName, paramIt->first, instName);
				LOGVERBOSE(wxT("<Environment>") + pi.ToString() + wxT(" = ") + value);
				KERNEL->GetMessageSink()->PostEvent(GET_EVT_PARAM_SET(pi, value, originId));
			}
		}
	}
	else
	{
		InstParamList::iterator instIt = paramIt->second.find(paramId.instName);
		if (instIt == paramIt->second.end())
		{
			if (create)
			{
				InstParamList::iterator predefIt = paramIt->second.find(wxT("predef"));
				if (predefIt != paramIt->second.end())
					value=predefIt->second.value;
				paramIt->second.insert(InstParamList::value_type(CloneWxString(paramId.instName), Parameter(value)));
			}
		}
		else
		{
			instIt->second.value=CloneWxString(value);
			Parameter::ParameterIdentifier pi(poolName, paramIt->first, instIt->first);
			LOGVERBOSE(wxT("<Environment>") + pi.ToString() + wxT(" = ") + value);
			KERNEL->GetMessageSink()->PostEvent(GET_EVT_PARAM_SET(pi, value, originId));
		}
	}
}

void Environment::processPool(PoolNameInstParamList::iterator poolIt, const Parameter::ParameterIdentifier &paramId, wxString &value, bool create, const wxString & originId)
{
	if (isMask(paramId.paramName))
	{
		for (NameInstParamList::iterator paramIt = poolIt->second.begin(); paramIt != poolIt->second.end(); ++paramIt)
		{
			wxString parName = paramIt->first;
			if (parName.Upper().Matches(paramId.paramName.Upper()))
				processName(poolIt->first, paramIt, paramId, value, false, originId);
		}
	}
	else
	{
		NameInstParamList::iterator paramIt = poolIt->second.find(paramId.paramName);
		if (paramIt == poolIt->second.end() && create)
		{
			poolIt->second.insert(NameInstParamList::value_type(CloneWxString(paramId.paramName), InstParamList()));
			paramIt = poolIt->second.find(paramId.paramName);
		}
		if (paramIt != poolIt->second.end())
			processName(poolIt->first, paramIt, paramId, value, true, originId);
	}
}

wxString Environment::SetParam(const Parameter::ParameterIdentifier & paramId, wxString value, const wxString & originId)
{
	wxCriticalSectionLocker readLock(registeredParametersCS);
	if (isMask(paramId.poolName))
	{
		for (PoolNameInstParamList::iterator poolIt = registeredParameters.begin(); poolIt != registeredParameters.end(); ++poolIt)
		{
			wxString poolName = poolIt->first;
			if (poolName.Upper().Matches(paramId.poolName.Upper()))
				processPool(poolIt, paramId, value, false, originId);
		}
	}
	else
	{
		PoolNameInstParamList::iterator poolIt = registeredParameters.find(paramId.poolName);
		if (poolIt == registeredParameters.end() && !isMask(paramId.paramName))
		{
			registeredParameters.insert(PoolNameInstParamList::value_type(CloneWxString(paramId.poolName), NameInstParamList()));
			poolIt = registeredParameters.find(paramId.poolName);
		}
		if (poolIt != registeredParameters.end())
			processPool(poolIt, paramId, value, true, originId);
	}
	return value;
}


bool Environment::UnregisterParam(const Parameter::ParameterIdentifier & paramId)
{
	bool found = false;

	wxCriticalSectionLocker readLock(registeredParametersCS);
	PoolNameInstParamList::iterator poolIt = registeredParameters.find(paramId.poolName);
	if (poolIt != registeredParameters.end())
	{
		std::vector<NameInstParamList::iterator> forDeletion;
		for (NameInstParamList::iterator parIt = poolIt->second.begin(); parIt != poolIt->second.end(); ++parIt)
		{
			wxString parName = parIt->first;
			if (parName.Matches(paramId.paramName))
			{
				std::vector<InstParamList::iterator> forDeletionInst;
				for (InstParamList::iterator instIt = parIt->second.begin(); instIt != parIt->second.end(); ++instIt)
				{
					wxString instName = instIt->first;
					if (instName.Matches(paramId.instName) && !instName.IsSameAs(wxT("predef")))
					{
						forDeletionInst.push_back(instIt);
						Parameter::ParameterIdentifier pi(poolIt->first, parName, instName);
						found = true;
						LOGVERBOSE(wxT("<Environment>Unregistered parameter ")+pi.ToString());
					}
				}
				for (std::vector<InstParamList::iterator>::const_iterator delIt = forDeletionInst.begin(); delIt != forDeletionInst.end(); ++delIt)
					parIt->second.erase(*delIt);
				if (parIt->second.empty())
					forDeletion.push_back(parIt);
			}
		}
		for (std::vector<NameInstParamList::iterator>::const_iterator delIt = forDeletion.begin(); delIt != forDeletion.end(); ++delIt)
			poolIt->second.erase(*delIt);
		if (poolIt->second.empty())
			registeredParameters.erase(poolIt);
	}

	return found;
}


// Warning LoadParams is not tested yet
void Environment::LoadParams(const wxString &fileName)
{
	wxFileName wxFname(fileName);
	if (!wxFname.FileExists()){
		LOGWARNING(wxT("<Environment>Unable to open file for loading parameters!"));
		return;
	}
	
	wxInputStream* stream = new wxFileInputStream(fileName);
	TextParser* tp = new TextParser(stream, 1000, 100);

	const char separators[] = ";\n\r";
	
	while (!tp->IsEmpty()) {
		wxString s = tp->GetWxStringUtf8(separators);
		KERNEL->GetMessageSink()->PostEvent(GET_EVT_INTERPRETER_PROCESS_COMMANDS(wxT("setparam "+s)));
	}

	delete stream;
	delete tp;
}

void Environment::SaveParams(const Parameter::ParameterIdentifier &pi, const wxString &fileName)
{
	wxFileOutputStream* file = new wxFileOutputStream(fileName);
	if (!file->IsOk()){
		LOGERROR(wxT("<Environment>Unable to create file for storing parameters!"));
		delete file;
		return;
	}

	wxBufferedOutputStream* stream = new wxBufferedOutputStream(*file);
	TextWriter *tw = new TextWriter(stream);

	std::vector<Environment::NameValue> vals;
	KERNEL->environment.GetParams(pi, vals);
	LOG(wxT("<Environment>Saving parameters to ") + fileName);
	
	for (std::vector<Environment::NameValue>::const_iterator it = vals.begin(); it != vals.end(); ++it) {
		LOG(it->first + wxT(" = ") + it->second);

		// cut the module instance ID from the param name
		TextParser txtParse(it->first.utf8_str());
		wxString module = txtParse.GetWxStringUtf8(".");
		wxString param = txtParse.GetWxStringUtf8(".");
		tw->Write((module + wxT(".") + param + wxT(" ") + it->second + wxT("\n")).utf8_str());
	}

	delete tw;
	delete stream;
	file->Close();
	delete file;
}
