/****************************************************************************
	Kalkulon - A programmable calculator for programmers

	This file is part of the Kalkulon core.
	
	If you find this source code useful, your are encouraged to use it in your own projects.
	Please give me credit!

	COPYRIGHT by Juergen Holetzeck 2003 - 2008
	e-mail:		contact@kalkulon.de
	homepage:	www.kalkulon.de
****************************************************************************/


// RPNStackEval_fn.cpp: implementation of the CRPNStackEval class.
//
//////////////////////////////////////////////////////////////////////


#include "stdafx.h"

#include <math.h>
#include <fstream>
#include <sstream>

#if _DEBUG
#include <iostream>
#endif

#include "RPNStackEval.h"
#include "ArithParser.h"
#include "ErrBase.h"
#include "fnlist_internal.h"

using namespace Kalkulon;

void CRPNStackEval::func(Token &tok)
{
	// function from internal function list
	if(call_fn(tok, fn_list)) return;

	// user defined functions via funcdll.dll
	#ifdef USE_WIN32_LIB
	if(FnDllValid && FnDll_fn_list)
	
	{
		bool ok=call_fn(tok, *FnDll_fn_list);
		if(ok && GetLastError && GetLastError())
		{
			if(GetErrorMessage)
				throw ErrMath(tok, GetErrorMessage());
			else
				throw ErrMath(tok, "unknown");
		}
		
		if(ok) return;
	}
	#endif

	// online declared user functions
	if(call_userfn(tok)) return;

	// no function found
	throw ErrUnknFunc(tok);
}


/////////////////////////////////////////////////////////////////////
// call function from a function list
bool CRPNStackEval::call_fn(Token &tok, const fn_list_t& fn_list)
{
	// return if requested function is not defined in this list
	if(tok.func_idx && tok.pfn_list != (void *) &fn_list) return false;
	
	double par0, par1, par2;
	
	size_t end=fn_list.size;
	for(size_t i=tok.func_idx; i<end; ++i)
	{
		unsigned int type = fn_list.fn_list_item[i].type;
		if(	tok.func_idx || 
			(	(unsigned int) tok.token_int == (type & 0xff) &&	// check number of arguments
				tok.token_str == fn_list.fn_list_item[i].name)		// check function name
			)
		{
			// store function index/pointer for later use, e.g. loops/functions
			tok.func_idx = i;
			tok.pfn_list = (void *) &fn_list;
			
			switch(type)
			{
			case FP_VOID:
				datastack.push (((fp_void) fn_list.fn_list_item[i].fp) ());
				return true;	
			case FP_DOUBLE:
				datastack.push (((fp_double) fn_list.fn_list_item[i].fp) (datastack.pop()));
				return true;	
			case FP_DOUBLE_DOUBLE:
				par0 = datastack.pop();
				datastack.push (((fp_double_double) fn_list.fn_list_item[i].fp) (datastack.pop(), par0));
				return true;
			case FP_DOUBLE_DOUBLE_DOUBLE:
				par1 = datastack.pop();
				par0 = datastack.pop();
				datastack.push (((fp_double_double_double) fn_list.fn_list_item[i].fp) (datastack.pop(), par0, par1));
				return true;
			case FP_DOUBLE_DOUBLE_DOUBLE_DOUBLE:
				par2 = datastack.pop();
				par1 = datastack.pop();
				par0 = datastack.pop();
				datastack.push (((fp_double_double_double_double) fn_list.fn_list_item[i].fp) (datastack.pop(), par0, par1, par2));
				return true;
			case FP_INT:
				datastack.push (((fp_int) fn_list.fn_list_item[i].fp) (static_cast<S32>(datastack.pop())));
				return true;	
			case FP_INT_INT:
				par0 = datastack.pop();
				datastack.push (((fp_int_int) fn_list.fn_list_item[i].fp) (static_cast<S32>(datastack.pop()), static_cast<S32>(par0)));
				return true;
			case FP_INT_INT_INT:
				par1 = datastack.pop();
				par0 = datastack.pop();
				datastack.push (((fp_int_int_int) fn_list.fn_list_item[i].fp) (static_cast<S32>(datastack.pop()), static_cast<S32>(par0), static_cast<S32>(par1)));
				return true;
			case FP_INT_INT_INT_INT:
				par2 = datastack.pop();
				par1 = datastack.pop();
				par0 = datastack.pop();
				datastack.push (((fp_int_int_int_int) fn_list.fn_list_item[i].fp) (static_cast<S32>(datastack.pop()), static_cast<S32>(par0), static_cast<S32>(par1), static_cast<S32>(par2)));
				return true;
			case FP_INT_DOUBLE:
				par0 = datastack.pop();
				datastack.push (((fp_int_double) fn_list.fn_list_item[i].fp) (static_cast<S32>(datastack.pop()), par0));
				return true;
			case FP_DOUBLE_INT:
				par0 = datastack.pop();
				datastack.push (((fp_double_int) fn_list.fn_list_item[i].fp) (datastack.pop(), static_cast<S32>(par0)));
				return true;
			case FP_STRING:
				datastack.push (((fp_string) fn_list.fn_list_item[i].fp) (static_cast<std::string>(datastack.pop())));
				return true;
			case FP_STRING_STRING:
				{
					const DataObject& par0 = datastack.pop();
					datastack.push (((fp_string_string) fn_list.fn_list_item[i].fp) (static_cast<std::string>(datastack.pop()),static_cast<std::string>(par0)));
					return true;
				}
			case DO_FP_EVAL_STRING:
				datastack.push (((do_fp_eval_string) fn_list.fn_list_item[i].fp) (this, static_cast<std::string>(datastack.pop())));
				return true;
			case DO_FP_EVAL_DO:
				datastack.push (((do_fp_eval_do) fn_list.fn_list_item[i].fp) (this, datastack.pop()));
				return true;
			case DO_FP_EVAL_DO_DO:
				{
					const DataObject& par0 = datastack.pop();
					datastack.push (((do_fp_eval_do_do) fn_list.fn_list_item[i].fp) (this, datastack.pop(), par0));
					return true;
				}
			case DO_FP_EVAL_DO_DO_DO:
				{
					const DataObject& par1 = datastack.pop();
					const DataObject& par0 = datastack.pop();
					datastack.push (((do_fp_eval_do_do_do) fn_list.fn_list_item[i].fp) (this, datastack.pop(), par0, par1));
					return true;
				}
			case DO_FP_EVAL_DO_DO_DO_DO:
				{
					const DataObject& par2 = datastack.pop();
					const DataObject& par1 = datastack.pop();
					const DataObject& par0 = datastack.pop();
					datastack.push (((do_fp_eval_do_do_do_do) fn_list.fn_list_item[i].fp) (this, datastack.pop(), par0, par1, par2));
					return true;
				}
			case FP_EVAL_STRING:
				datastack.push (((fp_eval_string) fn_list.fn_list_item[i].fp) (this, static_cast<std::string>(datastack.pop())));
				return true;
			case DO_FP_EVAL:
				datastack.push (((do_fp_eval) fn_list.fn_list_item[i].fp) (this));
				return true;
			case FP_EVAL:
				datastack.push (((fp_eval) fn_list.fn_list_item[i].fp) (this));
				return true;
			case FP_DO:
				datastack.push (((fp_do) fn_list.fn_list_item[i].fp) (datastack.pop()));
				return true;
			case DO_FP_DO:
				datastack.push (((do_fp_do) fn_list.fn_list_item[i].fp) (datastack.pop()));
				return true;
			case DO_FP_DO_DO:
				{
					const DataObject& par0 = datastack.pop();
					datastack.push (((do_fp_do_do) fn_list.fn_list_item[i].fp) (datastack.pop(), par0));
					return true;
				}
			case DO_FP_DO_DO_DO:
				{
					const DataObject& par1 = datastack.pop();
					const DataObject& par0 = datastack.pop();
					datastack.push (((do_fp_do_do_do) fn_list.fn_list_item[i].fp) (datastack.pop(), par0, par1));
					return true;
				}
			case STRING_FP_VOID:
				{
					datastack.push (((string_fp_void) fn_list.fn_list_item[i].fp) ());
					return true;
				}
			case STRING_FP_STRING:
				{
					datastack.push (((string_fp_string) fn_list.fn_list_item[i].fp) (static_cast<std::string>(datastack.pop())));
					return true;
				}
			case STRING_FP_STRING_DO:
				{
					const DataObject& par0 = datastack.pop();
					datastack.push (((string_fp_string_do) fn_list.fn_list_item[i].fp) (static_cast<std::string>(datastack.pop()), par0));
					return true;
				}
			default:
				return false;
			}
		}
	}
	return false;
}


bool CRPNStackEval::call_userfn(Token &tok)
{
	// add number of arguments to function name, eg. f(x,y) -> f@2
	std::string fn(tok.token_str);
	{
		char buf[32];
		buf[0] = '@';
#if _MSC_VER >= 1400
		_itoa_s((unsigned int) tok.token_int, buf+1, 31, 10);
#else
		itoa((unsigned int) tok.token_int,buf+1,10);
#endif
		fn+=buf;
	}
	
	size_t num_param;
	functions_t::iterator it;
	if( (it=functions.find(fn))!=functions.end() )
	{	// user function found

		// set function index/pointer to mark function as 'userfn'
		tok.func_idx = (size_t) -1;
		tok.pfn_list = NULL;

		symbols_t sym_tmp;

		num_param=it->second.param_list.size();
		for(size_t i=num_param;i>0;--i)
			sym_tmp[it->second.param_list[i-1]]=datastack.pop();
		symbols_list.push_back(&sym_tmp);

		try{
			datastack.push(eval(it->second.rpn_stack));
			symbols_list.pop_back();
			return true;
		}
		catch(ErrBase &err)
		{
			symbols_list.pop_back();
			throw ErrInDefFunc(fn, err.GetErrorMessage());
		}
		catch(...)
		{
			symbols_list.pop_back();
			throw;
		};
	}
	return false;
}


bool CRPNStackEval::eval_purefn(Token &tok)
{
	// new local scope
	symbols_t sym_tmp;
	{
		char buf[32];
		buf[0] = '#';
		for(size_t i=tok.token_int;i>0;--i)
		{		
#if _MSC_VER >= 1400
			_itoa_s((unsigned int) i, buf+1, 31, 10);
#else
			itoa((unsigned int) i,buf+1,10);
#endif
			if(i==1)
				sym_tmp["#"]=sym_tmp[buf]=datastack.pop();
			else
				sym_tmp[buf]=datastack.pop();
		}
	}
	symbols_list.push_back(&sym_tmp);
	
	const DataObject& dob=datastack.pop();
	if(dob.getDid() != DataObject::PUREFUNC) 	throw ErrMathInternal("operator@","TYPE MISMATCH");

	try{
		datastack.push(eval(*dob.getPtrRPNStack()));
		symbols_list.pop_back();
		return true;
	}
	catch(ErrBase &err)
	{
		symbols_list.pop_back();
		throw ErrInDefFunc("pure func", err.GetErrorMessage());
	}
	catch(...)
	{
		symbols_list.pop_back();
		throw;
	};
}

/////////////////////////////////////////////////////////////////////
// implementation of commands

			
int CRPNStackEval::load(const char* file)
{
	// open source file
	std::ifstream in(file, std::ios_base::binary);
	
	if(in)
	{
		CArithParser	parser;
		RPNStack_t		rpn_stack;
		try{
			parser.parse(&in, &rpn_stack);
#if _DEBUG
				// print parser results
				RPNStack_t::iterator it		= rpn_stack.begin();
				RPNStack_t::iterator itend	= rpn_stack.end();
				
				while(it != itend)
				{
					std::cout << *(it++) << " ";
				}
				std::cout << std::endl;
#endif
			eval(rpn_stack);
		}
		catch(ErrBase &err)
		{
			std::string t("Load(\"");
			t+=file;
			t+="\")";
			throw ErrInDefFunc(t, err.GetErrorMessage());
		}
		catch(...)
		{
			throw;
		};

		return 0;
	}
	else
	{
		return 1;
	}
}


DataObject  CRPNStackEval::parseEval(const std::string& expr)
{
	CArithParser	parser;
	RPNStack_t		rpn_stack;
	try{
		std::istringstream is(expr);
		parser.parse(&is, &rpn_stack);
		return eval(rpn_stack);
	}
	catch(ErrBase &err)
	{
		std::string t("Eval(\"");
		t+=expr;
		t+="\")";
		throw ErrInDefFunc(t, err.GetErrorMessage());
	}
	catch(...)
	{
		throw;
	};
}


DataObject CRPNStackEval::ForEach1(std::vector<DataObject>* plist, RPNStack_t* ppurefunc)
{
	// new local scope
	symbols_t sym_tmp;
	symbols_list.push_back(&sym_tmp);

	DataObject r(DataObject::LIST);
	try{
		size_t size= plist->size();
		for(size_t i=0; i<size; ++i)
		{
			sym_tmp["#"] = (*plist)[i];
			r.getPtrListValue()->push_back(eval(*ppurefunc));
		}

		symbols_list.pop_back();
		return r;
	}
	catch(ErrBase &err)
	{
		symbols_list.pop_back();
		throw ErrInDefFunc("ForEach", err.GetErrorMessage());
	}
	catch(...)
	{
		symbols_list.pop_back();
		throw;
	};
}


DataObject CRPNStackEval::ForEach2(std::vector<DataObject>* plist1, std::vector<DataObject>* plist2, RPNStack_t* ppurefunc)
{
	// new local scope
	symbols_t sym_tmp;
	symbols_list.push_back(&sym_tmp);

	DataObject r(DataObject::LIST);
	try{
		size_t size= plist1->size();
		for(size_t i=0; i<size; ++i)
		{
			sym_tmp["#"] = sym_tmp["#1"] = (*plist1)[i];
			sym_tmp["#2"] = (*plist2)[i];
			r.getPtrListValue()->push_back(eval(*ppurefunc));
		}

		symbols_list.pop_back();
		return r;
	}
	catch(ErrBase &err)
	{
		symbols_list.pop_back();
		throw ErrInDefFunc("ForEach", err.GetErrorMessage());
	}
	catch(...)
	{
		symbols_list.pop_back();
		throw;
	};
}


DataObject CRPNStackEval::Table(double start, double end, double delta, RPNStack_t* ppurefunc)
{
	// new local scope
	symbols_t sym_tmp;
	symbols_list.push_back(&sym_tmp);

	delta = fabs(delta);
	double mult = 0, idx;

	DataObject r(DataObject::LIST);
	try{
		if(start<=end)
			while((idx=start+mult++*delta)<=end)
			{
				sym_tmp["#"] = idx;
				r.getPtrListValue()->push_back(eval(*ppurefunc));
			}
		else
			while((idx=start-mult++*delta)>=end)
			{
				sym_tmp["#"] = idx;
				r.getPtrListValue()->push_back(eval(*ppurefunc));
			}

		symbols_list.pop_back();
		return r;
	}
	catch(ErrBase &err)
	{
		symbols_list.pop_back();
		throw ErrInDefFunc("Table", err.GetErrorMessage());
	}
	catch(...)
	{
		symbols_list.pop_back();
		throw;
	};
}


DataObject CRPNStackEval::Fold(std::vector<DataObject>* plist, const DataObject& first, RPNStack_t* ppurefunc)
{
	// new local scope
	symbols_t sym_tmp;
	symbols_list.push_back(&sym_tmp);

	DataObject r(first);
	try{
		size_t size= plist->size();
		for(size_t i=0; i<size; ++i)
		{
			sym_tmp["#"] = sym_tmp["#1"] = r;
			sym_tmp["#2"] = (*plist)[i];
			r = eval(*ppurefunc);
		}

		symbols_list.pop_back();
		return r;
	}
	catch(ErrBase &err)
	{
		symbols_list.pop_back();
		throw ErrInDefFunc("Fold", err.GetErrorMessage());
	}
	catch(...)
	{
		symbols_list.pop_back();
		throw;
	};
}


DataObject CRPNStackEval::FoldList(std::vector<DataObject>* plist, const DataObject& first, RPNStack_t* ppurefunc)
{
	// new local scope
	symbols_t sym_tmp;
	symbols_list.push_back(&sym_tmp);

	DataObject r(DataObject::LIST);
	r.getPtrListValue()->push_back(first);
	try{
		size_t size= plist->size();
		for(size_t i=0; i<size; ++i)
		{
			sym_tmp["#"] = sym_tmp["#1"] = (*r.getPtrListValue())[i];
			sym_tmp["#2"] = (*plist)[i];
			r.getPtrListValue()->push_back(eval(*ppurefunc));
		}

		symbols_list.pop_back();
		return r;
	}
	catch(ErrBase &err)
	{
		symbols_list.pop_back();
		throw ErrInDefFunc("FoldList", err.GetErrorMessage());
	}
	catch(...)
	{
		symbols_list.pop_back();
		throw;
	};
}


void CRPNStackEval::clearAll()
{
	out_idx=-1; 
	symbols.clear(); 
	symbols_list.clear();
	symbols_list.push_back(&symbols);
	functions.clear();
	DataObject::setNumberFormat(DataObject::STD_FORMAT);
}

/////////////////////////////////////////////////////////////////////
// overwritten _matherr funtion to handle global floating point errors

#if _MSC_VER

#pragma warning( disable : 4297) // function assumed not to throw an exception but does
int _matherr( struct _exception *except )
{
	switch(except->type)
	{
		case _DOMAIN:
			throw ErrMathInternal(except->name,"DOMAIN");
		case _SING:
			throw ErrMathInternal(except->name,"SINGULARITY");
		case _OVERFLOW:
			throw ErrMathInternal(except->name,"OVERFLOW");
		case _PLOSS:
		case _TLOSS:
			throw ErrMathInternal(except->name,"LOSS");
		case _UNDERFLOW:
			throw ErrMathInternal(except->name,"UNDERFLOW");
		default:
			throw ErrMathInternal(except->name,"UNKNOWN");
	}
//	return 1;
}
#pragma warning( default : 4297)
#endif
