//  
//  GLScript.cs
//  
//  Author:
//       Robert BRACCAGNI alias Gai-Luron <lfsgailuron@free.fr>
// 
//  Copyright (c) 2010 Gai-Luron
// 
//  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 3 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, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections;
using System.Text;
using System.Text.RegularExpressions;
namespace GLScript
{
    public class GLApp
    {
        public class GLScriptException : Exception
        {
            public GLScriptException(string message) : base(message) { }
            public GLScriptException(string message, Exception innerException) : base(message, innerException) { }
        }
        public class functionDef
        {
            public string[] argsNames;
            public int nbArgs = 0;
            public TokenBuffer tkb;
            public int StartLineInSrc;
            public bool retFlag = false;
            public ArrayList functionAttached = new ArrayList();
            public functionDef(string[] pargsNames, int pnbArgs, TokenBuffer ptkb, int pStartLineInSrc)
            {
                this.argsNames = pargsNames;
                this.tkb = ptkb;
                this.StartLineInSrc = pStartLineInSrc;
                this.nbArgs = pnbArgs;
            }
        }
        Stack stk = new Stack(2048);

        bool retFlag = false;
        private string nameApp;
        public SetOfVars GVAR = new SetOfVars( );
        private System.Collections.Hashtable Functions = new System.Collections.Hashtable();
        public delegate void callBackFunction(unionVal var, ArrayList ar);
        Hashtable listOfCallBackFunctions = new Hashtable();
		public long funcTimeOut = 2000; // Function timeOut, limit infinite loop
		private DateTime funcTimeOutTime;
        private GLDebug.Debug myDebug;

        const int R_CONTINUE = 1;
        const int R_BREAK = 2;
        const int R_ERROR = -1;

        public GLApp( GLDebug.Debug pmyDebug, string p_nameApp)
        {
            this.nameApp = p_nameApp;
            this.myDebug = pmyDebug;
        }
        public GLApp(string p_nameApp)
        {
            this.nameApp = p_nameApp;
        }
        public void registerCallBackFunction( string functionName,  callBackFunction pCBF )
        {
            listOfCallBackFunctions[ functionName.ToLower() ] = pCBF;
        }
		public void registerFunction(string funcName, string buffer, string pargsNames, string fileName, int StartLineInSrc, string AttachToEvent, Hashtable localconst, Hashtable globalconst)
        {
            string[] argsNames = null;
            int nbArgs = 0;
            if (pargsNames != "")
            {
                argsNames = pargsNames.Split(',');
                nbArgs = argsNames.Length;
            }
            
            Functions[funcName.ToLower()] = new functionDef(argsNames, nbArgs, new TokenBuffer(buffer, fileName, StartLineInSrc,localconst,globalconst), StartLineInSrc);

			if (AttachToEvent != "")
            {
                if (Functions.Contains(AttachToEvent.ToLower()))
                {
                    functionDef currFunc = (functionDef)( Functions[AttachToEvent.ToLower()] );
                    currFunc.functionAttached.Add(funcName.ToLower()); // Attach a function to this function
                }
            }
        }
        public void registerFunction( string funcName, functionDef myFuncDef )
        {
            Functions[funcName.ToLower()] = myFuncDef;
        }
        public void dumpFunction(string funcName)
        {
            funcName = funcName.ToLower();
            TokenBuffer tkb = (Functions[funcName] as functionDef).tkb;
            for (int i = 0; i < tkb.TokenList.Count; i++)
            {
                token tk = (token)tkb.TokenList[i];
                myDebug.WriteLine("err",tk.typ + " : " + tk.value + " at file " + tk.fileName +" line " + tk.lineNumber);
            }
        }
        public void dumpNamesFunctions()
        {
            foreach (DictionaryEntry d in Functions)
            {
                Console.WriteLine(d.Key);
            }
        }
        public bool existFunction(string funcName)
        {
            funcName = funcName.ToLower();
            return (Functions.ContainsKey(funcName));
        }
        public void setReturnFlagFunction(string funcName, bool flg)
        {
            funcName = funcName.ToLower();
            (Functions[funcName] as functionDef).retFlag = flg;
        }
        public bool isReturnFlagFunction(string funcName)
        {
            funcName = funcName.ToLower();
            return (Functions[funcName] as functionDef).retFlag;
        }

        public bool executeFunction(string funcName, string[] argsVals)
        {
            bool ret;
            ret = privExecuteFunction(funcName, argsVals);
            funcName = funcName.ToLower();
            if (Functions.Contains(funcName))
            {
                functionDef currFunc = (functionDef)Functions[funcName];
                for (int i = 0; i < currFunc.functionAttached.Count; i++)
                {
                    ret = privExecuteFunction((string)currFunc.functionAttached[i], argsVals); // Execute Attached Function
                }
            }
            stk.Clear();
            return ret;
        }

		private bool privExecuteFunction(string funcName, string[] argsVals)
		{
			funcName = funcName.ToLower();
			if (!Functions.Contains(funcName))
				return false;
			(Functions[funcName] as functionDef).retFlag = false;
			SetOfVars LVAR = new SetOfVars();
			int lg = 0;
			if (argsVals != null)
				lg = argsVals.Length;

			if (lg != (Functions[funcName] as functionDef).nbArgs)
			{
				string errorMsg = "Syntax error in cfg file: Number of function argument not matche at line #" + (Functions[funcName] as functionDef).StartLineInSrc + " in function '" + funcName + "' script aborted";
				myDebug.WriteLine("err", errorMsg);
				return false;
			}
			for (int i = 0; i < lg; i++)
			{
				LVAR.Set((Functions[funcName] as functionDef).argsNames[i].Substring(1), new unionVal(0, "\"" + argsVals[i] + "\"", typVal.str));
			}
			funcTimeOutTime = DateTime.Now.AddMilliseconds( funcTimeOut );
			if (privExecuteTokensPart(0, funcName, GVAR, LVAR, false, false, -1, -1) == R_ERROR)
				return false;
			else
				return true;
		}
        private int privExecuteTokensPart(int level, string funcName, SetOfVars GCAR, SetOfVars LVAR, bool breakable, bool inSwitchCase, int start, int end)
        {


            string yytext;
            string argLine = "";
            char breakCar;
            int currLine = 0;
            string errorMsg = "";
            bool resultTestCond = false;
            int retVal = R_CONTINUE;

            token tok;
            TokenParse tkparse;

            funcName = funcName.ToLower();
            TokenBuffer CurrTokenBuffer = (Functions[funcName] as functionDef).tkb;

            tkparse = new TokenParse(CurrTokenBuffer, start, end);
            while (true)
            {

                tok = tkparse.readNextToken();
				yytext = tok.value;
				currLine = tok.lineNumber;

				if (DateTime.Now > funcTimeOutTime) // TimeOut infinite loop
				{
					errorMsg = "Time Out in Event or Sub " + funcTimeOut.ToString() + " ms";
					goto endFunction;
				}

                //                                    Console.WriteLine(yytext);
                switch (tok.typ)
                {
                    case Scanner.E_GLOBALVAR:
						#region GLOBALVAR
						{
                            tok = tkparse.readNextToken();
                            if (tok.typ != Scanner.E_VAR)
                            {
                                errorMsg = "var ident need";
                                goto endFunction;
                            }
                            string idVar = tok.value.Substring(1);
                            tok = tkparse.readNextToken();
                            if (tok.typ != Scanner.E_SEMICOLON)
                            {
                                errorMsg = "';' needed at end of line";
                                goto endFunction;
                            }
                            GVAR.globVars[idVar] = true;
                        }
                        break;
						#endregion
					case Scanner.E_FUNCTION:
					case Scanner.E_IDENT:
					case Scanner.E_STRING:
                    case Scanner.E_INTEGER:
                    case Scanner.E_FLOAT:
					case Scanner.E_VAR:
					case Scanner.E_ARVAR:
					case Scanner.E_PAR_OPEN:
						#region STANDARD EVALUATION
						breakCar = ' ';
                        try
                        {
                            argLine = retrieveEvalVar(funcName, tkparse, CurrTokenBuffer, GVAR, LVAR, false, ref breakCar);
                        }
                        catch (GLScriptException e)
                        {
                            errorMsg = e.Message;
                            goto endFunction;
                        }
                        catch
                        {
                            throw;
                        }

                        if (breakCar == '!')
                        {
                            errorMsg = "';' needed";
                            goto endFunction;
                        }
                        if (isReturnFlagFunction(funcName))
                            return R_CONTINUE;
                        //                        Console.WriteLine(argLine);
                        break;
						#endregion
					case Scanner.E_DEFAULT:
                        goto case Scanner.E_CASE;
                    case Scanner.E_CASE:
						#region CASE
						tok = tkparse.readNextToken();
						if (inSwitchCase)
                        {
                            breakCar = ' ';
                            try
                            {
                                argLine = retrieveEvalVar(funcName, tkparse, CurrTokenBuffer, GVAR, LVAR, true, ref breakCar);
                            }
                            catch (GLScriptException e)
                            {
                                errorMsg = e.Message;
                                goto endFunction;
                            }
                            catch (Exception e)
                            {
                                throw e;
                            }
                            if (breakCar != ':')
                            {
                                errorMsg = "Need ':' in a CASE option";
                                goto endFunction;
                            }
                            break;
                        }
                        errorMsg = "CASE or DEFAULT not attempted";
                        goto endFunction;
						#endregion
					case Scanner.E_SWITCH:
						#region SWITCH
						tok = tkparse.readNextToken();
                        breakCar = ' ';
                        try
                        {
                            argLine = retrieveEvalVar(funcName, tkparse, CurrTokenBuffer, GVAR, LVAR, true, ref breakCar);
                        }
                        catch (GLScriptException e)
                        {
                            errorMsg = e.Message;
                            goto endFunction;
                        }
                        catch (Exception e)
                        {
                            throw e;
                        }
                        if (breakCar == '!')
                        {
                            errorMsg = "';' needed";
                            goto endFunction;
                        }

                        string testVal = argLine;
                        int startCasePart = -1;
                        int endCasePart = -1;
                        int nbSwitch = 0;
                        tok = tkparse.readNextToken();
                        while (true)
                        {
                            if (tok.typ != Scanner.E_CASE && tok.typ != Scanner.E_DEFAULT && tok.typ != Scanner.E_ENDSWITCH)
                            {
                                errorMsg = "'CASE or DEFAULT' needed";
                                goto endFunction;
                            }
                            if (tok.typ == Scanner.E_ENDSWITCH)
                            {
                                break;
                            }
                            breakCar = ' ';
							if (tok.typ == Scanner.E_DEFAULT)
							{
								argLine = testVal;
								tok = tkparse.readNextToken();
								if (tok.typ != Scanner.E_DOUBLEDOT)
								{
									errorMsg = "Need ':' in a DEFAULT option";
									goto endFunction;
								}
							}
							else
							{
								tok = tkparse.readNextToken();
								try
								{
									argLine = retrieveEvalVar(funcName, tkparse, CurrTokenBuffer, GVAR, LVAR, true, ref breakCar);
								}
								catch (GLScriptException e)
								{
									errorMsg = e.Message;
									goto endFunction;
								}
								catch (Exception e)
								{
									throw e;
								}

								if (breakCar != ':')
								{
									errorMsg = "Need ':' in a CASE option";
									goto endFunction;
								}
							}
                            if (testVal == argLine)
                            {
                                startCasePart = tkparse.curPos+1;
                                nbSwitch = 0;
                                while (true)
                                {
                                    tok = tkparse.readNextToken();
                                    if (tok.typ == Scanner.E_END)
                                    {
                                        errorMsg = "Switch not terminated";
                                        goto endFunction;
                                    }
                                    if (tok.typ == Scanner.E_SWITCH)
                                        nbSwitch++;
                                    if (tok.typ == Scanner.E_ENDSWITCH && nbSwitch == 0)
                                    {
                                        endCasePart = tkparse.curPos - 1;
                                        break;
                                    }
                                    if (tok.typ == Scanner.E_ENDSWITCH)
                                        nbSwitch--;
                                }
                                if (startCasePart != -1)
                                {
                                    retVal = privExecuteTokensPart(level + 1, funcName, GVAR, LVAR, true, true, startCasePart, endCasePart);
                                    tkparse.curPos = endCasePart + 1;
                                }
                                break;
                            }
                            else
                            {
                                nbSwitch = 0;
                                while (true)
                                {
                                    tok = tkparse.readNextToken();
                                    if (tok.typ == Scanner.E_END)
                                    {
                                        errorMsg = "Switch not terminated";
                                        goto endFunction;
                                    }
                                    if (tok.typ == Scanner.E_SWITCH)
                                        nbSwitch++;
                                    if ((tok.typ == Scanner.E_ENDSWITCH || tok.typ == Scanner.E_CASE || tok.typ == Scanner.E_DEFAULT ) && nbSwitch == 0)
                                        break;
                                    if (tok.typ == Scanner.E_ENDSWITCH)
                                        nbSwitch--;
                                }
                            }
                        }


                        break;
						#endregion
					case Scanner.E_IF:
						#region IF
						token tokIf = tok;
                        resultTestCond = false;
                        tok = tkparse.readNextToken();
                        breakCar = ' ';
                        try
                        {
                            argLine = retrieveEvalVar(funcName, tkparse, CurrTokenBuffer, GVAR, LVAR, true, ref breakCar);
                        }
                        catch (GLScriptException e)
                        {
                            errorMsg = e.Message;
                            goto endFunction;
                        }
                        catch (Exception e)
                        {
                            throw e;
                        }
                        if (breakCar == '!')
                        {
                            errorMsg = "';' needed";
                            goto endFunction;
                        }
                        if (isReturnFlagFunction(funcName))
                            return R_CONTINUE;
                        try
                        {
                            float fvalue = float.Parse(argLine);
                            if (fvalue > 0)
                                resultTestCond = true;
                            else
                                resultTestCond = false;
                        }
                        catch
                        {
                            if (argLine.Length > 0)
                                resultTestCond = true;
                            else
                                resultTestCond = false;
                        }
                        tok = tkparse.readNextToken();
                        if (tok.typ != Scanner.E_THEN)
                        {
                            errorMsg = "'THEN' needed";
                            goto endFunction;
                        }
                        int startThenPart = tkparse.curPos + 1;
                        int endThenPart = tokIf.endThenPart;
                        int startElsePart = tokIf.startElsePart;
                        int endElsePart = tokIf.endElsePart;

						if (tokIf.startThenPart == 0 ) // Find part of if if never finded
						{
							int nbIf = 0;
							while (true)
							{

								tok = tkparse.readNextToken();
								if (tok.typ == Scanner.E_END)
								{
									errorMsg = "'IF' not terminated";
									goto endFunction;
								}
								if (tok.typ == Scanner.E_IF)
									nbIf++;
								if (nbIf == 0 && tok.typ == Scanner.E_ELSE)
								{
									endThenPart = tkparse.curPos - 1;
									startElsePart = tkparse.curPos + 1;
								}
								if (nbIf == 0 && tok.typ == Scanner.E_ENDIF)
								{
									if (startElsePart != 0)
										endElsePart = tkparse.curPos - 1;
									else
										endThenPart = tkparse.curPos - 1;
									break;
								}
								if (tok.typ == Scanner.E_ENDIF)
									nbIf--;
							}
							tokIf.startThenPart = startThenPart;
							tokIf.endThenPart = endThenPart;
							tokIf.startElsePart = startElsePart;
							tokIf.endElsePart = endElsePart;
						}
						else{
							if (startElsePart != 0)
								tkparse.curPos = endElsePart + 1;
							else
								tkparse.curPos = endThenPart + 1;
						}
                        retVal = R_CONTINUE;
                        if (resultTestCond)
                            retVal = privExecuteTokensPart(level + 1, funcName, GVAR, LVAR, breakable,false, startThenPart, endThenPart);
                        else
                        {
                            if (startElsePart != 0)
                                retVal = privExecuteTokensPart(level + 1, funcName, GVAR, LVAR, breakable, false, startElsePart, endElsePart);
                        }
                        if (retVal == R_BREAK)
                            return R_BREAK;
						if (retVal == R_ERROR)
							return retVal;
						if (isReturnFlagFunction(funcName))
							return R_CONTINUE;
                        break;
						#endregion
					case Scanner.E_WHILE:
						#region WHILE
						resultTestCond = false;

						tok = tkparse.readNextToken();
                        breakCar = ' ';
                        int posTestWhile = tkparse.curPos;
                        try
                        {
                            argLine = retrieveEvalVar(funcName, tkparse, CurrTokenBuffer, GVAR, LVAR, true, ref breakCar); // Only to pos to begin While
                        }
                        catch (GLScriptException e)
                        {
                            errorMsg = e.Message;
                            goto endFunction;
                        }
                        catch (Exception e)
                        {
                            throw e;
                        }

                        if (breakCar == '!')
                        {
                            errorMsg = "';' needed";
                            goto endFunction;
                        }
                        if (isReturnFlagFunction(funcName))
                            return R_CONTINUE;

                        int posStartWhilePart = tkparse.curPos + 1;
                        int endWhilePart = 0;
                        int nbWhile = 0;
                        while (true)
                        {
                            tok = tkparse.readNextToken();
                            if (tok.typ == Scanner.E_END)
                            {
                                errorMsg = "'WHILE' not terminated";
                                goto endFunction;
                            }
                            if (tok.typ == Scanner.E_WHILE)
                                nbWhile++;
                            if (nbWhile == 0 && tok.typ == Scanner.E_ENDWHILE)
                            {
                                endWhilePart = tkparse.curPos - 1;
                                break;
                            }
                            if (tok.typ == Scanner.E_ENDWHILE)
                                nbWhile--;
                        }
                        while (true)
                        {
                            tkparse.curPos = posTestWhile;
                            try
                            {
                                argLine = retrieveEvalVar(funcName, tkparse, CurrTokenBuffer, GVAR, LVAR, true, ref breakCar);
                            }
                            catch (GLScriptException e)
                            {
                                errorMsg = e.Message;
                                goto endFunction;
                            }
                            catch (Exception e)
                            {
                                throw e;
                            }
                            if (breakCar == '!')
                            {
                                errorMsg = "';' needed";
                                goto endFunction;
                            }
                            if (isReturnFlagFunction(funcName))
                                return R_CONTINUE;
                            try
                            {
                                float fvalue = float.Parse(argLine);
                                if (fvalue > 0)
                                    resultTestCond = true;
                                else
                                    resultTestCond = false;
                            }
                            catch
                            {
                                if (argLine.Length > 0)
                                    resultTestCond = true;
                                else
                                    resultTestCond = false;
                            }
                            if (resultTestCond)
                            {
                                retVal = privExecuteTokensPart(level + 1, funcName, GVAR, LVAR, true, false, posStartWhilePart, endWhilePart);
								if (retVal == R_BREAK)
								{
									tkparse.curPos = endWhilePart + 1;
									break;
								}
								if (retVal == R_ERROR)
									return retVal;
								if (isReturnFlagFunction(funcName))
									return R_CONTINUE;
							}
                            else
                            {
                                tkparse.curPos = endWhilePart + 1;
                                break;
                            }
                        }
                        break;
						#endregion
					case Scanner.E_FOR:
                        #region Region FOR...ENDFOR

                        {
							token tokFor = tok;
							resultTestCond = false;

                            int startArg = 1;
                            int endArg = 0;
							tok = tkparse.readNextToken();
                            breakCar = ' ';
                            retrieveArg(tkparse, false, ref breakCar, ref startArg, ref endArg);
                            if (breakCar == '!')
                            {
                                errorMsg = "';' needed";
                                goto endFunction;
                            }
                            endArg++; // Position end in ;
                            privExecuteTokensPart(level + 1, funcName, GVAR, LVAR, true, false, startArg, endArg);
							tok = tkparse.readNextToken();
							int posTestFor = tkparse.curPos;
                            try
                            {
                                argLine = retrieveEvalVar(funcName, tkparse, CurrTokenBuffer, GVAR, LVAR, false, ref breakCar);
                            }
                            catch (GLScriptException e)
                            {
                                errorMsg = e.Message;
                                goto endFunction;
                            }
                            catch (Exception e)
                            {
                                throw e;
                            }

                            if (breakCar == '!')
                            {
                                errorMsg = "';' needed";
                                goto endFunction;
                            }
                            if (isReturnFlagFunction(funcName))
                                return R_CONTINUE;
                            startArg = 0;
                            endArg = 0;
                            breakCar = ' ';
							tok = tkparse.readNextToken();
							retrieveArg(tkparse, true, ref breakCar, ref startArg, ref endArg);
                            if (breakCar == '!')
                            {
                                errorMsg = "';' needed";
                                goto endFunction;
                            }
                            endArg++; // Position end in )

                            tok = tkparse.readCurrToken();
                            if (tok.typ != Scanner.E_PAR_CLOSE)
                            {
                                errorMsg = "')' needed";
                                goto endFunction;
                            }
                            int posStartForPart = tkparse.curPos + 1;
                            int posEndForPart = 0;
                            int nbFor = 0;
							if (tokFor.startFor == 0)
							{
								while (true)
								{
									tok = tkparse.readNextToken();
									if (tok.typ == Scanner.E_END)
									{
										errorMsg = "'FOR' not terminated";
										goto endFunction;
									}
									if (tok.typ == Scanner.E_FOR)
										nbFor++;
									if (nbFor == 0 && tok.typ == Scanner.E_ENDFOR)
									{
										posEndForPart = tkparse.curPos - 1;
										break;
									}
									if (tok.typ == Scanner.E_ENDFOR)
										nbFor--;
								}
								tokFor.startFor = posStartForPart;
								tokFor.endFor = posEndForPart;
							}
							else
							{
								posEndForPart = tokFor.endFor;
								tkparse.curPos = posEndForPart + 1;
							}
                            while (true)
                            {
                                tkparse.curPos = posTestFor;
                                try
                                {
                                    argLine = retrieveEvalVar(funcName, tkparse, CurrTokenBuffer, GVAR, LVAR, false, ref breakCar);
                                }
                                catch (GLScriptException e)
                                {
                                    errorMsg = e.Message;
                                    goto endFunction;
                                }
                                catch (Exception e)
                                {
                                    throw e;
                                }
                                if (breakCar == '!')
                                {
                                    errorMsg = "';' needed";
                                    goto endFunction;
                                }
                                if (isReturnFlagFunction(funcName))
                                    return R_CONTINUE;


                                try
                                {
                                    float fvalue = float.Parse(argLine);
                                    if (fvalue > 0)
                                        resultTestCond = true;
                                    else
                                        resultTestCond = false;
                                }
                                catch
                                {
                                    if (argLine.Length > 0)
                                        resultTestCond = true;
                                    else
                                        resultTestCond = false;
                                }
                                if (resultTestCond)
                                {
                                    retVal = privExecuteTokensPart(level + 1, funcName, GVAR, LVAR, true, false, posStartForPart, posEndForPart);
                                    if (retVal == R_BREAK)
                                    {
                                        tkparse.curPos = posEndForPart + 1;
                                        break;
                                    }
									if (retVal == R_ERROR)
										return retVal;
									if (isReturnFlagFunction(funcName))
										return R_CONTINUE;
                                }
                                else
                                {
                                    tkparse.curPos = posEndForPart + 1;
                                    break;
                                }
                                // Un peu crade, je le conçoit, exécution du code d'increment de la boucle FOR
                                tok = (token)CurrTokenBuffer.TokenList[endArg];
                                tok.typ = Scanner.E_SEMICOLON;
                                privExecuteTokensPart(level + 1, funcName, GVAR, LVAR, true, false, startArg, endArg);
                                tok.typ = Scanner.E_PAR_CLOSE;
                            }
                        }
                        #endregion
                        break;
                    case Scanner.E_FOREACH:
                        #region FOREACH...ENDFOREACH
                        {

                            tok = tkparse.readNextToken();
                            if (tok.typ != Scanner.E_VAR)
                            {
                                errorMsg = "'Var' needed";
                                goto endFunction;
                            }
                            string destVar = tok.value;
                            tok = tkparse.readNextToken();
                            if (tok.typ != Scanner.E_IN)
                            {
                                errorMsg = "'Token IN' needed";
                                goto endFunction;
                            }
                            tok = tkparse.readNextToken();
                            if (tok.typ != Scanner.E_VAR)
                            {
                                errorMsg = "'Var' needed";
                                goto endFunction;
                            }
                            string origVar = tok.value;
                            tok = tkparse.readNextToken();
                            if (tok.typ != Scanner.E_PAR_CLOSE)
                            {
                                errorMsg = "')' needed";
                                goto endFunction;
                            }
                            if (isReturnFlagFunction(funcName))
                                return R_CONTINUE;
                            int posStartForPart = tkparse.curPos + 1;
                            int posEndForPart = 0;
                            int nbFor = 0;
                            while (true)
                            {
                                tok = tkparse.readNextToken();
                                if (tok.typ == Scanner.E_END)
                                {
                                    errorMsg = "'FOREACH' not terminated";
                                    goto endFunction;
                                }
                                if (tok.typ == Scanner.E_FOREACH)
                                    nbFor++;
                                if (nbFor == 0 && tok.typ == Scanner.E_ENDFOREACH)
                                {
                                    posEndForPart = tkparse.curPos - 1;
                                    break;
                                }
                                if (tok.typ == Scanner.E_ENDFOREACH)
                                    nbFor--;
                            }
                            SetOfVars.dumpElem[] dumpList;
                            if (GVAR.globalExist(origVar))
                                dumpList = GVAR.listDump(origVar.Substring(1));
                            else
                                dumpList = LVAR.listDump(origVar.Substring(1));
                            int idxDump = 0;
                            while (true)
                            {
                                if (idxDump >= dumpList.Length)
                                {
                                    tkparse.curPos = posEndForPart + 1;
                                    break;
                                }
                                string key = dumpList[idxDump].ids;

                                unionVal newVal = new unionVal(dumpList[idxDump].val);
                                if (dumpList[idxDump].val.CallBackGet != null)
                                    newVal = dumpList[idxDump].val.CallBackGet(dumpList[idxDump].id, dumpList[idxDump].val.CallBackArg);
                                newVal.CallBackSet = null;
                                newVal.CallBackGet = null;
                                newVal.CallBackArg = "";

                                if (GVAR.globalExist(destVar))
                                {
                                    GVAR.Set(destVar.Substring(1) + ",\"key\"", new unionVal( 0,"\"" + key + "\"",typVal.str ));
                                    GVAR.Set(destVar.Substring(1) + ",\"value\"", newVal);
                                }
                                else
                                {
                                    LVAR.Set(destVar.Substring(1) + ",\"key\"", new unionVal(0, "\"" + key + "\"", typVal.str));
                                    LVAR.Set(destVar.Substring(1) + ",\"value\"", newVal);
                                }

                                if (isReturnFlagFunction(funcName))
                                    return R_CONTINUE;

                                retVal = privExecuteTokensPart(level + 1, funcName, GVAR, LVAR, true, false, posStartForPart, posEndForPart);
                                if (retVal == R_BREAK)
                                {
                                     tkparse.curPos = posEndForPart + 1;
                                     break;
                                }
								if (retVal == R_ERROR)
									return retVal;
								if (isReturnFlagFunction(funcName))
									return R_CONTINUE;
                                idxDump++;
                            }
                        }
                        #endregion
                        break;
                    case Scanner.E_BREAK:
						#region BREAK
						if (!breakable)
                        {
                            errorMsg = "BREAK not attemped";
                            goto endFunction;
                        }
                        return R_BREAK;
						#endregion
					case Scanner.E_END:
                        if (level == 0) // Push 0 arg for return value
                            stk.Push(0);
                        return R_CONTINUE;
                    default:
                        errorMsg = "Token not catched";
                        goto endFunction;
                }

            }
        endFunction:
            errorMsg = "Syntax error in cfg file \"" + tok.fileName + "\" at line #" + tok.lineNumber  +"\n\t" + errorMsg + "" + "\n\tFunction '" + funcName + "' script aborted";
            myDebug.WriteLine("err",errorMsg);
            return R_ERROR;
        }
        void retrieveArg(TokenParse tkparse, bool breakFunc, ref char breakCar, ref int start, ref int end)
        {

            int par = 0;
            int brack = 0;

			start = tkparse.curPos;

			token tok = tkparse.readCurrToken();
            while (true)
            {

                if (tok.typ == Scanner.E_PAR_OPEN)
                {
                    par++;
					goto readNext;
                }
                if (tok.typ == Scanner.E_PAR_CLOSE && par > 0)
                {
                    par--;
					goto readNext;
				}
                if (tok.typ == Scanner.E_BRACKET_OPEN)
                {
                    brack++;
					goto readNext;
                }
                if (tok.typ == Scanner.E_BRACKET_CLOSE && brack > 0)
                {
                    brack--;
					goto readNext;
				}
                if (tok.typ == Scanner.E_SEMICOLON)
                {
                    breakCar = ';';
                    break;
                }
                if (tok.typ == Scanner.E_DOUBLEDOT)
                {
                    breakCar = ':';
                    break;
                }
                if (tok.typ == Scanner.E_END)
                {
                    breakCar = '!';
                    break;
                }
                if (( tok.typ == Scanner.E_COLON || tok.typ == Scanner.E_PAR_CLOSE ) && par == 0 && brack == 0 && breakFunc == true)
                {
                    if (tok.typ == Scanner.E_COLON)
                        breakCar = ',';
                    else
                        breakCar = ')';
                    break;
                }
			readNext:
				tok = tkparse.readNextToken();
            }
            end = tkparse.curPos - 1;
            return;

        }
        string retrieveEvalVar(string funcName, TokenParse tkparse, TokenBuffer currTokenBuffer, SetOfVars GVAR, SetOfVars LVAR, bool breakFunc, ref char breakCar)
        {
            System.Collections.ArrayList tokens = new System.Collections.ArrayList();

            int startPos = 0;
            int endPos = 0;

            retrieveArg(tkparse, breakFunc, ref breakCar, ref startPos, ref endPos);
            try
            {
				Parseur p = null;
				if ((currTokenBuffer.TokenList[tkparse.curPos] as token).parsed == null)
				{
					(currTokenBuffer.TokenList[tkparse.curPos] as token).parsed = new Parseur(currTokenBuffer, startPos, endPos);
//					p = (currTokenBuffer.TokenList[tkparse.curPos] as token).parsed;
//Console.WriteLine(p.ToRPN());
			}
				else
				{
//					p = (currTokenBuffer.TokenList[tkparse.curPos] as token).parsed;
//					Console.WriteLine(p.ToRPN());
				}
				p = (currTokenBuffer.TokenList[tkparse.curPos] as token).parsed;
//Console.WriteLine(p.ToRPN());

				Parseur.executeParsedFunction BC = new Parseur.executeParsedFunction(parseBackcall);
                retFlag = false;
                unionVal retVal = p.getval(GVAR, LVAR, BC);

				if (retFlag)
				{
					setReturnFlagFunction(funcName, true);
					retFlag = false;
				}
				if (retVal.typVal == typVal.num)
					return retVal.fval.ToString();
				else
					return retVal.sval;
			}
			catch (ParseException e)
			{
				throw (new GLScriptException(e.Message));
			}
			catch
			{
				throw;
			}
	    }
        void parseBackcall(SetOfVars GVAR,SetOfVars LVAR, unionVal val, ArrayList args)
        {
            string tmpStr = "";
            int nbArgs = args.Count;
            switch (val.nameFunction.ToLower())
            {

				case "round":
					{
						if (nbArgs != 2)
							throw new GLScriptException("Incorrect args number in function " + val.nameFunction);
						tmpStr = (string)args[0];
						try
						{
							val.fval = float.Parse(tmpStr);
							val.typVal = typVal.num;
						}
						catch
						{
							throw new GLScriptException("Can't convert string \"" + (string)args[0] + "\" to number " + val.nameFunction);
						}
						int rnd = 0;
						try
						{
							rnd = getInt((string)args[1]);
						}
						catch
						{
							throw new GLScriptException("Need int in 2nd parameter of " + val.nameFunction);
						}
						val.fval = (float)Math.Round( val.fval, rnd );
						val.typVal = typVal.num;
						return;
					}
				case "strformat":
					if (nbArgs < 1)
                        throw new GLScriptException("Incorrect args number in function " + val.nameFunction);
                    tmpStr = (string)args[0];
                    args.RemoveAt(0);
// Try to convert to numerical value args
					for (int i = 0; i < args.Count; i++)
					{
						try
						{
							args[i] = float.Parse((string)args[i]);
						}
						catch
						{
						}
					}
					val.sval = "\"" + string.Format(tmpStr, args.ToArray()) + "\"";
					val.typVal = typVal.str;
                    return;
                case "tostring":
                    if (nbArgs != 1)
                        throw new GLScriptException("Incorrect args number in function " + val.nameFunction);
                    tmpStr = (string)args[0];
                    val.sval = "\"" + tmpStr + "\"";
                    val.typVal = typVal.str;
                    return;
				case "tonum":
					if (nbArgs != 1)
						throw new GLScriptException("Incorrect args number in function " + val.nameFunction);
					tmpStr = (string)args[0];
					try
					{
						val.fval = float.Parse(tmpStr);
						val.typVal = typVal.num;
					}
					catch
					{
						throw new GLScriptException("Can't convert string \"" + (string)args[0] + "\" to number " + val.nameFunction);
					}
					return;
				case "not":
					if (nbArgs != 1)
						throw new GLScriptException("Incorrect args number in function " + val.nameFunction);
					tmpStr = (string)args[0];
					try
					{
						val.fval = float.Parse(tmpStr);
						if (val.fval > 0)
							val.fval = 0;
						else
							val.fval = 1;
						val.typVal = typVal.num;
					}
					catch
					{
						throw new GLScriptException("Can't convert string \"" + (string)args[0] + "\" to number " + val.nameFunction);
					}
					return;
				case "toupper":
                    if (nbArgs != 1)
                        throw new GLScriptException("Incorrect args number in function " + val.nameFunction);
                    tmpStr = (string)args[0];
                    val.typVal = typVal.str;
                    val.sval = "\"" + tmpStr.ToUpper() + "\"";
                    return;
                case "tolower":
                    if (nbArgs != 1)
                        throw new GLScriptException("Incorrect args number in function " + val.nameFunction);
                    tmpStr = (string)args[0];
                    val.typVal = typVal.str;
                    val.sval = "\"" + tmpStr.ToLower() + "\"";
                    return;
                case "trim":
                    if (nbArgs != 1)
                        throw new GLScriptException("Incorrect args number in function " + val.nameFunction);
                    tmpStr = (string)args[0];
                    val.typVal = typVal.str;
                    val.sval = "\"" + tmpStr.Trim() + "\"";
                    return;
                case "indexof":
                    if (nbArgs != 2)
                        throw new GLScriptException("Incorrect args number in function " + val.nameFunction);
                    tmpStr = (string)args[0];
                    string findString = (string)args[1];
                    val.typVal = typVal.num;
                    val.fval = tmpStr.IndexOf(findString);
                    return;
                case "splittoarray":
                    {
                        if (nbArgs != 2)
                            throw new GLScriptException("Incorrect args number in function " + val.nameFunction);
                        string tmp = (string)args[1];
                        char splitChar = tmp[0];
                        string[] splited = (args[0] as string).Split(splitChar);
                        if (splited.Length == 0)
                        {
                            val.typVal = typVal.str;
                            val.sval = "\"\"";
                        }
                        else
                        {
                            val.typVal = typVal.setOfVar;
                            val.setVars = new SetOfVars( );
                            for (int i = 0; i < splited.Length; i++)
                            {
                                val.setVars.Set(i.ToString(), new unionVal(0, "\"" + splited[i] + "\"" , typVal.str));
                            }
                        }
                    }
                    return;

                case "split":
                    {
                        int pos = 0;
                        if (nbArgs != 3)
                            throw new GLScriptException("Incorrect args number in function " + val.nameFunction);
                        string tmp = (string)args[1];
                        char splitChar = tmp[0];
                        string[] splited = (args[0] as string).Split(splitChar);
                        try
                        {
                            pos = getInt((string)args[2]);
                        }
                        catch
                        {
                            throw new GLScriptException("Need int in 3rd parameter of split");
                        }
                        if (pos > splited.Length)
                        {
                            val.typVal = typVal.str;
                            val.sval = "\"\"";
                        }
                        else
                        {
                            val.typVal = typVal.str;
                            val.sval = "\"" + splited[pos] + "\"";
                        }
                    }
                    return;
                case "unset":
                    if (nbArgs != 1)
                        throw new GLScriptException("Incorrect args number in function " + val.nameFunction);
                    string nameVar = (val.ArgsVals[0] as Parseur).val.nameVar;
                    GVAR.unSet(nameVar);
                    LVAR.unSet(nameVar);

                    return;
                case "return":
                    retFlag = true;
                    if (args.Count > 1)
                        throw new GLScriptException("You can return only one value... ");
                    if (args.Count == 1)
                    {
                        stk.Push(args[0]);
                        stk.Push(1);
                    }
                    else
                        stk.Push(0);
                    return;
                case "write":
                    if (nbArgs > 1)
                        throw new GLScriptException("Incorrect args number in function " + val.nameFunction);
                    if (nbArgs == 1)
                    {
                        Console.Write((string)args[0]);
                    }
                    else
                        Console.Write("");
                    return;
                case "writeline":
                    if (nbArgs > 1)
                        throw new GLScriptException("Incorrect args number in function " + val.nameFunction);
                    if (nbArgs == 1)
                    {
                        Console.WriteLine((string)args[0]);
                    }
                    else
                        Console.WriteLine("");
                    return;
                case "strlen":
                    if (nbArgs != 1)
                        throw new GLScriptException("Incorrect args number in function " + val.nameFunction);

                    val.typVal = typVal.num;
                    val.fval = (args[0] as string).ToString().Length;
                    return;
                case "substr":
                    if (nbArgs < 2 || nbArgs > 3)
                        throw new GLScriptException("Incorrect args number in function " + val.nameFunction);
                    val.typVal = typVal.str;
                    val.fval = 0;
                    int start = 0;
                    int nb = 0;
                    string str = (string)args[0];
                    try
                    {
                        start = getInt((string)args[1]);
                    }
                    catch
                    {
                        throw new GLScriptException("Need int in first parameter of " + val.nameFunction);
                    }
                    if (nbArgs == 3)
                    {

                        try
                        {
                            nb = getInt((string)args[2]);
                        }
                        catch
                        {
                            throw new GLScriptException("Need int in second parameter of subStr");
                        }
                        if (nb > str.Length - start)
                            nb = str.Length - start;
                        val.sval = "\"" + str.Substring(start, nb) + "\"";
                    }
                    else
                        val.sval = "\"" + str.Substring(start) + "\"";
                    return;

                case "dumpvar":
                    {
                        string idVar = (val.ArgsVals[0] as Parseur).val.nameVar;
                        if (GVAR.globalExist(idVar))
                            GVAR.Dump(idVar);
                        else
                            LVAR.Dump(idVar);
                    }
                    return;
                case "arraycount":
                    {
                        string idVar = (val.ArgsVals[0] as Parseur).val.nameVar;
                        if (GVAR.globalExist(idVar))
                        {
                            val.typVal = typVal.num;
                            val.fval = GVAR.ArrayCount(idVar);
                        }
                        else
                        {
                            val.typVal = typVal.num;
                            val.fval = LVAR.ArrayCount(idVar);
                        }
                    }
                    return;
                case "randomnum":
                    {
                        int startNum = 0;
                        int endNum = 1;
                        if( nbArgs >= 3 || nbArgs == 1 )
                            throw new GLScriptException("Incorrect args number in function " + val.nameFunction);
                        if (nbArgs == 2)
                        {
                            startNum = getInt((string)args[0]);
                            endNum = getInt((string)args[1]);
                        }
                        Random random = new Random();
                        val.typVal = typVal.num;

                        val.fval = (float)(random.NextDouble() * ( endNum - startNum ) + startNum);
                    }
                    return;
                case "isregexmatch":
                    {
                        if (nbArgs < 2 )
                            throw new GLScriptException("Incorrect args number in function " + val.nameFunction);

                        Regex regExp = new Regex((string)args[0]);
                        val.typVal = typVal.num;
                        if (regExp.IsMatch((string)args[1]))
                            val.fval = 1;
                        else
                            val.fval = 0;
                    }
                    return;


            }
            if (existFunction(val.nameFunction.ToLower()))
            {
                privExecuteFunction(val.nameFunction.ToLower(), (string[])args.ToArray(typeof(string)));
				if (stk.Count != 0)
				{
					int nbRetStak = (int)stk.Pop();
					if (nbRetStak != 1)
						val.sval = "\"\"";
					else
						val.sval = "\"" + (string)stk.Pop() + "\"";
				}
				else
					val.sval = "\"\"";
				val.typVal = typVal.str;
				return;
            }
            if (listOfCallBackFunctions.Contains(val.nameFunction.ToLower()))
            {
                callBackFunction myCBF = (callBackFunction)listOfCallBackFunctions[val.nameFunction.ToLower()];
                myCBF(val, args);
                return;
            }
            throw new GLScriptException("Function not defined..." + val.nameFunction);
        }
        private int getInt(string str)
        {
            int ret = 0;
            try
            {
                ret = int.Parse(str);
            }
            catch
            {
                throw new GLScriptException("Int value needed...");
            }
            return ret;
        }

    }
}
