/**
 * MicroJava Symbol Table Objects:
 * Every named object in a program is stored in an <code>Obj</code> node.
 * Every scope has a list of objects declared within it.
 */

public class Obj {
	public static final int 
		Con = 0, Var = 1, Type = 2, Meth = 3, Fld = 4, Prog = 5;
	
	public int kind;	// Con, Var, Type, Meth, Fld, Prog
	public String name;
	public Struct type;
	public Obj next;	// next Obj node in this scope
	public int adr;		// Con: value; Meth, Var, Fld: offset
	public int level;	// Var: declaration level; Meth: # of parameters
	public Obj locals;	// Meth: list of local variables; 
						// Prog: symbol table of program
	
	Obj (int kind, String name, Struct type) {
		this.kind = kind; this.name = name; this.type = type;
	}
	
	// for testing purposes
	public Obj (int kind, String name, Struct type, int adr, int level, Obj locals) {
		this(kind, name, type);
		this.adr = adr; this.level = level; this.locals = locals;
	}
	
	public boolean equals (Object o) {
		if (super.equals(o)) return true;	// same object

		if (!(o instanceof Obj)) return false;

		Obj other = (Obj) o;

		return kind == other.kind && name.equals(other.name) &&
			   type.equals(other.type) &&
			   adr == other.adr && level == other.level &&
			   equalsCompleteList(locals, other.locals);
	}
	
	public String toString () {
		StringBuffer sb = new StringBuffer();
            boolean b=false; 
		
		switch (kind) {
			case Con: sb.append("Con "); break;
			case Var: sb.append("Var "); break;
			case Type: sb.append("Type "); break;
			case Meth: sb.append("Meth "); break;
			case Fld: sb.append("Fld "); b=true; break;
			case Prog: sb.append("Prog "); break;
		}
		sb.append(name);
		sb.append(": ");
		Obj objlist =  locals;
		switch (type.kind) {
			case Struct.None: sb.append("notype"); break;
			case Struct.Int: sb.append("int"); break;
			case Struct.Char: sb.append("char"); break;
			case Struct.Arr: sb.append("Arr of "); 
                        	 switch (type.elemType.kind) {
                    			case Struct.None: sb.append("notype"); break;
			                    case Struct.Int: sb.append("int"); break;
			                    case Struct.Char: sb.append("char"); break;
			                    case Struct.Arr: sb.append("Arr of "); 
			                    case Struct.Class: sb.append("Class"); break;
		                     }
			                 break;
			case Struct.Class: sb.append("Class"); if (!b) objlist=type.fields; break;
		}		
		sb.append(", ");
		sb.append(adr);
		sb.append(", ");
		sb.append(level+" ");
		while (objlist != null) {
    		if (kind==Prog) sb.append("\n    ");
    		sb.append("[");
			sb.append(objlist.toString());
    		sb.append("]");
			objlist = objlist.next;
		}		 
		
		return sb.toString();
	}
	
	/** Compare complete Obj node list. */
	public static boolean equalsCompleteList (Obj o1, Obj o2) {
		if (o1 == o2) return true;			// same object
		
		while (o1 != null && o1.equals(o2)) {
			o1 = o1.next; o2 = o2.next;
		}
		if (o1 != null || o2 != null) return false;
		
		return true;
	}
}


