using System;
using System.Collections;
using System.IO;

namespace Liss
{
	public class SymbolTable
	{
		private int stat_identation;
		
		private string strSubstArgs = "||*SUBSTITUTE*||";
		
		// saves the value of the line command option '-caduceus' 
		private bool caduceus;
		
		// To save the comments from the original Liss code that we want to keep in the target C code
		private ArrayList comments;
		
		// To save the global variable that we want to pass as an argument in the functions
		private string actualFun;
		private ArrayList argDeclarations;


		// Local variables on functions
		public int localF = 0;
		// arguments on a variable
		public int local = -1;
        
		// 
		public int offset = 0;

		//
		public VM_Code genCode = new VM_Code();

		// Initialize the identifiers Table
		public ArrayList Table = new ArrayList();

		// To save the information of an array associated at a initialization of variable
		public ArrayList list = new ArrayList();
		
		// Save the dimensions of an array
		public ArrayList dimensions = new ArrayList();

		// Save the types of parameters declared in a subprogram
		public ArrayList dimensions2 = new ArrayList();

		// Save the array multi/uni - dimensional initialization
		public ArrayList initArray = new ArrayList();

		// Save the line of one array 
		public ArrayList lineArray = new ArrayList();

		// Save the indexes to calculate later the step in array
		public ArrayList indexes = new ArrayList();

		// Save the open ([) braces to control if the array is correctly initialized
		public ArrayList braces = new ArrayList();

		// To the case of a vector initialitazion
		public ArrayList oneDim = new ArrayList();

		// To save the arguments of a subprogram and latter update his entry
		public ArrayList args = new ArrayList();

		// Pass the parser as argument
		Parser parser;

		

		// Default constructor
		public SymbolTable()
		{
			this.stat_identation = 0;
			this.comments = new ArrayList();
			this.actualFun = "";
			this.argDeclarations = new ArrayList();
		}

		// Other Constructor
        public SymbolTable(Parser parser1, bool caduceus1)
		{
			this.stat_identation = 0;
			this.comments = new ArrayList();
			this.actualFun = "";
			this.argDeclarations = new ArrayList();
			this.parser = parser1;
			this.caduceus = caduceus1;
		}

		public string genCaduceusCom()
		{
			return 
				// this.PrintIdent() + "/* THIS FILE WAS GENERATED TO BE USED IN CADUCEUS\n" +
				this.PrintIdent() + "/* THIS CODE LINES WILL NOT BE READ BY CADUCEUS\n";
		}
		
		// gets the value of caduceus option
		public bool checkCaduceus()
		{
			return this.caduceus;
		}

		// gets the actual global variables that must be passed as an argument
		public ArrayList getGlVar()
		{
			foreach (Entry e in Table)
				if (e.name == this.actualFun)
					return e.glVars;
					
			return new ArrayList();
		}

		// adds name1 to the actual global variables that must be passed as an argument
		public void addGlVar(string name1)
		{
			foreach (Entry e in Table)
				if (e.name == this.actualFun)
					e.glVars.Add(name1);
		}

		// gets the correct arguments declaration line for a function
		public string getArgDecl(string name1, string arg)
		{
			ArrayList newArgs = this.getGlVar();
			
			foreach (Entry e in Table)
				if (e.name == this.actualFun)
					newArgs = e.glVars;
					
			string argdecl = "";
			
			foreach (string var in newArgs)
			{
				int type = this.findType(var);
				string typedecl = "";
				
				switch(type)
				{
					// set
					case 4: typedecl = "set";
							break;
					// sequence
                	case 5: typedecl = "list";
							break;
					// default
					default: typedecl = "int";
							break;
				}
				argdecl += ", " + typedecl + " *" + var;
			}
			
			if (arg == "" && argdecl.StartsWith(", "))
				argdecl = argdecl.Remove(0, 2);
			
			return arg + argdecl;
		}
		
		// checks if var name1 already exists in the function
		public void CheckVar(string name1, int level1)
		{
			bool res = false;
			
			foreach (Entry e in Table)
				if ( e.name == name1 && (e.level == level1 || e.level == -level1 || e.classO == "subProgram") )
					res = true;

			if (!res)
				foreach (string s in this.getGlVar())
					if (s == name1)
						res = true;

			if (!res)
				this.addGlVar(name1);
		}
		
		// set the value of the string that contains the name of actual function
		public void SetActFun(string val)
		{
			this.actualFun = val;
		}
		
		// returns the string that contains the name of actual function
		public string GetActFun()
		{
			return this.actualFun;
		}
		
		// returns the correct variable name inside a function
		public string GetCorrectName(string name1)
		{
			foreach (string s in this.getGlVar())
				if (s == name1)
					if (this.findType(name1) == 3)
						return name1;
					else
						return "(*" + name1 + ")";
			
			return name1;
		}
		
		// returns the correct argument name inside a function
		public string GetCorrectArg(string name1, string args, int level)
		{
			ArrayList argName1 = new ArrayList();
			string decl = "";
			
			if (this.GetActFun() == name1)
			{
				this.argDeclarations.Add(args);
				return this.strSubstArgs + this.argDeclarations.Count + this.strSubstArgs;
			}
			else
			{
				foreach (Entry e in Table)
					if (e.name == name1)
						argName1 = e.glVars;
			
				foreach (string a in argName1)
				{
					this.CheckVar(a, level);
					if (this.findType(a) == 3)
						decl += ", " + this.GetCorrectName(a);
					else
						decl += ", &" + this.GetCorrectName(a);
				}
			
				if (args == "" && decl.StartsWith(", "))
					decl = decl.Remove(0, 2);
			}
			
			return args + decl;
		}
		
		// returns the correct argument name inside a function
		public string substArg(string sub, string name1, int level)
		{
			string tmp = "";
			int i = 1;
			
			if (this.argDeclarations.Count > 0 && name1 != this.actualFun)
			{
				foreach(string d in this.argDeclarations)
				{
					tmp = this.GetCorrectArg(name1, d, level);
					sub = sub.Replace(this.strSubstArgs + i + this.strSubstArgs,tmp);
					i++;
				}
				this.argDeclarations.Clear();
			}
			
			return sub;
		}
		
		// get current comments
		public string GetComments()
		{
			string res = "";
			ArrayList com = this.comments;
			
			foreach(string s in com)
			{
				if ( s.StartsWith("//") )
					res += this.PrintIdent() + s;
				else
				{
					string tmp = "";
					string[] sep = s.Split('\n');
					for (int i=0; i<sep.LongLength; i++)
						tmp += this.PrintIdent() + sep[i].Trim() + "\n";
					res += tmp;
				}
			}
			this.comments.Clear();

			return res;
		}
		
		// add a comment
		public void AddComments(string com)
		{
			if (com.StartsWith("--"))
			{
				com = com.Remove(0, 2);
				com = com.Insert(0, "//");
			}
			
			this.comments.Add(com);
		}
		
		// get current identation value
		public int GetCurIdent()
		{
			return this.stat_identation;
		}
		
		// set current identation value
		public void SetCurIdent(int n)
		{
			this.stat_identation = n;
		}
		
		// print the correct identation
		public string PrintIdent()
		{
			return genCode.PrintIdent(this.stat_identation);
		}
		
		// Increments identation
		public void IncIdent()
		{
			++(this.stat_identation);
		}
		
		// Decrements identation
		public void DecIdent()
		{
			--(this.stat_identation);
		}
		
		// Decrements identation n times
		public void DecIdent(int n)
		{
			while (n > 0)
			{
				--(this.stat_identation);
				n--;
			}
		}
		

		// 
		public void OpenScope()
		{
			this.localF = 0;
		}


		// Close the scope of a subprogram
		public void CloseScope(int level1)
		{
			for (int i = 0; i < Table.Count; i++)
			{
				Entry e = (Entry)Table[i];

				if ((e.level == level1 || e.level == -level1) && (e.classO != "subProgram"))
					if (e.next == null)
					{
						Table.RemoveAt(i);
						i--;
					}
					else if (e.classO != "subProgram")
					{
						e = e.next;
						Table[i] = e;
					}
			}
		}


		// Initialize the environment with four types : Integer, Boolean, Set, Sequence
		public void InicEnv()
		{
			// name, class, type, size, memAddress, initValues, dim, level, next (entry)
			Entry g1 = new Entry("Integer","Type","Atom",1,0,0,null,0,null);
			Entry g2 = new Entry("Boolean","Type","Atom",1,0,0,null,0,null);
			Entry g3 = new Entry("Set","Type","Set",1,0,0,null,0,null);
			Entry g4 = new Entry("Sequence","Type","Sequence",1,0,0,null,0,null);

			Table.Add(g1);
			Table.Add(g2);
			Table.Add(g3);
			Table.Add(g4);
		}



		// Verify if the types are compatible
		public void Expect(int type, int type1)
		{
			if (type != type1)
				parser.SemErr("Expected type was '" + this.giveName(type) + "'");
		}


		// Verify if the types are compatible
		public void ValidCall(string name, int level,int arg)
		{
			foreach (Entry e in Table)
				if (e.name.Equals(name))
				{
					if (! e.classO.Equals("subProgram"))
					{
						parser.SemErr("Bad call! '" + name + "' isn't a subprogram!");
						System.Environment.Exit(0);
					}
					else
					{
						if (e.dim.GetType().Name.Equals("ArrayList")) 
						{ 
							if (arg == 1 && ((ArrayList)e.dim).Count > 0 && name.Length == 0)
								parser.SemErr("Bad call! Missing arguments in '" + name + "!'");
						}
						else if ((! (this.getLevel(name) == (level+1))) && (! (this.getLevel(name) == level)))
						{
							parser.SemErr("Bad call! '" + name + "' couldn't called on this level!");
							System.Environment.Exit(0);
						}
					}
				}
		}


		// Lookup for a variable
		public string LookUp(int level1)
		{
			string str = "";

			foreach (Entry e in Table)
			{
				if (e.classO.Equals("var") && e.level == level1)
					str += e.asLocal(e.memAdress);
			}

			return str;
		}

		// Method for find a type of a variable 
		public int findType(string name1)
		{
			string str = "";

			foreach (Entry g in Table)
				if (g.name.Equals(name1))
					str = g.type;

			return this.giveType(str);
		}

		// Gets the size of the symbol Table at the moment
		public int getSizeTable()
		{
			return Table.Count;
		}

		// Returns the pre-defined size of a type
		public int getSizeType(int type)
		{
			string typeS = this.giveName(type);

			foreach(Entry g in Table)
			{
				if ( g.name.Equals(typeS))
					return g.size;
			}

			return 0;
		}

		// Returns the pre-defined size of a type
		public int getSizeType(string type)
		{
			foreach(Entry g in Table)
			{
				if ( g.name.Equals(type))
					return g.size;
			}

			return 0;
		}
		
		// ------------------- Returns the dimension of a array
		// It's used to print a array, to find out the max value
		public int findSize(string name1)
		{
			int dim = 0;

			foreach (Entry g in Table)
				if (g.name.Equals(name1))
					dim = g.size;

			return dim;
		}

		// Gets the level of a variable
		public int getLevel(string name1)
		{
			foreach (Entry g in Table)
				if (g.name.Equals(name1))
					return g.level;

			return 0;
		}

		// Returns the memory address of a variable
		public int getMemAdress(string name1)
		{
		 foreach (Entry g in Table)
			if (g.name.Equals(name1))
				return g.memAdress;

			return 0;
		}


		// Returns the inic of memory address
		public int getMemAdress()
		{
			int last = Table.Count - 1;

			for (int k = last; k >= 0; k--)
			{
				Entry g = (Entry) Table[k];
				if (g.memAdress != 0) 
				{
					return g.memAdress;
				}
			}
			return 0;
		}

		// Returns the inic of memory address
		public object getValue(string name1)
		{
			foreach(Entry g in Table)
				if (g.name.Equals(name1))
					return g.initValues;
			return null;
		}


		// Return the class of a entry 
		public string getClass(string name1)
		{
			string str = "";

			foreach(Entry e in Table)
				if (e.name.Equals(name1))
					return e.classO;

			foreach(Entry e in Table)
				if  (name1.StartsWith(e.name))
						str = e.classO;

			return str;
		}


		// Verify if the types in a subprogram match with the definition
		public void Compatible(int nParam, int type, string name1)
		{
			ArrayList aux = this.returnArgs(name1);

			if (aux!= null)
			{
				string aux2 = (string)aux[nParam];
				foreach (Entry e in Table)
					if (e.name.Equals(aux2))
						if (this.giveType(aux2) != type)
							parser.SemErr("Parameter type mismatch!");
			}
		}

		//
		public object defaultObj(string type)
		{
			foreach(Entry g in Table)
				if ( g.name.Equals(type))
					return g.initValues;

			return null;
		}


		// Return the array of dimensions, p.e. {4,5,2} - case of arrays
		public ArrayList returnDim(string name1)
		{
			foreach(Entry g in Table)
			{
				if (g.name.Equals(name1))
					return (ArrayList)g.dim;
			}

			return null;
		}


		// Return the array of types of a subprogram - case of subprogram (p.e. {integer,boolean,...}
		public ArrayList returnArgs(string name1)
		{
			foreach(Entry g in Table)
			{
				if (g.name.Equals(name1))
					return (ArrayList)g.numArgs;
			}

			return null;
		}


		// Return the number of arguments of a subprogram
		public int nParam(string name1)
		{
			ArrayList aux = this.returnArgs(name1);

			if (aux != null)
                return aux.Count;
			else
				return 0;
		}


		// Make a new line in the Table of identifiers
		public void newVar(string name,int level)
		{
			int last = this.getMemAdress();
			
			Entry g = new Entry(name,"var","integer",1,1 + last,0,null,level,null);
			Table.Add(g);
		}


		// Make a new line in the Table of identifiers
		public void AddEntryFun(string name,int size, int level, string typeStr, object obj)
		{
			Entry g = new Entry(name,"subProgram",typeStr,size,0,0,obj,level,null);
			g.numArgs = this.dimensions2;
			g.glVars = new ArrayList();
			Table.Add(g);
		}



		// Add a Entry to the Table of identifiers
		public void AddLineTable(int num,int size,object dim)
		{
			ArrayList init = new ArrayList();

			for (int i = 0; i < size; i++)
				init.Add(0);

			Entry g = new Entry("Array_" + num,"Type","Array",size,0,init,dim,0,null);
			Table.Add(g);
		}



		// Make a new line in the Table of identifiers
		public void AddEntry(string name1,int level)
		{
			
			for (int i=0; i < Table.Count; i++)
			{
				Entry e = (Entry)Table[i];

				if (e.name.Equals(name1) && e.level != level)
				{
					Entry p = new Entry();
					p.name = name1;
					p.classO = "var";
					p.type = "";
					p.size = 1;
					p.memAdress = localF;
					p.initValues = null;
					p.level = level;
					p.next = e;
					Table[i] = p;
					localF++;
					return;
				}
				else if (e.name.Equals(name1) && e.level == level)
					parser.SemErr("Variable " + name1 + " already defined!");
			}

			Entry g;

			if ( level == 0)
				g = new Entry(name1,"var","",1,0,0,null,level,null);
			else
			{
				g = new Entry(name1,"var","",1,localF,0,null,level,null);
				localF++;
			}

			Table.Add(g);
		}

		//
		public void AddEntry(string name1,int type, string typeStr, int level1,object obj)
		{
			int size1 = this.getSizeType(typeStr);
			ArrayList init = new ArrayList();

			if (type == 3)
				for (int i = 0; i < size1; i++)
					init.Add(0);

			for (int i=0; i < ((ArrayList)Table).Count; i++)
			{
				Entry e = (Entry)Table[i];

				if (e.name.Equals(name1) && e.level != level1)
				{
					Entry p = new Entry();
					p.name = name1;
					p.classO = "var";
					p.type = typeStr;
					p.size = size1;
					p.memAdress = local;
					if (type == 3 || type == 4)
						p.initValues = init;
					else p.initValues = 0;
					p.dim = obj;
					p.level = level1;
					p.next = e;
					Table[i] = p;
					local -= size1;
					return;
				}
				else if (e.name.Equals(name1) && e.level == level1)
					parser.SemErr("Variable " + name1 + " already defined!");
			}

			Entry g;

			if (type == 3 || type == 4)
                g = new Entry(name1,"var",typeStr,size1,local,init,obj,level1,null);
			else
				g = new Entry(name1,"var",typeStr,size1,local,0,obj,level1,null);

			Table.Add(g);
			local -= size1;

		}


		public void UpdateEntry(string name)
		{
			int mem = -1;

			for (int i = args.Count-1; i >= 0; i--)
			{
				string name1 = (string)args[i];
				foreach (Entry e in Table)
				{
					if (e.name == name1)
					{
						e.memAdress = -e.size;
						mem += e.memAdress;
					}
				}
			}
		}

		public void UpdateEntry(string name1,int type,object obj)
		{
			foreach (Entry line in Table)
				if (line.name.Equals(name1)) 
				{
						line.initValues = obj;
						line.type = this.giveName(type);
				}
		}

		public void UpdateEntry(string name1, string code)
		{
			foreach (Entry line in Table)
				if (line.name.Equals(name1)) 
						line.initCode = code;
		}

		public void UpdateEntry(int type, string typeStr, int init, int endT)
		{

			for (int i = init; i < endT; i++)
			{
				Entry e = (Entry) Table[i];
				int last = this.getMemAdress();
				int sizeT = this.getSizeType(typeStr);
				string typeValue = e.initValues.GetType().Name;
				if (! e.classO.Equals("Type") && ! e.classO.Equals("subProgram"))
				{
					e.type = typeStr;
					if (type != 4)
					{
						e.size = sizeT;
						if (e.level == 0)
							e.memAdress = last + sizeT;
					}

				
					switch(type)
					{
						case (3): if (! (typeValue.Equals("Int32")))
									  e.initValues = this.correctInit();
								  else
									  e.initValues = this.defaultObj(typeStr);

								  e.dim = this.dimensions;
								  break;
						case (4): break;
						case (5): 
								  break;
						default:  break;
					}

				}
			}
			foreach (Entry e in Table) 
			{
				if (e.type.Equals("") && e.classO.Equals("var"))
					e.type = typeStr;
				if (e.initValues == null)
					e.initValues = this.defaultObj(typeStr);
			}
		}


	
		// ------------------------------------------------------
		// 
		// ------------------------------------------------------

		// Dimensions
		public void initDimensions()
		{
			this.dimensions = new ArrayList();
		}

		// Add a number to array dimensions
		public void AddDimension(int num)
		{
			this.dimensions.Add(num);
		}

		// Return the array of dimensions
		public ArrayList returnDimensions()
		{
			return this.dimensions;
		}


		// ------------------------------------------------------
		// 
		// ------------------------------------------------------

		// Dimensions
		public void initDimensions2()
		{
			this.dimensions2 = new ArrayList();
		}

		// Add a number to array dimensions
		public void AddDimension2(string type)
		{
			this.dimensions2.Add(type);
		}

		// Return the array of dimensions
		public ArrayList returnDimensions2()
		{
			return this.dimensions2;
		}


		// Return the size of array
		public int sizeDimensions(string name1)
		{
			int size = 0;

			foreach (Entry e in Table)
				if (e.name.Equals(name1))
					if (e.numArgs != null)
						for (int i = 0; i < ((ArrayList)e.numArgs).Count; i++)
						{
							string aux = (string)((ArrayList)e.numArgs)[i];
							size += this.getSizeType(aux);
						}
			
			return size;
		}

		// ------------------------------------------------------
		//  Args
		// ------------------------------------------------------

		// Args
		public void initArgs()
		{
			args = new ArrayList();
		}

		//
		public void AddArg(string name)
		{
			args.Add(name);
		}

		// ------------------------------------------------------
		// 
		// ------------------------------------------------------

		// Dimensions
		public void newInitArray()
		{
			initArray = new ArrayList();
		}

		// Add a size to array dimensions
		public void AddArray(object obj)
		{
			initArray.Add(obj);
		}

		// Return the array of dimensions
		public ArrayList returnInitArray()
		{
			return this.initArray;
		}


		// ------------------------------------------------------
		// Braces
		// ------------------------------------------------------

		// Braces
		public void newBraces()
		{
			braces = new ArrayList();
		}

		// Add a n. of braces to array 'braces'
		public void AddNumBraces(object obj)
		{
            braces.Add((int)obj);
		}


		// validate if the braces are correct
		public bool validateBraces(object obj)
		{
			int max = ((ArrayList) obj).Count;

			foreach (int c in this.braces)
				if (c != max)
				{
					parser.SemErr("Array is not correctly initialized!");
					return false;
				}
			return true;
		}


		// ------------------------------------------------------
		// 
		// ------------------------------------------------------

		// Line
		public void newOneDim()
		{
			this.oneDim = new ArrayList();
		}

		// Add a line to array dimensions
		public void AddOneDim(object obj)
		{
			oneDim.Add(obj);
		}

		// Tests if the array is null
		// In this case means that it's a vector.
		public ArrayList isVector()
		{
			if (this.lineArray.Count == 0) 
				this.AddLine(this.oneDim);
            
			return this.lineArray;
		}

		// ------------------------------------------------------
		// 
		// ------------------------------------------------------

		// Line
		public void newLine()
		{
			lineArray = new ArrayList();
		}

		// Add a line to array dimensions
		public void AddLine(object obj)
		{
			lineArray.Add(obj);
		}
		
		// Return the array of dimensions
		public ArrayList returnLineArray()
		{
				return this.lineArray;
		}

		// ------------------------------------------------------
		// 
		// ------------------------------------------------------

		// Indexes
		public void initIndexes()
		{
			indexes = new ArrayList();
		}

		// Add a index to array indexes
		public void AddIndex(int ind)
		{
			indexes.Add(ind);
		}

		// Return the array of indexes
		public ArrayList returnIndexes()
		{
			return this.indexes;
		}


		public string Length(string name)
		{
			int type = this.findType(name);

			if (type == 3)
				return this.product(this.returnDim(name),0).ToString();
			else
				return genCode.LengthSequence(name);
		}

		// Correct initialization
		public ArrayList correctInit()
		{
			ArrayList aux = new ArrayList();
			
			int count = this.dimensions.Count - 1 ; 
			int max = (int) this.dimensions[count];
			int min = (int) this.dimensions[0];
			int prod = product(this.dimensions,1);
			int mark = 0;
		
			for (int i = 0; i < lineArray.Count; i++)
			{
				ArrayList aux2 = (ArrayList)lineArray[i];

				for (int j = 0; j < aux2.Count; j++)
				{
					ArrayList aux3 = (ArrayList)aux2[j];
					for (int k = 0; k < aux3.Count; k++)
					{
						aux.Add((int)aux3[k]);
						mark++;
					}
					for (int k = aux3.Count; k < max; k++)
					{
						mark++;
						aux.Add(0);
					}
				}
                

				for (int j = mark; j < prod ; j++)
				{
					aux.Add(0);
				}
				mark = 0;

			}
			if (count != 0)
				for (int i = lineArray.Count; i < min; i++)
					for (int j = 0; j < prod; j++)
						aux.Add(0);
	
			return aux;
		}

		// Correct indexes
		public void validateIndex(string name,int count)
		{
			ArrayList dim = this.returnDim(name);

			if (dim != null)
                if ((count < dim.Count) || (count > dim.Count))
					parser.SemErr("Number of indexes on array '" + name + "' must be equal at max size of array!");
		}

		// Correct initialization
		public void validateArray(object dim1)
		{
			ArrayList dim = (ArrayList)dim1;
			int lines = (int)dim[0];
			int max = (int) dim[dim.Count-1];

			if (this.lineArray.Count > lines)
				parser.SemErr("Array isn't correctly initialized!");
			else
				for (int i = 0; i < this.lineArray.Count; i++)
				{
					ArrayList aux2 = (ArrayList)lineArray[i];
					if (aux2.Count > this.product(dim,0))
						parser.SemErr("Array isn't correctly initialized!");
					for (int j = 0; j < aux2.Count; j++)
					{
						ArrayList aux3 = (ArrayList)aux2[j];
						if (aux3.Count > max)
							parser.SemErr("Array isn't correctly initialized!");
					}
				}

			
		}


		// Calculate the step must be given to give the correct index on array
		public string calculateStep(string name, int num)
		{
			string str = "";
			ArrayList dim = this.returnDim(name);
			int prod = product(dim,num);
			
            str = genCode.Pushi(prod) +
				  genCode.Mul();

			return str;
		}

		// Calculate the product since the position i
		public int product(ArrayList dim, int i)
		{
			int prod = 1;

			if (i < dim.Count)
				for (int k = i; k < dim.Count; k++)
					prod = prod * ((int)dim[k]);
			
			return prod;
		}


		// print the recently variables declared

		public string printTable(int initT, int endT)
		{
			string str = "";

			for (int i = initT; i < endT; i++)
			{
				Entry e = (Entry)Table[i];
				if (e.classO.Equals("var"))
					str += e;
			}

			return str;
		}


		// // build a declaration line with the variables in the Table between position initT and endT
		public string declLine(int initT, int endT, int level)
		{
			ArrayList VarDecls = new ArrayList();
			Entry e = (Entry)Table[initT];

			for (int i = initT; i < endT; i++)
			{
				e = (Entry)Table[i];
				if (e.classO.Equals("var"))
					VarDecls.Add(e.name);
			}
			
			int type = this.giveType(e.type);
			
			return this.declLineArray(VarDecls, type, level);
		}
		
		
		// build a declaration line with the variables in ArrayList VarDecl
		public string declLineArray(ArrayList VarDecl, int type, int level)
		{
			string str = this.PrintIdent(), origStr = this.PrintIdent();
			string strDim = "";
			bool firstTime = true;
			ArrayList initVal;
            string arrayVar;
            string arraydecl = this.PrintIdent(), origArraydecl = this.PrintIdent();
            if (VarDecl.Count > 0)
            {
				switch(type)
				{
					// boolean
					case 1: str += "int ";
							break;
						
					// integer
					case 2: str += "int ";
							break;
						
					// array
					case 3: str += "int ";
							break;
						
					// set
					case 4: 	arraydecl += "sElem ";
       		         			str += "set ";
          		      			break;
				
					// sequence
	                case 5 : 	arraydecl += "elem ";
    	            			str += "list ";
        	        			break;
                			
            	    // subProgram
					case 6 : break;
				
					// unknown = 0
					default: 	str += "int ";
								break;
				}
				origArraydecl = arraydecl;

				foreach(string id in VarDecl)
					for(int ii=0; ii<Table.Count; ii++)
					{
						Entry e = (Entry) Table[ii];
						if(e.name == id && e.classO.Equals("var"))
							switch(type)
							{
								// boolean
								case 1: str += e.name + " = ";
										if (e.initCode!= "")
											str += e.initCode;
										else
											str += "0";
										str += ", ";
										break;
									
								// integer
								case 2: str += e.name + " = ";
										if (e.initCode!= "")
											str += e.initCode;
										else
											str += "0";
										str += ", ";
										break;
								
								// array
								case 3: if (firstTime)
										{
											ArrayList dim = this.returnDim(e.name);
							
											foreach (int dimI in dim)
												strDim += "[" + dimI + "]";
											firstTime = false;
										}
										str += e.name + strDim + " = ";
										if (e.initCode!= "")
											str += e.initCode;
										else
										{
											string tmp = "0";
											ArrayList dim = this.returnDim(e.name);
											for(int i=0; i<dim.Count; i++)
												tmp = "{" + tmp + "}";
											str += tmp;
										}
										str += ", ";
																
										break;
								
								// set
								case 4: 	if (e.initValues.GetType().Name.Equals("ArrayList"))
       			         					{
           			     						initVal = (ArrayList) e.initValues;
           		   		  						arrayVar = this.genVar();
               		 							this.newVar(arrayVar, level);
  		            	  						arraydecl += arrayVar + "[] = {";
       		         							str += e.name + " = newSet(" + arrayVar + ", " + initVal.Count + "), ";
           		     							foreach (string val in initVal)
               		 							{
		               								string val_el = val.Trim();
		               								arraydecl += "\"" + val_el + "\",";
	   	            							}
	   	            							
	   	            							arraydecl = arraydecl.Remove(arraydecl.LastIndexOf(','), 1) + "}, ";
       		         						}
            	    						else
												str += e.name + " = NULL, ";
                							break;
						
								// sequence
   		             			case 5 : 	if (e.initValues.GetType().Name.Equals("ArrayList"))
      		          						{
           		     							initVal = (ArrayList) e.initValues;
      		          							arrayVar = this.genVar();
	           		     						this.newVar(arrayVar, level);
   	            		 						arraydecl += arrayVar + "[] = {";
   		  		           						str += e.name + " = newInitList(" + arrayVar + ", " + initVal.Count + "), ";
	          		      						foreach (int val in initVal)
		            	   							arraydecl += val + ", ";
	   	            							arraydecl = arraydecl.Remove(arraydecl.LastIndexOf(','), 2) + "}, ";
       		         						}
            	    						else
	                							str += e.name + " = newList(), ";
    	            						break;
                			
        	        			// subProgram
								case 6 : break;
							
								// unknown = 0
								default: str += e.name + " = ";
										if (e.initCode!= null)
											str += e.initCode;
										else
											str += "0";
										str += ", ";
										break;
							}
					}
			}
			
			if ((type == 5) || (type == 4))
				if (arraydecl != origArraydecl)
				{
					arraydecl = arraydecl.Remove(arraydecl.LastIndexOf(','), 2) + ";\n";
					str = arraydecl + str;
				}
			
			if (str != origStr)
				str = str.Remove(str.LastIndexOf(','), 2);
			
			return str;

		}
		

		// Array list
		// ----------------------------------------------
		// For the type array
		// ----------------------------------------------


		// Initializa the array "list"
		public void initList()
		{
			list = new ArrayList();
		}

		// Add an element to a list
		public void addList(int num)
		{
			list.Add(num);
		}

		// Return the list 
		public ArrayList returnList()
		{
			return list;
		}


		// This method is responsable by verify if the type of variables declared are correct.
		// For example, in this declaration:
		// a, b = [1] -> int, must give an error of types
		public void errorsType(int type1, int init, int endT)
		{

			for (int i = init; i < endT; i++)
			{
				Entry line = (Entry)Table[i];
				int type2 = this.giveType(line.type);
				if (type2 != 0)
					if (type1 != type2)
						parser.SemErr("Error: the variable " + line.name + " is not correctly initiliazed!"); 
			}
		}


		// Give the name associated at an type 
		public string giveName(int expect)
		{
			string str="";

			switch(expect)
			{
				case 1: str = "Boolean"; break;
				case 2: str = "Integer"; break;
				case 3: str = "Array"; break;
				case 4: str = "Set"; break;
				case 5: str = "Sequence"; break;
				default: str = "Unknown"; break;
			}
			return str;
		}


		// Give the integer associated at an string type 
		public int giveType(string type)
		{
			int type1;
			if (type.StartsWith("Array"))
				type = "Array";

			switch(type)
			{
				case "Boolean": type1 = 1; break;
				case "Integer": type1 = 2; break;
				case "Array": type1 = 3; break;
				case "Set": type1 = 4; break;
                case "Sequence" : type1 = 5; break;
				case "subProgram" : type1 = 6; break;
				default: type1 = 0; break;
			}
			return type1;
		}


		


		// Return the operator
		public string Operator(string op)
		{
			string aux;

			switch(op)
			{
				case("MUL"): aux = " * "; break;
				case("DIV"): aux = " / "; break;
				case("ADD"): aux = " + "; break;
				case("SUB"): aux = " - "; break;
				case("OR"):  aux = " || "; break;
				case("AND"): aux = " && "; break;
				case("EQUAL"): aux = " == "; break;
				case("NOT"): aux = " != "; break;
				case("INF"): aux = " < "; break;
				case("INFEQ"): aux = " <= "; break;
				case("SUP"): aux = " > "; break;
				case("SUPEQ"): aux = " >= "; break;
				case("<<"): aux = " << "; break;
				case("++"): aux = " ++ "; break;
				case("**"): aux = " ** "; break;
				default: aux=""; break;
			}

			return aux;
		}


		// -----------------------------------------------------------
		//						OPER SETS
		// -----------------------------------------------------------

		



		// To return the index of a variable in the symbol table

		public int IndexOf(string name1)
		{
			for (int i = 3; i < Table.Count; i++)
			{
				Entry line = (Entry) Table[i];
				if (line.name.Equals(name1))
					if (line.level == 0)
						return line.memAdress-line.size;
					else
						return line.memAdress;
			}

			return 0;
		}


		// To return the index of a variable in the symbol table

		public int IndexVar(string name1)
		{
			for (int i = 3; i < Table.Count; i++)
			{
				Entry e = (Entry) Table[i];
				if (e.name.Equals(name1))
						return i;
			}

			return 0;
		}

		// ------------------ Print Expressions --- write/writeLn

		public string print(string name, int level)
		{
			int type = this.findType(name);

			string newname = this.GetCorrectName(name);

			if (name.StartsWith("tail"))
				type = 5;
			string str = "";
		
			//	0 = undef
			//	1 = boolean
			//	2 = integer
			//	3 = array
			//	4 = set
			//	5 = sequence
			switch(type)
			{
				case(3): ArrayList size = this.returnDim(name);
							str = this.printArray(newname, size, level);
							break;
				case(4): str = this.PrintIdent() + genCode.printSet(newname); 
						 break;
				case(1): str = this.PrintIdent() + genCode.printBoolean(newname);
					     break;
				case(5): str = this.PrintIdent() + genCode.printSequence(newname); 
						 break; 
				default: str = this.PrintIdent() + genCode.printDefaultValue(newname);
						 break;
			}
			return str;
		}

		// Verifies if a variable name already exists
		public bool varExists(string var)
		{
			foreach (Entry line in Table)
				if (line.name.Equals(var))
					return true;
			
			return false;
		}
		
		// Generates unique variables names
		public string genVar()
		{
			int index = 0;
			string var;
			do
			{
				var = "liss" + index;
				index++;
			}
			while (this.varExists(var));
			return var;
		}
		
		// print an array
		public string printArray(string name, ArrayList size, int level)
		{
			int num_identation = 0;
			string str = this.PrintIdent(), dimensions = "", condStr = "";
			str += "printf(\"[\");\n";
			foreach (int dim in size)
			{
				str += this.PrintIdent();
				string i = this.genVar();
				this.newVar(i, level);
				str += "for(" + i + "=0; " + i + "<" + dim + "; " + i + "++)\n";
				dimensions += "[" + i + "]";
				condStr += i + "==" + (dim-1) + " && ";
				this.IncIdent();
				num_identation++;
			}
			condStr = condStr.Remove(condStr.Length-4, 4);
			this.DecIdent();
			str += this.PrintIdent() + "{\n";
			this.IncIdent();
			str += this.PrintIdent() + "if (" + condStr + ")\n";
			this.IncIdent();
			str += this.PrintIdent() + "printf(\"%d\", " + name + dimensions + ");\n";
			this.DecIdent();
			str += this.PrintIdent() + "else\n";
			this.IncIdent();
			str += this.PrintIdent() + "printf(\"%d,\", " + name + dimensions + ");\n";
			this.DecIdent();
			this.DecIdent();
			str += this.PrintIdent() + "}\n";
			this.DecIdent(num_identation-1);
			str += this.PrintIdent() + "printf(\"]\");\n";
			
			return str;
		}
		
		public string ForEach(string index, string array, int level)
		{
			int num_identation = 0;
			ArrayList size = this.returnDim(array);
			string str = "", dimensions = "";

			foreach (int dim in size)
			{
				string i = this.genVar();
				this.newVar(i, level);
				str += this.PrintIdent() + "for(" + i + "=0; " + i + "<" + dim + "; " + i + "++)\n"
						+ this.GetComments();
				dimensions += "[" + i + "]";
				this.IncIdent();
				num_identation++;
			}
			this.DecIdent();
			str += this.PrintIdent() + "{\n";
			this.IncIdent();
			str += this.PrintIdent() + this.GetCorrectName(index) + " = " + this.GetCorrectName(array) + dimensions + ";\n";
			this.DecIdent();
			return str;
		}
		
		public bool CompareArrayList(ArrayList array1, ArrayList array2)
		{
			int i = 0;
			if (array1.Count != array2.Count)
				return false;
			else
			{
				foreach(int s1 in array1)
					if (!s1.Equals(array2[i]))
						return false;
					else
						i++;
				return true;
			}
		}
		
		// Assign array
		public string AssignArray(string name1, string name2, int level)
		{
			int num_identation = 0;
			string str = "", dimensions = "";
			
			// in the case of second argument be a subprogram
			string n2 = name2.Split('(')[0];
			
			ArrayList size1 = this.returnDim(name1);
			ArrayList size2 = this.returnDim(n2);
			
			if (this.CompareArrayList(size1, size2))
			{
				foreach (int dim in size1)
				{
					string i = this.genVar();
					this.newVar(i, level);
					str += this.PrintIdent() +
						"for(" + i + "=0; " + i + "<" + dim + "; " + i + "++)\n";
					dimensions += "[" + i + "]";
					this.IncIdent();
					num_identation++;
				}
				str += this.PrintIdent() + name1 + dimensions + " = " + name2 + dimensions;
				this.DecIdent(num_identation);
			}
			
			return str;
		}


		/****************************************************
		 * 
		 *			LOOPS FOR (UP AND DOWN)
		 * 
		 * *************************************************/





		/************************************************
		 *			HEAP
		 * *********************************************
		 * */
		 
		

		/************************************************
		 * 
		 *			LABELS
		 * 
		 * *********************************************/



		/********************************************
		 * 
		 *			WRITE RESULTS ON FILE TEST.VM
		 * 
		 * *****************************************/
		

		// write the code in a file	
		public void Interpret (string data, string file) 
		{ 
			try 
			{
				using (StreamWriter sw = new StreamWriter(file))
				{
					// Add text to the file.
					sw.Write(data);
					Console.WriteLine("Generated code was written in '" + file + "'!");
				}
			}
			catch (IOException) 
			{
				Console.WriteLine("--- Error accessing file {0}", data);
				System.Environment.Exit(0);
			}
		}


	}
}
