//  
//  SetOfVars.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;

namespace GLScript
{
    public class SetOfVarsException : Exception
    {
        public SetOfVarsException(string message) : base(message) { }
        public SetOfVarsException(string message, Exception innerException) : base(message, innerException) { }
    }

    public class SetOfVars
    {
        public class dumpElem
        {
            public string ids;
            public string id;
            public unionVal val;
            public dumpElem(string pids,string pid,unionVal pval)
            {
                ids = pids;
                id = pid;
                val = new unionVal(pval );
            }
        }
        public System.Collections.Hashtable globVars = new System.Collections.Hashtable();

        public bool globalExist(string id)
        {
			string[] ids;
            if( id != null )
				ids = id.Split(',');
			else
				throw new ParseException("Wrong NULL index in an array...");
            return (globVars.Contains(ids[0]));
        }

        private System.Collections.Hashtable vars = new System.Collections.Hashtable();
        public enum typVar
        {
            val,
            function,
            array,
        }
        public class var
        {
            public typVar typ;
            public object obj;
            public var(typVar ptyp, object pobj)
            {
                this.typ = ptyp;
                this.obj = pobj;
            }
        }
        public SetOfVars( )
        {
        }
        public void raz()
        {
            vars.Clear();
        }
        public void unSet(string id)
        {
            if( vars.Contains( id ) )
                vars.Remove(id);
        }
        public void Set(string id, unionVal obj)
        {
            dumpElem[] dumpList = null;
			string[] arIds = id.Split(',');
            try
            {
				Set2(arIds, obj, dumpList, 0);
            }
            catch (SetOfVarsException)
            {
                
            }
            catch (Exception ex)
            {
                throw( ex );
            }
        }
		void Set2(string[] ids, unionVal obj, dumpElem[] dumpList, int idx )
        {
			string id = ids[idx];
// If is sub index of an array, create new set of Var
			if (idx != ids.Length - 1)
            {
                if (!vars.Contains(id))
                    vars[id] = new var(typVar.array, new SetOfVars(  ));

                if( ( vars[id] as var).typ == typVar.val )
                    throw new ParseException("You can't change the dimension of an array... use unSet( $" + id + " ); before");
                ((vars[id] as var).obj as SetOfVars).Set2(ids, obj, dumpList,idx+1);
                return;
            }
            typVar tvar = typVar.val;
            if( obj.CallBackGet != null || obj.CallBackSet != null )
                tvar = typVar.function;
            if ((obj as unionVal).typVal == typVal.setOfVar) // if it's a setOfVar, it's an array to try to attach, not set a value
                tvar = typVar.array;

// You can set a value to a var if this var is already an array and not a final value
// Except if you want to attach a setOfVar
            if (vars.Contains(id) && (vars[id] as var).typ == typVar.array && (obj as unionVal).typVal != typVal.setOfVar)
                throw new ParseException("You can't change the dimension of an array... use unSet( $" + id + " ); before");

            if ((obj as unionVal).typVal == typVal.setOfVar)
                vars[id] = new var(tvar, obj.setVars);  // if a set of var, attach it to the vars
            else if( vars.Contains( id ) && (vars[id] as var).typ == typVar.function ){ 
                unionVal oldVal = (unionVal)(vars[id] as var).obj;
                if ( oldVal.CallBackSet != null)
                {
                    oldVal.CallBackSet(id, obj, oldVal.CallBackArg); // Set Value with callback
                    return;
                }
            }
            else
                vars[id] = new var(tvar, new unionVal(obj));
        }
        public unionVal Get(string id)
        {
            string[] ids = id.Split(',');
            if (ids.Length > 1)
            {
                if (!vars.Contains(ids[0]))
                    return new unionVal(0, "\"\"", typVal.str);
                else if ((vars[ids[0]] as var).typ == typVar.array)
                    return ((vars[ids[0]] as var).obj as SetOfVars).Get(string.Join(",", ids, 1, ids.Length - 1));
                else
                    return new unionVal(0, "\"Wrong dimension Array\"", typVal.str);
            }
            if (vars.ContainsKey(id))
            {
                if ((vars[ids[0]] as var).typ != typVar.array)
                {
                    unionVal uval = (unionVal)(vars[id] as SetOfVars.var).obj;
                    if (uval.CallBackGet == null)
                        return (uval);
                    else
                        return uval.CallBackGet(id, uval.CallBackArg);
                }
                else
                    return new unionVal(0, "\"Array\"", typVal.str);
            }
            else
                return new unionVal(0, "\"\"", typVal.str);

        }
        public void duplicateVar( string idDest, string idOrig,dumpElem[] dumpList){

            if (idOrig == idDest)
                return;

            int dimIdOrig = idOrig.Split(',').Length - 1;

            string id = "";
            string idTmp = idDest.Split(',')[0];
            unSet( idTmp );

            for (int i = 0; i < dumpList.Length; i++)
            {
                string[] ids = dumpList[i].ids.Split(',');
                if (ids.Length == 1)
                    id = idDest;
                else
                    id = idDest + "," + string.Join(",", ids, 1, ids.Length - 1);
                unionVal newVal = new unionVal(dumpList[i].val);
                if (dumpList[i].val.CallBackGet != null)
                    newVal = dumpList[i].val.CallBackGet(dumpList[i].id, dumpList[i].val.CallBackArg);
                newVal.CallBackSet = null;
                newVal.CallBackGet = null;
                newVal.CallBackArg = "";
                Set(id, newVal);
            }
        }
        public int ArrayCount(string id)
        {
            dumpElem[] dumpList;
            dumpList = listDump(id);
            if (dumpList.Length == 1 && dumpList[0].ids == id) // if var is not an array but a std var
                return 0;
            else
                return dumpList.Length;
        }

        public string GetVal(string id, int idx)
        {
            dumpElem[] dumpList;
            dumpList = listDump(id);
            string GetValue;

            if (dumpList[idx].val.typVal == typVal.str)
                GetValue = dumpList[idx].val.sval;
            else
                GetValue = dumpList[idx].val.fval.ToString();

            return GetValue;
        }
        

        public void Dump(string id)
        {
            //Get all Array
            dumpElem[] dumpList; 

            dumpList = listDump(id);
			Console.WriteLine("---- Dump of " + id + " ----");
            for (int i = 0; i < dumpList.Length; i++)
            {
                string ids = dumpList[i].ids;
                int pos = ids.IndexOf(",");

                //Detect Multidimensional Arrays $Array[0,0] example
                // ',' detection
                if (pos != -1) 
                    ids = ids.Substring(0, pos) + "[" + ids.Substring(pos+1) + "]";

                Console.Write("$" + ids + " = ");
                if (dumpList[i].val.CallBackGet != null)
                {
                    unionVal uval = dumpList[i].val.CallBackGet( dumpList[i].id, dumpList[i].val.CallBackArg);
                    Console.WriteLine(uval.sval);
                }
                else if (dumpList[i].val.typVal == typVal.str)
                    Console.WriteLine(dumpList[i].val.sval);
                else
                    Console.WriteLine(dumpList[i].val.fval);
            }
			Console.WriteLine("---- End Dump of " + id + " ----\n");
		}
        public dumpElem[] listDump(string idOrig)
        {
            ArrayList dumpList = new ArrayList();
            string[] ids = idOrig.Split(',');
            string id = ids[0];

            if (vars.ContainsKey(id))
            {
                if ((vars[id] as var).typ != typVar.array)
                {
                    unionVal value = (unionVal)(vars[id] as var).obj;
                    dumpList.Add(new dumpElem(id, id, value));
                }
                else
                {
                    DumpArray(id,id,(SetOfVars)(vars[id] as var).obj, idOrig, dumpList );
                }
            }
            return (dumpElem[])dumpList.ToArray(typeof(dumpElem));

        }
        public void DumpArray(string id,string idBase, SetOfVars obj,string idOrig, ArrayList  dumpList )
        {
            foreach (DictionaryEntry d in obj.vars )
            {
                var currVar = (var)d.Value;
                if (currVar.typ != typVar.array)
                {
                    unionVal value = (unionVal)currVar.obj;
                    string ids = id + "," + d.Key;
                    if (ids.IndexOf(idOrig) == 0)
                    {
                        ids = idBase + ids.Substring(idOrig.Length);
                        dumpList.Add(new dumpElem(ids, (string)d.Key, value));
                    }
                }
                else
                {
                    DumpArray(id + "," + d.Key,idBase,(SetOfVars)currVar.obj,idOrig, dumpList);
                }
            }
        }

 
    }

}
