/*=======================================================
 * Interp.java 
 *
 * "A Tcl like language implementation in Java named APWTCL
 * ((Java) Arnulf's Preferred Web Tcl)"
 *
 * APWTCL Interp class
 *
 * Released under same BSD license as Tcl.
 * (Tcl BSD license found at <http://www.tcl.tk/software/tcltk/license.html>)
 *
 * Copyright 2012 Arnulf P. Wiedemann
 *
 */

package org.apwtcl.lang;

//#if memdebug
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;

import org.apwtcl.lang.objtype.ArrayObjType;
import org.apwtcl.lang.objtype.CommandObjType;
import org.apwtcl.lang.objtype.DictObjType;
import org.apwtcl.lang.objtype.DoubleObjType;
import org.apwtcl.lang.objtype.ExprObjType;
import org.apwtcl.lang.objtype.IndexObjType;
import org.apwtcl.lang.objtype.IntObjType;
import org.apwtcl.lang.objtype.ItclObjType;
import org.apwtcl.lang.objtype.ListObjType;
import org.apwtcl.lang.objtype.NamespaceObjType;
import org.apwtcl.lang.objtype.ParamObjType;
import org.apwtcl.lang.objtype.ReturnCodeObjType;
import org.apwtcl.lang.objtype.ScriptLineObjType;
import org.apwtcl.lang.objtype.ScriptObjType;
import org.apwtcl.lang.objtype.SourceObjType;
import org.apwtcl.lang.objtype.StringObjType;
import org.apwtcl.lang.objtype.VariableObjType;


public class Interp extends Token implements Debug {

//#if memdebug
  /* keep track on how many ApwtclObjs have been created */
  static long interp_count = 0;
  /* actually keep track via weak references of all allocated ApwtclObjs */
//#endif 

  private long id;
  public ArrayObjType array_obj_type;
  public CommandObjType command_obj_type;
  public DictObjType dict_obj_type;
  public DoubleObjType double_obj_type;
  public ExprObjType expr_obj_type;
  public IndexObjType index_obj_type;
  public IntObjType int_obj_type;
  public ItclObjType itcl_obj_type;
  public ListObjType list_obj_type;
  public NamespaceObjType namespace_obj_type;
  public ParamObjType param_obj_type;
  public ReturnCodeObjType return_code_obj_type;
  public ScriptLineObjType script_line_obj_type;
  public ScriptObjType script_obj_type;
  public SourceObjType source_obj_type;
  public StringObjType string_obj_type;
  public VariableObjType variable_obj_type;
  
  public ApwtclObj default_obj;
  public ApwtclObj empty_string_obj;
  public CallFrame frame_ptr;
  public CallFrame var_frame_ptr;
  public CallFrame top_frame_ptr;
  
  public EvalStatement eval_statement;
  public Namespace global_ns_ptr;
  public Namespace current_namespace;
  public String current_namespace_name;
  public Resolve command_resolver;
  public Resolve variable_resolver;
  public ApwtclObj true_obj;
  public ApwtclObj false_obj;
  public ApwtclObj result;
  public HashMap<String, ItclClass> classes;
  public boolean error_flag;
  public ArrayList<ApwtclObj> cmd_priv_data;
  public ApwtclObj error_file_name_obj;
  public int return_code;
  public int error_line;
  public int add_stack_trace;
  public ApwtclObj error_proc;
  public ApwtclObj stack_trace;
  public Method call_procedure;
  public int proc_epoch;
  public int flags;
  public String curr_file_name;
  public int curr_line_no;
  public ApwtclObj rewrite_name_obj;
  public int rewrite_name_count;
  public int local;
  public ApwtclObj full_class_name;
  public int class_type;
  public ApwtclObj local_procs;
  public int max_nesting_depth;
  public int return_level;
  public int last_line_no;
  public int uplevel_dist;
  public ArrayList<Namespace> namespaces;
  public HashMap<Interp, ArrayList<InterpAlias>> aliases;
  public HashMap<String, ItclObject> class_objects;
  public int unknown_called;
  public ApwtclObj unknown;
  public Resolve lookup_resolve_var;
  public ApwtclObj error_code;
  public Expr eval_expression;
  public ItclClass curr_class_info;
  public LoadedClassInfo loaded_class_info;
  public TclTest tcl_tests;
  public ApwtclObj win;
  public ApwtclObj xhdr_object;
  public String start_dir;
  public String current_dir;
  public ApwtclObj current_script_obj;

  /* ==================== Interp ================================== */
  public Interp() {
    ++interp_count;
    id = interp_count;

    /* initialise the ObjType Objects */
    array_obj_type = new ArrayObjType(this);
    dict_obj_type = new DictObjType(this);
    expr_obj_type = new ExprObjType(this);
    double_obj_type = new DoubleObjType(this);
    index_obj_type = new IndexObjType(this);
    int_obj_type = new IntObjType(this);
    itcl_obj_type = new ItclObjType(this);
    list_obj_type = new ListObjType(this);
    namespace_obj_type = new NamespaceObjType(this);
    param_obj_type = new ParamObjType(this);
    return_code_obj_type = new ReturnCodeObjType(this);
    script_line_obj_type = new ScriptLineObjType(this);
    script_obj_type = new ScriptObjType(this);
    source_obj_type = new SourceObjType(this);
    string_obj_type = new StringObjType(this);
    variable_obj_type = new VariableObjType(this);
    /* next line must be at end it needs other *ObjType objects !! */
    command_obj_type = new CommandObjType(this);

    default_obj = new ApwtclObj(this);
    empty_string_obj = string_obj_type.newEmptyStringObj("INTERP_1");
    true_obj = int_obj_type.newIntObj(1);
    true_obj.incrRefCount("I_INTERP_3");
    false_obj = int_obj_type.newIntObj(0);
    false_obj.incrRefCount("I_INTERP_4");

    eval_statement = new EvalStatement(this);

    myInit();
    command_resolver = null;
    variable_resolver = null;
    result = empty_string_obj;
    result.incrRefCount("I_INTERP_5");
    classes = new HashMap<String, ItclClass>();
    error_flag = false;
    cmd_priv_data = null;
    error_file_name_obj = empty_string_obj;
    error_file_name_obj.incrRefCount("INTERP_6");
    error_line = 0;
    add_stack_trace = 0;
    error_proc = empty_string_obj;
    error_proc.incrRefCount("INTERP_7");
    return_code = 0;
    proc_epoch = 0;
    flags = 0;
    stack_trace = empty_string_obj;
    stack_trace.incrRefCount("INTERP_8");
    curr_file_name = "";
    curr_line_no = 0;
    rewrite_name_obj = empty_string_obj;
    rewrite_name_obj.incrRefCount("INTERP_9");
    rewrite_name_count = 0;
    local = 0;
    full_class_name = null;
    class_type = -1;
    local_procs = null;
    max_nesting_depth = 20;
    return_level = 0;
    last_line_no = 0;
    uplevel_dist = 0;
    namespaces = new ArrayList<Namespace>();
    aliases = new HashMap<Interp, ArrayList<InterpAlias>>();
    class_objects = new HashMap<String, ItclObject>();
    unknown_called = 0;
    unknown = string_obj_type.newStringObj("unknown", -1, "INTERP_7");
    lookup_resolve_var = null;
    error_code = null;
    eval_expression = new Expr(this);
    curr_class_info = null;
    loaded_class_info = new LoadedClassInfo(this);
    tcl_tests = null;
    win = null;
    xhdr_object = null;
    start_dir = "/home/arnulf/workspace/apwtcl/src/org/apwtcl/lang";
    current_dir = ".";
    current_script_obj = null;
  }

  /* ==================== mySelf ================================== */
  public String mySelf() {
    String str = "Interp!"+id+"!";
    return str;
  }

  /* ==================== toString ================================== */
  public String toString() {
    String str = mySelf();
    return str;
  }
  /* ==================== toDebugString ================================== */
  public String toDebugString() {
    StringBuffer str = new StringBuffer();
    str.append(mySelf()+"\n");
    if (frame_ptr == null) {
      str.append("   frame_ptr: <null>\n");
    } else {
      str.append("   frame_ptr: "+frame_ptr.toDebugString()+"\n");
    }
    return str.toString();
  }

  /* ==================== myInit ===================================== */
  public void myInit (/* Window win */) {
/*
    if (win.DOMParser) {
//      this.dom_parser=new DOMParser();
//      xmlDoc=this.dom_parser.parseFromString(text,"text/xml");
    } else {
      // Internet Explorer
//      xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
//      xmlDoc.async="false";
//      xmlDoc.loadXML(text); 
    }
*/

//    this.win = win;
    global_ns_ptr = namespace_obj_type.createNamespace("");
    if (global_ns_ptr == null) {
      panic(true, "createInterp: can't create global namespace");
    }
    current_namespace = global_ns_ptr;
    
    this.frame_ptr = new CallFrame(this, CALL_TYPE_NAMESPACE);
    this.frame_ptr.ns_ptr = global_ns_ptr;
    CallFrame frame_ptr = this.frame_ptr.newCallFrame(/* call_type */ CALL_TYPE_NAMESPACE, this.frame_ptr);
    int result = namespace_obj_type.pushCallFrame(frame_ptr, global_ns_ptr, /*isProcCallFrame*/ CALL_TYPE_NAMESPACE);
    if (result != OK) {
      panic(true, "createInterp: failed to push the root stack frame");
    }
    this.frame_ptr.argc = 0;
    this.frame_ptr.level = 0;
    this.frame_ptr = frame_ptr;
    this.var_frame_ptr = frame_ptr;
    this.top_frame_ptr = frame_ptr;

    current_namespace_name = global_ns_ptr.name;
    current_namespace = global_ns_ptr;
  }

  /* ==================== getCurrentNamespace ===================================== */
  public Namespace getCurrentNamespace() {
     if (uplevel_dist == 0) {
       return current_namespace;
     }
     return namespaces.get(frame_ptr.level - uplevel_dist);
  }

  /* ==================== getCommand ===================================== */
  public Command getCommand(ApwtclObj obj_ptr, int flags, ArrayList<ArrayList<ApwtclObj>> extra_args_ptr) {
    String cmd_name = obj_ptr.getString();
    ApwtclObj my_obj_ptr;
//print("cmd_name!"+cmd_name+"!"+((cmd_name.charAt(0) == ':' ) && (cmd_name.charAt(1) == ':')));
    if (!((cmd_name.charAt(0) == ':' ) && (cmd_name.charAt(1) == ':'))) {
      String ns_name = frame_ptr.ns_ptr.full_name;
      if (!ns_name.equals("::")) {
    	ns_name = ns_name+"::";
      }
      my_obj_ptr = string_obj_type.newStringObj(ns_name+cmd_name, -1, "INTERP_11");	
    } else {
      my_obj_ptr = obj_ptr;
    }
    ArrayList<InterpAlias> my_aliases = aliases.get(this);
    Command cmd_obj = null;
    boolean had_extra_args = false;
//print("aliases!"+aliases+"!"+my_obj_ptr+"!");
    for (int i = 0; i < aliases.size(); i++) {
      InterpAlias my_alias = my_aliases.get(i);
//print("alias!"+i+"!"+my_alias+"!");
      InterpAlias have_alias = my_alias.getAlias(empty_string_obj, my_obj_ptr);
//print("have_alias!"+have_alias+"!"+my_obj_ptr+"!");
      if (have_alias != null) {
        if (have_alias.params.size() != 0) {
          /* have arguments !!! */
//print("TARG!"+have_alias.target_cmd.getString()+"!"+have_alias.params+"!");
          extra_args_ptr.add(have_alias.params);
          had_extra_args = true;
        }
        obj_ptr = have_alias.target_cmd;
        break;
      }
    }
    if (!had_extra_args) {
    	extra_args_ptr.add(null);
    }
    cmd_name = obj_ptr.getString();
    
//print("new cmd_name!"+cmd_name+"!");
    /* optimization: we check, if the command is in the command_cache and if the command epoch
     * R.Base.command_oid <= call frame.command_oid and if yes, we can use the cached command
     */
/*
    if ((frame_ptr.top_command_oid == R.Base.command_oid) &&
       (frame_ptr.top_itcl_command_oid == R.Base.itcl_command_oid) &&
       (frame_ptr.top_class_function_oid == R.Base.class_function_oid)) {
      var my_command_cache = frame_ptr.command_cache;
      if (typeof my_command_cache[cmd_name] != "undefined") {
print("found cmd!"+cmd_name+"!"+frame_ptr.top_command_oid+"!"+R.Base.command_oid+"!");
        cmd_obj = my_command_cache[cmd_name];
      }
    } else {
      var my_command_cache = frame_ptr.command_cache;
      my_command_cache = new Object();
      frame_ptr.top_command_oid = R.Base.command_oid;
      frame_ptr.top_itcl_command_oid = R.Base.itcl_command_oid;
      frame_ptr.top_class_function_oid = R.Base.class_function_oid;
    }
*/
    /* FIXME !! need to not use cache for these otherwise problems
     * have to figure out why
     */
    if ((cmd_name == "constructor") || (cmd_name == "destructor") || (cmd_name == "join") || (cmd_name == "function")) {
      cmd_obj = null;
    }
    if (cmd_obj == null) {
      cmd_obj = command_obj_type.getCommandFromObj(obj_ptr);
//print("getCFO!"+cmd_obj+"!");
      if (cmd_obj == null) {
        /* check if we have to load one of the tcl_pkg_*cmd packages to resolve the command */
//        cmd_obj = intp.checkStubsCommand(cmd_name);
      }
      if (cmd_obj != null) {
//        var my_command_cache = intp.frame_ptr.command_cache;
//        my_command_cache[cmd_name] = cmd_obj;
      }
    }
    if (cmd_obj == null) {
//      throw "No such command '"+cmd_name+"'";
//print("cmd_obj null!");
    } else {
//print("cmd_obj!"+cmd_obj.toDebugString()+"!");
    }
    return cmd_obj;
  }

  /* ==================== getNamespace ===================================== */
  public Namespace getNamespace() {
    int level = eval_statement.level - uplevel_dist;
//print("getNamespace!"+level+"!"+eval_statement.level+"!"+uplevel_dist+"!"+"!");
//print("getNamespace a!"+level+"!"+"!");
    return eval_statement.call_frames.get(level).ns_ptr;
  }

  /* ==================== getNamespaceName ===================================== */
  public String getNamespaceName() {
    return current_namespace_name;
  }

  /* ==================== getGlobalNamespace ===================================== */
  public Namespace getGlobalNamespace() {
    return global_ns_ptr;
  }

  /* ==================== setLookupResolveVar ===================================== */
  public void setLookupResolveVar(Resolve val) {
    lookup_resolve_var = val;
  }

  /* ==================== getLookupResolveVar ===================================== */
  public Resolve getLookupResolveVar() {
    return lookup_resolve_var;
  }

  /* ==================== setClassObject ===================================== */
  public ItclClass setClassObject(String name, ItclClass val) {
    return classes.put(name, val);
  }

  /* ==================== getClassObject ===================================== */
  public int getClassObject(String name, ArrayList<ItclClass> result_ptr) {
    // FIXME need better handling of uplevel namespace for ::itcl::type here !!!
    name = escapeKey(name);
    if (classes.get(name) == null) {
      if (classes.get("::"+name) == null) {
        result_ptr.add(null);
	return ERROR;
      } else {
       result_ptr.add(classes.get("::"+name));
      }
    } else {
        result_ptr.add(classes.get(name));
    }
    return OK;
  }

  /* ==================== getItclObject ===================================== */
  public int getItclObject(String name, ArrayList<ItclObject>result_ptr) {
    // FIXME need better handling of uplevel namespace for ::itcl::type here !!!
    name = escapeKey(name);
//print("getItclObject!"+name+"!"+intp.frame_ptr.toDebugString()+"!");
    if (class_objects.get(name) == null) {
      if (class_objects.get("::"+name) == null) {
        if (class_objects.get(frame_ptr.ns_ptr.full_name+"::"+name) == null) {
          if (class_objects.get(frame_ptr.ns_ptr.parent_namespace.full_name+"::"+name) == null) {
            result_ptr.add(null);
	  } else {
            result_ptr.add(class_objects.get(frame_ptr.ns_ptr.parent_namespace.full_name+"::"+name));
	  }
	} else {
          result_ptr.add(class_objects.get(frame_ptr.ns_ptr.full_name+"::"+name));
	}
      } else {
       result_ptr.add(class_objects.get("::"+name));
      }
    } else {
        result_ptr.add(class_objects.get(name));
    }
    return OK;
  }

  /* ==================== validName ===================================== */
  public int validName(String type, ApwtclObj name_obj_ptr) {
    if (name_obj_ptr.obj_type != OBJ_TYPE_VARIABLE) {
      String str = name_obj_ptr.getString();
      int len = string_obj_type.getStringLength(name_obj_ptr);
      for (int i = 0; i < len; i++) {
//        if (str.charAt(i) == '\0') {
//	    setResultFormatted(type+" name contains embedded null");
//          return ERROR;
//	}
      }
    }
    return OK;
  }

  /* ==================== incrProcEpoch ===================================== */
  public void incrProcEpoch() {
    proc_epoch++;
  }

  /* ==================== wrongNumArgs ===================================== */
  public void wrongNumArgs(int offset, ArrayList<ApwtclObj> argv, String msg) {
    ApwtclObj obj_ptr;
    ApwtclObj list_obj_ptr;
    int argv_idx = 0;
    ArrayList<ApwtclObj> result = new ArrayList<ApwtclObj>();

print("wrongNumArgs! "+offset+"!"+argv+"!"+msg+"!");
if (false) {
    if (rewrite_name_obj != null) {
      offset -= rewrite_name_count;
      argv_idx += rewrite_name_count;
      list_obj_ptr = list_obj_type.newListObj(result, 1);
      rewrite_name_obj = result.get(0);
//      list_obj_type.insertElements(list_obj_ptr, -1, argv.size(), argv_idx, argv);
    } else {
      list_obj_ptr = list_obj_type.newListObj(result, 1);
    }
    if (msg != null) {
      list_obj_type.listAppendElement(list_obj_ptr, string_obj_type.newStringObj(msg, -1, "INTERP_10"));
    }
    list_obj_ptr.incrRefCount("I_INTERP_10");
//    obj_ptr = list_obj_type.ListJoin(list_obj_ptr, " ", 1);
    list_obj_ptr.decrRefCount("D_INTERP_1");
    obj_ptr.incrRefCount("I_INTERP_11");
    setResultFormatted("wrong # args: should be \"%#s\"", obj_ptr);
    obj_ptr.decrRefCount("D_INTERP_2");
}
return;
  }

  /* ==================== setEmptyResult ================================== */
  public void setEmptyResult() {
    setResult(empty_string_obj);
  }

  /* ==================== setResult ================================== */
  public void setResult(ApwtclObj obj_ptr) {
    obj_ptr.incrRefCount("I_INTERP_12");
    result.decrRefCount("D_INTERP_3");
    result = obj_ptr;
  }

  /* ==================== setResultString ================================== */
  public void setResultString(String str) {
    result.decrRefCount("D_INTERP_4");
    result = string_obj_type.newStringObj(str, -1, "INTERP11");
    result.incrRefCount("I_INTERP_13");
  }

  /* ==================== setResultBool ================================== */
  public void setResultBool(boolean val) {
    int my_val = 0;
    if (val == true) {
      my_val = 1;
    }
    if (val == false) {
      my_val = 0;
    }
    setResult(int_obj_type.newIntObj(my_val));
  }

  /* ==================== setResultInt ================================== */
  public void setResultInt(int val) {
    setResult(int_obj_type.newIntObj(val));
  }

  /* ==================== setResultFormatted ===================================== */
  /**
   * Very simple printf-like formatting, designed for error messages.
   *
   * The format may contain up to 5 '%s' or '%#s', corresponding to variable arguments.
   * The resulting string is created and set as the result.
   *
   * Each '%s' should correspond to a regular string parameter.
   * Each '%#s' should correspond to a (ApwtclObj *) parameter.
   * Any other printf specifier is not allowed (but %% is allowed for the % character).
   *
   * e.g. setResultFormatted("Bad option \"%#s\" in proc \"%#s\"", optionObjPtr, procNamePtr);
   *
   * Note: We take advantage of the fact that printf has the same behaviour for 
   * both %s and %#s
   */
  public void setResultFormatted(String format, ArrayList<Object> arguments) {
    /* Initial space needed */
    int n = 1;
    StringBuffer buf = new StringBuffer("");
    int i;
    int start_idx = 0;
    int len = format.length();
    String str;

    for (i = 0; i < len; i++) {
      if (format.charAt(i) == '%') {
        switch (format.charAt(i + 1)) {
        case 's':
          buf.append(format.substring(start_idx, i));
	  str = (String)arguments.get(n);
          buf.append(str);
	  n++;
	  i += 1;
	  start_idx = i + 1;
          break;
	case '#':
	  if (format.charAt(i + 2) == 's') {
	    ApwtclObj obj_ptr;

            buf.append(format.substring(start_idx, i));
	    obj_ptr = (ApwtclObj)arguments.get(n);
            buf.append(obj_ptr.getString());
	    n++;
	    i += 2;
	    start_idx = i + 1;
            break;
          } else {
	    i++;
          }
	default:
	  i++;
	}
      }
    }
    buf.append(format.substring(start_idx, i));
    len = buf.length();
    setResult(string_obj_type.newStringObjNoAlloc(buf.toString(), len, "INTERP_12"));
  }

  /* ==================== getResult ===================================== */
  public ApwtclObj getResult() {
    return result;
  }

  /* ==================== deleteLocalProcs ================================== */
  public void deleteLocalProcs() {
print("ERROR deleteLocalProcs not yet implemented");
  }

  /* ==================== setObjErrorCode ===================================== */
  /*
   *----------------------------------------------------------------------
   *
   * setObjErrorCode --
   *
   *      This function is called to record machine-readable information about
   *      an error that is about to be returned. The caller should build a list
   *      object up and pass it to this routine.
   *
   * Results:
   *      None.
   *
   * Side effects:
   *      The error_code field of the interp is set to the new value.
   *
   *----------------------------------------------------------------------
   */
  public void setObjErrorCode(ApwtclObj error_obj_ptr) {
    if (error_code != null) {
      error_code.decrRefCount("D_INTERP_5");
    }
    error_code = error_obj_ptr;
    error_code.incrRefCount("I_INTERP_14");
  }

  /* ==================== setErrorCode ===================================== */
  /*
   *----------------------------------------------------------------------
   *
   * setErrorCode --
   *
   *      This function is called to record machine-readable information about
   *      an error that is about to be returned.
   *
   * Results:
   *      None.
   *
   * Side effects:
   *      The error_code field of the interp is modified to hold all of the
   *      arguments to this function, in a list form with each argument becoming
   *      one element of the list.
   *
   *----------------------------------------------------------------------
   */
  public void setErrorCode(ArrayList<String> args) {
    ApwtclObj error_obj = string_obj_type.newStringObj("", -1, "INTERP_13");
    int i;

    /*
     * Scan through the arguments one at a time, appending them to the
     * error_code field as list elements.
     */
     for (i = 0; i < args.size(); i++) {
        list_obj_type.listAppendElement(error_obj, string_obj_type.newStringObj(args.get(i), -1, "INNTERP_14"));
     }
     setObjErrorCode(error_obj);
  }

  /* ==================== getCallFrameByInteger ===================================== */
  /* Similar to getCallFrameByLevel() but the level is specified
   * as a relative integer like in the [info level ?level?] command.
   */
  public CallFrame getCallFrameByInteger(ApwtclObj level_obj_ptr) {
    ArrayList<Long> level_ptr = new ArrayList<Long>();
    CallFrame my_frame_ptr;
    int level;

    if (int_obj_type.getLong(level_obj_ptr, level_ptr) == OK) {
      level = (int)(level_ptr.get(0) & 0xffffffff);
      if (level <= 0) {
        /* Convert from a relative to an absolute level */
        level = frame_ptr.level + level;
      }
      if (level == 0) {
        return top_frame_ptr;
      }
      /* Lookup */
      for (my_frame_ptr = frame_ptr; my_frame_ptr != null; my_frame_ptr = frame_ptr.parent_call_frame) {
        if (my_frame_ptr.level == level) {
          return my_frame_ptr;
        }
      }
    }
    setResultFormatted("bad level \"%#s\"", level_obj_ptr);
    return null;
   }

  /* ==================== getCallFrameByLevel ===================================== */
  /* Returns the call frame relative to the level represented by
   * level_obj_ptr. If level_obj_ptr == null, the * level is assumed to be '1'.
   *
   * This function accepts the 'level' argument in the form
   * of the commands [uplevel] and [upvar].
   *
   * For a function accepting a relative integer as level suitable
   * for implementation of [info level ?level?] check the
   * getCallFrameByInteger() function.
   *
   * Returns null on error.
   */
  public CallFrame getCallFrameByLevel(ApwtclObj level_obj_ptr) {
    int level;
    String str;
    CallFrame my_frame_ptr;

//print("getFrameByLevel!"+level_obj_ptr.toDebugString()+"!"+intp.frame_ptr.toDebugString()+"!");
    if (level_obj_ptr != null) {
      str = level_obj_ptr.getString();
      if (str.charAt(0) == '#') {
        level = Integer.parseInt(str.substring(1));
        if (str.length() == 0) {
          level = -1;
        }
      } else {
        ArrayList<Long> level_ptr = new ArrayList<Long>();
        if (int_obj_type.getLong(level_obj_ptr, level_ptr) != OK || level_ptr.get(0) < 0) {
          level = -1;
        } else {
          level = (int)(level_ptr.get(0) & 0xffffffff);
          /* Convert from a relative to an absolute level */
          level = frame_ptr.level - level;
        }
      }
    } else {
      str = "1";              /* Needed to format the error message. */
      level = frame_ptr.level - 1;
    }
    if (level == 0) {
      return top_frame_ptr;
    }
    if (level > 0) {
      /* Lookup */
      for (my_frame_ptr = frame_ptr; my_frame_ptr != null; my_frame_ptr = frame_ptr.parent_call_frame) {
        if (my_frame_ptr.level == level) {
          return my_frame_ptr;
        }
      }
    }
    setResultString("bad level \""+str+"\"");
    return null;
  }

  /* ==================== infoLevel ===================================== */
  public int infoLevel(ApwtclObj level_obj_ptr, ArrayList<ApwtclObj> obj_ptr_ptr, boolean is_info_level_cmd) {
    CallFrame target_call_frame;

    target_call_frame = getCallFrameByInteger(level_obj_ptr);
    if (target_call_frame == null) {
      return ERROR;
    }
    /* No proc call at toplevel call frame */
    if (target_call_frame == top_frame_ptr) {
      setResultFormatted("bad level \"%#s\"", level_obj_ptr);
      return ERROR;
    }
    if (is_info_level_cmd) {
      obj_ptr_ptr.add(list_obj_type.newListObj(target_call_frame.argv, target_call_frame.argc));
    } else {
      ApwtclObj list_obj = list_obj_type.newListObj(null, 0);

      list_obj_type.listAppendElement(list_obj, target_call_frame.argv.get(0));
      list_obj_type.listAppendElement(list_obj, target_call_frame.file_name_obj);
      list_obj_type.listAppendElement(list_obj, int_obj_type.newIntObj(target_call_frame.line));
      obj_ptr_ptr.add(list_obj);
    }
    return OK;
  }

  /* ==================== unknownFcn ===================================== */
  public int unknownFcn(int argc, ArrayList<ApwtclObj> argv, ApwtclObj file_name_obj, int line_no) {
print("UNKNOWN!"+argc+"!"+argv+"!"+file_name_obj+"!"+line_no+"!");
    ArrayList<ApwtclObj> v = new ArrayList<ApwtclObj>();
    int ret_code;

    /* If unknown() is recursively called too many times...
     * done here
     */
    if (unknown_called > 50) {
      return ERROR;
    }
    /* If the [unknown] command does not exists returns
     * just now 
     */
    ArrayList<ArrayList<ApwtclObj>> extra_args_ptr = new ArrayList<ArrayList<ApwtclObj>>();
    if (getCommand(unknown, FUNCTION_FLAGS_NONE, extra_args_ptr) == null) {
      return ERROR;
    }
    /* The object interp.unknown just contains
     * the "unknown" string, it is used in order to
     * avoid to lookup the unknown command every time
     * but instead to cache the result. 
     */
    /* Make a copy of the arguments vector, but shifted on
     * the right of one position. The command name of the
     * command will be instead the first argument of the
     * [unknown] call. 
     */
    v.add(unknown);
    for (int i = 0; i < argc; i++) {
      v.add(argv.get(i));
    }
    /* Call it */
    unknown_called++;
    ret_code = eval_statement._evalObjVector(argc + 1, v, file_name_obj, line_no);
    unknown_called--;
    /* Clean up */
    return ret_code;
  }

  /* ==================== getTclTests ===================================== */
  public TclTest getTclTests() {
    if (tcl_tests == null) {
      tcl_tests = new TclTest(this);
    }
    return tcl_tests;
  }

  /* ==================== setResultFormatted ================================== */
  public int setResultFormatted(String str, ApwtclObj obj_ptr) {
    return OK;
  }

  /* ==================== resetResult ================================== */
  public void resetResult() {
  }

  /* ==================== incrCoreCommand ================================== */
  public int incrCoreCommand() {
return OK;
  }

  /* ==================== unknown ===================================== */
  public int unknown(int argc, ArrayList<ApwtclObj> argv) {
return -1;
  }

}
