/*=======================================================
 * ApwtclObj.java 
 *
 * "A Tcl like language implementation in Java named APWTCL
 * ((Java) Arnulf's Preferred Web Tcl)"
 *
 * APWTCL ApwtclObj 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

//#endif

import java.util.ArrayList;
import java.util.HashMap;

import org.apwtcl.lang.objtype.*;

import com.sun.xml.internal.ws.util.StringUtils;

public class ApwtclObj extends Token implements Debug {

//#if memdebug
  /* keep track on how many ApwtclObjs have been created */
  static long oid = 0;
//#endif 

  public static ApwtclObj free_obj_list = null;
  public static ApwtclObj live_obj_list = null;

  public long id;
  private Interp interp;
  private ApwtclObj next_obj_ptr;
  private ApwtclObj prev_obj_ptr;
  private ObjTypeBase obj_type_base;
  private Object value1;
  private Object value2;
  private Object value3;

  public StringBuffer bytes;
  public int len;
  public String allocated;
  public String freed;
  public ArrayList<String> incr_lst;
  public ArrayList<String> decr_lst;
  public boolean check_allocate;
  public int ref_count;

  public int obj_type;

  /* ==================== ApwtclObj ================================== */
  public ApwtclObj(Interp interp) {
    this.interp = interp;
    ++oid;
    id = oid;

    bytes = new StringBuffer();
    len = 0;
    allocated = null;
    freed = null;
    obj_type_base = new ObjTypeBase(interp);
    incr_lst = new ArrayList<String>();
    decr_lst = new ArrayList<String>();
    check_allocate = true;
  }

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

  /* ==================== toString ===================================== */
  public String toString() {
    return getString();
  }

  /* ==================== toDebugString ===================================== */
  public String toDebugString() {
    StringBuffer str = new StringBuffer(mySelf()+"\n");
    str.append("  ref_count: "+ref_count+"\n");
    str.append("  len: "+len+"\n");
    str.append("  obj_type: "+getObjTypeString(obj_type)+"\n");
    str.append("  allocated: "+allocated+"\n");
    str.append("  freed: "+freed+"\n");
    str.append("  incr: "+incr_lst+"\n");
    str.append("  decr: "+decr_lst+"\n");
    str.append("  bytes: "+((bytes == null) ? "<null>" : getString())+"\n");
    if (obj_type > 0) {
      switch (obj_type) {
      case OBJ_TYPE_LIST:
        str.append("  list_elem: "+listValue_GetElem()+"\n");
        break;
      case OBJ_TYPE_INT:
    	str.append("  value: "+wideValue_GetValue()+"\n");
        break;
      }
    }
    return str.toString();
  }

  /* ==================== arrayValue_GetVarNameObjPtr ================================== */
  public ApwtclObj arrayValue_GetVarNameObjPtr() {
    return (ApwtclObj)value1;
  }

  /* ==================== arrayValue_SetVarNameObjPtr ================================== */
  public ApwtclObj arrayValue_SetVarNameObjPtr(ApwtclObj var_name_obj_ptr) {
    value1 = (Object)var_name_obj_ptr;
    return var_name_obj_ptr;
  }

  /* ==================== arrayValue_GetIndexObjPtr ================================== */
  public ApwtclObj arrayValue_GetIndexObjPtr() {
    return (ApwtclObj)value2;
  }

  /* ==================== arrayValue_SetIndexObjPtr ================================== */
  public ApwtclObj arrayValue_SetIndexObjPtr(ApwtclObj index_obj_ptr) {
    value2 = (Object)index_obj_ptr;
    return index_obj_ptr;
  }

  /* ==================== binaryValue_GetData ================================== */
  public Object binaryValue_GetData() {
    return value1;
  }

  /* ==================== binaryValue_SetData ================================== */
  public Object binaryValue_SetVarNameObj(Object data) {
    value1 = data;
    return data;
  }

  /* ==================== binaryValue_GetLen ================================== */
  public int binaryValue_GetLen() {
    return (Integer)value2;
  }

  /* ==================== binaryValue_SetLen ================================== */
  public int binaryValue_SetLen(int len) {
    value2 = (Object)len;
    return len;
  }

  /* ==================== cmdValue_GetEpoch ================================== */
  public int cmdValue_GetEpoch() {
    return (Integer)value1;
  }

  /* ==================== cmdValue_SetEpoch ================================== */
  public int cmdValue_SetEpoch(int epoch) {
    value1 = epoch;
    return epoch;
  }

  /* ==================== cmdValue_GetResolvedCmdNamePtr ================================== */
  public ResolvedCmdName cmdValue_GetResolvedCmdNamePtr() {
	  return (ResolvedCmdName)value2;
  }

  /* ==================== cmdValue_SetResolvedCmdNamePtr ================================== */
  public ResolvedCmdName cmdValue_SetResolvedCmdNamePtr(ResolvedCmdName cmd_ptr) {
    value2 = (Object)cmd_ptr;
    return cmd_ptr;
  }

  /* ==================== doubleValue_GetValue ================================== */
  public double doubleValue_GetValue() {
    return (Double)value1;
  }

  /* ==================== doubleValue_SetValue ================================== */
  public double doubleValue_SetValue(double val) {
    value1 = (Object)((Double)val);
    return val;
  }

  /* ==================== hashValue_GetValue ================================== */
  public Object hashValue_GetValue() {
    return value1;
  }

  /* ==================== hashValue_SetValue ================================== */
  public Object hashValue_SetValue(Object val) {
    value1 = val;
    return val;
  }

  /* ==================== indexValue_GetValue ================================== */
  public int indexValue_GetValue() {
    return (Integer)value1;
  }

  /* ==================== indexValue_SetValue ================================== */
  public int indexValue_SetValue(int val) {
    value1 = (Object)val;
    return val;
  }

  /* ==================== itclClassValue_GetClassDef ================================== */
  public ApwtclObj itclClassValue_GetClassDef() {
    return (ApwtclObj)value1;
  }

  /* ==================== itclClassValue_SetClassDef ================================== */
  public ApwtclObj itclClassValue_SetClassDef(ApwtclObj class_def) {
    value1 = (Object)class_def;
    return class_def;
  }

  /* ==================== itclClassValue_GetClassType ================================== */
  public int itclClassValue_GetClassType() {
    return (Integer)value2;
  }

  /* ==================== itclClassValue_SetClassType ================================== */
  public int itclClassValue_SetClassType(int class_type) {
    value2 = (Object)class_type;
    return class_type;
  }

  /* ==================== itclClassValue_GetClassObjects ================================== */
  public ApwtclObj itclClassValue_GetClassObjects() {
    return (ApwtclObj)value3;
  }

  /* ==================== itclClassValue_SetClassObjects ================================== */
  public ApwtclObj itclClassValue_SetMaxLen(ApwtclObj class_objects) {
    value3 = (ApwtclObj)class_objects;
    return class_objects;
  }

  /* ==================== listValue_GetElem ================================== */
  public ArrayList<ApwtclObj> listValue_GetElem() {
    return (ArrayList<ApwtclObj>)value1;
  }

  /* ==================== listValue_SetElem ================================== */
  public ArrayList<ApwtclObj> listValue_SetElem(ArrayList<ApwtclObj> elem) {
    value1 = (Object)elem;
    return elem;
  }

  /* ==================== listValue_GetLen ================================== */
  public int listValue_GetLen() {
    return (Integer)value2;
  }

  /* ==================== listValue_SetLen ================================== */
  public int listValue_SetLen(int len) {
    value2 = (Object)len;
    return len;
  }

  /* ==================== listValue_GetMaxLen ================================== */
  public int listValue_GetMaxLen() {
    return (Integer)value3;
  }

  /* ==================== listValue_SetMaxLen ================================== */
  public int listValue_SetMaxLen(int max_len) {
    value3 = (Object)max_len;
    return max_len;
  }

  /* ==================== namespaceValue_GetNamespace ================================== */
  public ApwtclObj namespaceValue_GetNamespace() {
    return (ApwtclObj)value1;
  }

  /* ==================== namespaceValue_SetNamespace ================================== */
  public ApwtclObj namespaceValue_SetNamespace(ApwtclObj namespace) {
    value1 = (Object)namespace;
    return namespace;
  }

  /* ==================== namespaceValue_GetFullName ================================== */
  public String namespaceValue_GetFullName() {
    return (String)value2;
  }

  /* ==================== namespaceValue_SetFullName ================================== */
  public String namespaceValue_SetFullName(String full_name) {
    value2 = (Object)full_name;
    return full_name;
  }

  /* ==================== paramInfoValue_GetParams ================================== */
  public FunctionArgList paramInfoValue_GetParams() {
    return (FunctionArgList)value1;
  }

  /* ==================== paramInfoValue_SetParams ================================== */
  public FunctionArgList paramInfoValue_SetParams(FunctionArgList params) {
    value1 = (Object)params;
    return params;
  }

  /* ==================== paramInfoValue_GetOrigParams ================================== */
  public FunctionArgList paramInfoValue_GetOrigParams() {
    return (FunctionArgList)value2;
  }

  /* ==================== paramInfoValue_SetOrigParams ================================== */
  public FunctionArgList paramInfoValue_SetOrigParams(FunctionArgList orig_params) {
    value2 = (Object)orig_params;
    return orig_params;
  }

  /* ==================== ptrAndLongRepValue_GetPtr ================================== */
  public ApwtclObj ptrAndLongRepValue_GetPtr() {
    return (ApwtclObj)value1;
  }

  /* ==================== ptrAndLongRepValue_SetPtr ================================== */
  public ApwtclObj ptrAndLongRepValue_SetPtr(ApwtclObj ptr) {
    value1 = (Object)ptr;
    return ptr;
  }

  /* ==================== ptrAndLongRepValue_GetValue ============================= */
  public long ptrAndLongRepValue_GetValue() {
    return (Long)value2;
  }

  /* ==================== ptrAndLongRepValue_SetValue ============================= */
  public long ptrAndLongRepValue_SetValue(long val) {
    value2 = (Object)val;
    return val;
  }

  /* ==================== ptrValue_GetPtr ================================== */
  public Object ptrValue_GetPtr() {
    return (Object)value1;
  }

  /* ==================== ptrValue_SetPtr ================================== */
  public Object ptrValue_SetPtr(Object ptr) {
    value1 = (Object)ptr;
    return ptr;
  }

  /* ==================== refValue_GetRefPtr ================================== */
  public ApwtclObj refValue_GetRefPtr() {
    return (ApwtclObj)value1;
  }

  /* ==================== refValue_SetRefPtr ================================== */
  public ApwtclObj refValue_SetRefPtr(ApwtclObj ref_ptr) {
    value1 = (Object)ref_ptr;
    return ref_ptr;
  }

  /* ==================== refValue_GetId ================================== */
  public int refValue_GetId() {
    return (Integer)value2;
  }

  /* ==================== refValue_SetId ================================== */
  public int refValue_SetId(int id) {
    value2 = (Object)id;
    return id;
  }

  /* ==================== regexpValue_GetCompre ================================== */
  public String regexpValue_GetCompre() {
    return (String)value1;
  }

  /* ==================== regexpValue_SetCompre ================================== */
  public String regexpValue_SetCompre(String compre) {
    value1 = (Object)compre;
    return compre;
  }

  /* ==================== regexpValue_GetFlags ================================== */
  public int regexpValue_GetFlags() {
    return (Integer)value2;
  }

  /* ==================== regexpValue_SetFlags ================================== */
  public int regexpValue_SetFlags(int flags) {
    value2 = (Object)flags;
    return flags;
  }

  /* ==================== returnCodeValue_GetReturnCode ================================== */
  public int returnCodeValue_GetReturnCode() {
    return (Integer)value1;
  }

  /* ==================== returnCodeValue_SetReturnCode ================================== */
  public int returnCodeValue_SetReturnCode(int return_code) {
    value1 = (Object)return_code;
    return return_code;
  }

  /* ==================== scriptLineValue_GetLine ================================== */
  public int scriptLineValue_GetLine() {
    return (Integer)value1;
  }

  /* ==================== scriptLineValue_SetLine ================================== */
  public int scriptLineValue_SetLine(int val) {
    value1 = (Object)val;
    return val;
  }

  /* ==================== scriptLineValue_GetArgc ================================== */
  public int scriptLineValue_GetArgc() {
    return (Integer)value2;
  }

  /* ==================== scriptLineValue_SetArgc ================================== */
  public int scriptLineValue_SetArgc(int val) {
    value2 = (Object)val;
    return val;
  }

  /* ==================== scriptValue_GetScript ================================== */
  public Script scriptValue_GetScript() {
    return (Script)value1;
  }

  /* ==================== scriptValue_SetScript ================================== */
  public Script scriptValue_SetScript(Script script) {
    value1 = (Object)script;
    return script;
  }

  /* ==================== sourceValue_GetFileNameObj ================================== */
  public ApwtclObj sourceValue_GetFileNameObj() {
    return (ApwtclObj)value1;
  }

  /* ==================== sourceValue_SetFileNameObj ================================== */
  public ApwtclObj sourceValue_SetFileNameObj(ApwtclObj file_name_obj) {
    value1 = (Object)file_name_obj;
    return file_name_obj;
  }

  /* ==================== sourceValue_GetLineNumber ================================== */
  public int sourceValue_GetLineNumber() {
    return (Integer)value2;
  }

  /* ==================== sourceValue_SetLineNumber ================================== */
  public int sourceValue_SetLineNumber(int line_number) {
    value2 = (Object)line_number;
    return line_number;
  }

  /* ==================== strValue_GetCharLength ================================== */
  public int strValue_GetCharLength() {
    return (Integer)value1;
  }

  /* ==================== strValue_SetCharLength ================================== */
  public int strValue_SetCharLength(int char_length) {
    value1 = (Object)char_length;
    return char_length;
  }

  /* ==================== strValue_GetMaxLength ================================== */
  public int strValue_GetMaxLength() {
    return (Integer)value1;
  }

  /* ==================== strValue_SetMaxLength ================================== */
  public int strValue_SetMaxLength(int max_length) {
    value1 = (Object)max_length;
    return max_length;
  }

  /* ==================== twoPtrValue_GetPtr1 ================================== */
  public Object twoPtrValue_GetPtr1() {
    return value1;
  }

  /* ==================== twoPtrValue_SetPtr1 ================================== */
  public Object twoPtrValue_SetPtr1(Object ptr1) {
    value1 = ptr1;
    return ptr1;
  }

  /* ==================== twoPtrValue_GetPtr2 ================================== */
  public Object twoPtrValue_GetPtr2() {
    return value2;
  }

  /* ==================== twoPtrValue_SetPtr2 ================================== */
  public Object twoPtrValue_SetPtr2(Object ptr2) {
    value2 = ptr2;
    return ptr2;
  }

  /* ==================== varValue_GetCallFrame ================================== */
  public CallFrame varValue_GetCallFrame() {
    return (CallFrame)value1;
  }

  /* ==================== varValue_SetCallFrame ================================== */
  public CallFrame varValue_SetCallFrame(CallFrame call_frame) {
    value1 = (Object)call_frame;
    return call_frame;
  }

  /* ==================== varValue_GetNamespace ================================== */
  public Namespace varValue_GetNamespace() {
    return (Namespace)value2;
  }

  /* ==================== varValue_SetNamespace ================================== */
  public Namespace varValue_SetNamespace(Namespace namespace) {
    value2 = (Object)namespace;
    return namespace;
  }

  /* ==================== varValue_GetVarPtr ================================== */
  public Variable varValue_GetVarPtr() {
    return (Variable)value3;
  }

  /* ==================== varValue_SetVarPtr ================================== */
  public Variable varValue_SetVarPtr(Variable var_ptr) {
    value3 = (Object)var_ptr;
    return var_ptr;
  }

  /* ==================== wideValue_GetValue ================================== */
  public long wideValue_GetValue() {
    return (Long)value1;
  }

  /* ==================== wideValue_SetValue ================================== */
  public long wideValue_SetValue(long val) {
    value1 = (Object)val;
    return val;
  }

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

  /* =========================== newObj ===================================== */
  public ApwtclObj newObj() {
    ApwtclObj my_obj_ptr;
    /* -- Check if there are objects in the free list -- */
    if (free_obj_list != null) {
      /* -- Unlink the object from the free list -- */
      my_obj_ptr = free_obj_list;
      free_obj_list = my_obj_ptr.next_obj_ptr;
    } else {
      /* -- No ready to use objects: allocate a new one -- */
      my_obj_ptr = new ApwtclObj(interp);
    }
    /* Object is returned with ref_count of 0. Every
     * kind of GC implemented should take care to don't try
     * to scan objects with ref_count == 0. */
    my_obj_ptr.ref_count = 0;
    /* All the other fields are left not initialized to save time.
     * The caller will probably want to set them to the right
     * value anyway. */

    /* -- Put the object into the live list -- */
    my_obj_ptr.prev_obj_ptr = null;
    my_obj_ptr.next_obj_ptr = live_obj_list;
    if (live_obj_list != null) {
      live_obj_list.prev_obj_ptr = my_obj_ptr;
    }
    live_obj_list = my_obj_ptr;
    my_obj_ptr.obj_type = 0;
    my_obj_ptr.bytes = null;
    return my_obj_ptr;
  }

  /* ==================== freeNewObj ===================================== */
  public void freeNewObj(String where) {
    freeObj(where);
    freed = where;
  }

  /* ==================== freeObj ===================================== */
  /* Free an Obj. Actually Objs are never freed, but
   * just moved to the free obj list, where they will be
   * reused by NewObj(). 
   */
  public void freeObj(String where) {
//print("freeObj!"+mySelf()+"!"+obj_type+"!"+getString()+"!");
    /* Check if the object was already freed, panic. */
    panic(ref_count != 0, "!!! "+mySelf()+" freed with bad ref_count "+ref_count+", type="+getObjTypeString(obj_type)+"!"+toDebugString()+"!");
    freeIntRep();
    /* Free the string representation */
    if (bytes != null) {
      bytes = null;
    }
    /* Unlink the object from the live objects list */
    if (prev_obj_ptr != null) {
      prev_obj_ptr.next_obj_ptr = next_obj_ptr;
    }
    if (next_obj_ptr != null) {
      next_obj_ptr.prev_obj_ptr = prev_obj_ptr;
    }
    if (live_obj_list == this) {
      live_obj_list = next_obj_ptr;
    }
    /* Link the object into the free objects list */
    prev_obj_ptr = null;
    next_obj_ptr = free_obj_list;
    if (free_obj_list != null) {
      free_obj_list.prev_obj_ptr = this;
    }
    free_obj_list = this;
    ref_count = -1;
    freed = where;
  }
				          
  /* ==================== dumpObjLists ===================================== */
  public void dumpObjLists() {
    ApwtclObj obj_ptr;

    print("live_list!"+live_obj_list+"!");
    obj_ptr = live_obj_list;
    while (obj_ptr != null) {
      print(obj_ptr.toDebugString());
//      print(obj_ptr.mySelf());
      obj_ptr = obj_ptr.next_obj_ptr;
    }

    print("free_list!"+free_obj_list+"!");
    obj_ptr = free_obj_list;
    while (obj_ptr != null) {
      print(obj_ptr.toDebugString());
//      print(obj_ptr.mySelf());
      obj_ptr = obj_ptr.next_obj_ptr;
    }
  }
				          
  /* ==================== invalidateStringRep ===================================== */
  public void invalidateStringRep() {
    if (bytes != null) {
      bytes = null;
    }
  } 

  /* ==================== setIntRepPtr ===================================== */
  public void setIntRepPtr(Object ptr) {
    ptrValue_SetPtr(ptr);
  } 

  /* ==================== freeIntRep ===================================== */
  public void freeIntRep() {
    obj_type_base.callFreeIntRepProc(obj_type, this);
  } 

  /* ==================== setStringRep ===================================== */
  public void setStringRep(ApwtclObj obj_ptr, String str, int len) {
    obj_ptr.bytes.append(str);
    obj_ptr.len = len;
  } 

  /* ==================== initStringRep ===================================== */
  /* Set the initial string representation for an object. */
  public void initStringRep(ApwtclObj obj_ptr, String str, int len) {
    obj_ptr.bytes.append(str);
    obj_ptr.len = len;
  } 

  /* Xdigitval and Odigitval are helper functions for escapeBackslash() */
  /* ==================== xdigitval ===================================== */
  public int xdigitval(char ch) {
    if (ch >= '0' && ch <= '9') {
      return ch - '0';
    }
    if (ch >= 'a' && ch <= 'f') {
      return ch - 'a' + 10;
    }
    if (ch >= 'A' && ch <= 'F') {
      return ch - 'A' + 10;
    }
    return -1;
  }

  /* ==================== odigitval ===================================== */
  public int odigitval(char ch) {
    if (ch >= '0' && ch <= '7') {
      return ch - '0';
    }
    return -1;
  }

  /* ==================== escapeBackslash ================================== */
  /* Perform Tcl escape substitution of 's', storing the result
   * string into 'dest'. The escaped string is guaranteed to
   * be the same length or shorted than the source string.
   * Slen is the length of the string at 's', if it's -1 the string
   * length will be calculated by the function.
   *
   * The function returns the length of the resulting string. 
   */
  public int escapeBackslash(ArrayList<String> dest_ptr, String str, int slen) {

    int result;
    int i;
    int len;
    StringBuffer p = new StringBuffer("");

    if (slen == -1) {
      slen = str.length();
    }
    for (i = 0; i < slen; i++) {
      switch (str.charAt(i)) {
      case '\\':
        switch (str.charAt(i + 1)) {
        case 'b':
          p.append("\b");
           i++;
           break;
        case 'f':
          p.append("\f");
          i++;
          break;
        case 'n':
          p.append("\n");
          i++;
          break;
        case 'r':
          p.append("\r");
          i++;
          break;
        case 't':
          p.append("\t");
          i++;
          break;
        case 'u':
        case 'x':
          /* A unicode or hex sequence.
           * \u1111 Expect 1-4 hex chars and convert to utf-8.
           * \x Expect 1-2 hex chars and convert to hex.
           * An invalid sequence means simply the escaped char.
           */

          int val = 0;
          int k;

          i++;
          for (k = 0; k < (str.charAt(i) == 'u' ? 4 : 2); k++) {
            int c = xdigitval(str.charAt(i + k + 1));

            if (c == -1) {
              break;
            }
            val = (val << 4) | c;
          }
          if (k > 0) {
            /* Got a valid sequence, so convert */
            if (str.charAt(i) == 'u') {
//              p.append(utf8_fromunicode(p, val));
            } else {
              p.append(val);
            }
            i += k;
            break;
          }
          /* Not a valid codepoint, just an escaped char */
          p.append(str.charAt(i));
          break;
        case 'v':
          p.append(0xb);
          i++;
          break;
        case '\n':
          /* Replace all spaces and tabs after backslash newline with a single space*/
          p.append(" ");
          do {
            i++;
	  } while (str.charAt(i) == ' ' || str.charAt(i) == '\t');
          break;
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
          /* octal escape */
          val = 0;
          int c = odigitval(str.charAt(i + 1));

          val = c;
          c = odigitval(str.charAt(i + 2));
          if (c == -1) {
            p.append(val);
            i++;
            break;
          }
          val = (val * 8) + c;
//          c = odigitval(str, i + 3);
          if (c == -1) {
            p.append(val);
            i += 2;
            break;
          }
          val = (val * 8) + c;
          p.append(val);
          i += 3;
          break;
        default:
          p.append(str.charAt(i + 1));
          i++;
          break;
        }
        break;
      default:
        p.append(str.charAt(i));
        break;
      }
    }
    len = p.length();
    dest_ptr.add(p.toString());
    return len;
  }

  /* ==================== isShared ================================== */
  public boolean isShared() {
    return (ref_count > 1);
  }

  /* ==================== isListObj ================================== */
  public boolean isListObj() {
    return (obj_type == OBJ_TYPE_LIST);
  }

  /* ==================== getIntRepPtr ================================== */
  public Object getIntRepPtr() {
     return ptrValue_GetPtr();
  }

  /* ==================== incrRefCount ================================== */
  public int  incrRefCount(String where) {
    ref_count++;
    if (check_allocate) {
      incr_lst.add(where);
    }
    return ref_count;
  }

  /* ==================== decrRefCount ================================== */
  public int decrRefCount(String where) {
    ref_count--;
    if (check_allocate) {
      decr_lst.add(where);
    }
    if (ref_count <= 0) {
      freeObj(where);
    }
    return ref_count;
  }

  /* ==================== getString ===================================== */
  public String getString() {
    if (bytes == null) {
      /* Invalid string repr. Generate it. */
      panic(obj_type == 0, "UpdateStringProc called against '<none>' type."+mySelf()+"!");
      panic(!obj_type_base.haveUpdateString(obj_type), "UpdateString called against '"+getObjTypeString(obj_type)+"' type.");
      obj_type_base.callUpdateString(obj_type, this);
    }
    return bytes.substring(0, len);
  }

  /* ==================== getStringLength ===================================== */
  public int getStringLength() {
    if (bytes == null) {
      /* Invalid string repr. Generate it. */
      panic(obj_type == 0, "UpdateStringProc called against '<none>' type."+mySelf()+"!");
      panic(obj_type_base.haveUpdateString(obj_type), "UpdateString called against '"+getObjTypeString(obj_type)+"' type.");
      obj_type_base.callUpdateString(obj_type, this);
    }
    return len;
  }

  /* ==================== getStringFromArray ===================================== */
  public String getStringFromArray(ArrayList<ApwtclObj> list, int nested) {
    ArrayList<String> res = new ArrayList<String>();
    String str;

    for (int i = 0; i < list.size(); i++) {
      str = list.get(i).getString();
      if (isList(str) && !isNested(str)) {
        res.add("{"+str+"}");
      } else {
        if (str == "") {
          res.add("{}");
        } else {
          res.add(str);
	}
      }
    }
    if (res.size() == 1) {
      return res.get(0);
    }
    StringBuilder sb = new StringBuilder();
    for (String s : res){
        sb.append(s);
        sb.append(" ");
    }
    return sb.toString();
  }

  /* ==================== findHashEntry ===================================== */
  public ApwtclObj findHashEntry(HashMap<String, ApwtclObj> hash_table, String key) {
    return hash_table.get(key);
  }

  /* ==================== deleteHashEntry ===================================== */
  public int deleteHashEntry(HashMap<String, ApwtclObj> hash_table, String key) {
    if (hash_table.get(key) == null) {
      return ERROR;
    }
    hash_table.remove(key);
    return OK;
  }

  /* ==================== addHashEntry ===================================== */
  public int addHashEntry(HashMap<String, ApwtclObj> hash_table, String key, ApwtclObj val) {
    /* Check if the new element already exists. */
    if (hash_table.get(key) != null) {
      return ERROR;
    }
    /* stores key */
    hash_table.put(key, val);
    return OK;
  }

  /* ==================== concatObj ===================================== */
  public ApwtclObj concatObj(int objc, ArrayList<ApwtclObj> objv) {
    int i;

    /* If all the objects in objv are lists,
     * it's possible to return a list as result, that's the
     * concatenation of all the lists. 
     */
    for (i = 0; i < objc; i++) {
      if (!objv.get(i).isListObj()) {
        break;
      }
    }
    if (i == objc) {
      ApwtclObj obj_ptr = interp.list_obj_type.newListObj(null, 0);

      for (i = 0; i < objc; i++) {
        interp.list_obj_type.listAppendList(obj_ptr, objv.get(i));
      }
      return obj_ptr;
    } else {
      /* Else... we have to glue strings together */
      int len = 0;
      int obj_len;
      StringBuffer p;

      /* Compute the length */
      for (i = 0; i < objc; i++) {
        len += objv.get(i).getStringLength();
      }
      if (objc > 0) {
        len += objc - 1;
      }
      /* Create the string rep, and a string object holding it. */
      p = new StringBuffer("");
      for (i = 0; i < objc; i++) {
        String s = objv.get(i).getString();
	int j = 0;

        obj_len = objv.get(i).getStringLength();
        /* Remove leading space */
        while (obj_len > 0 && (s.charAt(j) == ' ' || s.charAt(j) == '\t' || s.charAt(j) == '\n')) {
          j++;
          obj_len--;
          len--;
        }
        /* And trailing space */
        while (obj_len > 0 && (s.charAt(obj_len - 1) == ' ' || s.charAt(obj_len - 1) == '\n' || s.charAt(obj_len - 1) == '\t')) {
          /* Handle trailing backslash-space case */
          if (obj_len > 1 && s.charAt(obj_len - 2) == '\\') {
            break;
          }
          obj_len--;
          len--;
        }
        p.append(s.substring(j, obj_len));
        j += obj_len;
        if (obj_len != 0 && i + 1 != objc) {
          p.append(" ");
        } else {
          if (i + 1 != objc) {
            /* Drop the space calcuated for this
             * element that is instead null. */
            len--;
          }
        }
      }
      return interp.string_obj_type.newStringObjNoAlloc(p.toString(), len, "OBJ_1");
    }
  }

  /* ==================== duplicateObj ===================================== */
  public ApwtclObj duplicateObj() {
    ApwtclObj dup_ptr;

    dup_ptr = newObj();
    if (bytes == null) {
      /* Object does not have a valid string representation. */
      dup_ptr.bytes = null;
    } else {
      initStringRep(dup_ptr, bytes.toString(), len);
    }
    /* By default, the new object has the same type as the old object */
    dup_ptr.obj_type = obj_type;
    if (obj_type > 0) {
      if (!obj_type_base.haveDupInternalRep(obj_type)) {
        dup_ptr.value1 = value1;
        dup_ptr.value2 = value2;
        dup_ptr.value3 = value3;
      } else {
        /* The dup proc may set a different type, e.g. null */
        obj_type_base.callDupInternalRep(obj_type, this, dup_ptr);
      }
    }
    return dup_ptr;
  }

  /* ==================== utf8_tounicode_case ===================================== */
  public int utf8_tounicode_case(char ch, ArrayList<Character> result_ch, int upper) {
    /* this is just a dummy right now, real utf8 support is missing !! */
    ArrayList<Character> res_ch_ptr = new ArrayList<Character>();
    int l = utf8_tounicode(ch, res_ch_ptr);
    Character res_ch = res_ch_ptr.get(0);
    if (upper != 0) {
      result_ch.add(Character.toUpperCase(res_ch));
    } else {
      result_ch.add(res_ch);
    }
    return l;
  }

  /* ==================== utf8_tounicode ===================================== */
  public int utf8_tounicode(char ch, ArrayList<Character>result_ch) {
/*    
	  String s = str;

    
    if (s.charCodeAt(0) < 0xc0) {
      result_ch.add(s.charAt(0));
      return 1;
    }
    if (s.charCodeAt(0) < 0xe0) {
      if ((s.charCodeAt(1) & 0xc0) == 0x80) {
        result_ch[0] = string.fromCharCode(((s.charCodeAt(0) & ~0xc0) << 6) | (s.charCodeAt(1) & ~0x80));
        return 2;
      }
    } else {
      if (s.charCodeAt(0) < 0xf0) {
        if (((str.charCodeAt(1) & 0xc0) == 0x80) && ((strcharCodeAt(2) & 0xc0) == 0x80)) {
          result_ch[0] = string.fromCharCode(((s.charCodeAt(0) & ~0xe0) << 12) | ((s.charCodeAt(1) & ~0x80) << 6) | (s.charCodeAt(2) & ~0x80));
          return 3;
        }
      }
    }
    /* Invalid sequence, so just return the byte */
    /* this is just a dummy right now, real utf8 support is missing !! */
    result_ch.add(ch);
    return 1;

    /* this is just a dummy right now, real utf8 support is missing !! */
//    result_ch[0] = str.charAt(0);
//    return 1;

  }

  /* ==================== utf8_tounicode ===================================== */
  public int utf8_tounicode(String str, ArrayList<Character>result_ch) {
/*    
	  String s = str;

    
    if (s.charCodeAt(0) < 0xc0) {
      result_ch.add(s.charAt(0));
      return 1;
    }
    if (s.charCodeAt(0) < 0xe0) {
      if ((s.charCodeAt(1) & 0xc0) == 0x80) {
        result_ch[0] = string.fromCharCode(((s.charCodeAt(0) & ~0xc0) << 6) | (s.charCodeAt(1) & ~0x80));
        return 2;
      }
    } else {
      if (s.charCodeAt(0) < 0xf0) {
        if (((str.charCodeAt(1) & 0xc0) == 0x80) && ((strcharCodeAt(2) & 0xc0) == 0x80)) {
          result_ch[0] = string.fromCharCode(((s.charCodeAt(0) & ~0xe0) << 12) | ((s.charCodeAt(1) & ~0x80) << 6) | (s.charCodeAt(2) & ~0x80));
          return 3;
        }
      }
    }
    /* Invalid sequence, so just return the byte */
/*	  
    result_ch.add(ch);
    return 1;

    /* this is just a dummy right now, real utf8 support is missing !! */
    result_ch.add(str.charAt(0));
    return 1;
  }

  /* ==================== utf8_memchr ===================================== */
/*
  utf8_memchr: function (str, len, ch) {
    var obj = this;

    /* need code for UTF8! */
    /* now it's without that */
/*
    return str.indexOf(ch);
  },

  /* ==================== utf8_prev_len ===================================== */
  public int utf8_prev_len(String str, int len) {
    int n = 1;

    /* Look up to len chars backward for a start-of-char byte */
//    while (--len) {
//      if ((str.chatAt(len) & 0x80) == 0) {
//        /* Start of a 1-byte char */
//        break;
//      }
//      if ((str.charAt(len) & 0xc0) == 0xc0) {
//        /* Start of a multi-byte char */
//        break;
//      }
//      n++;
//    }
    return n;
  }

  /* ==================== utf8_length ===================================== */
  public int utf8_length(ApwtclObj obj_ptr) {
    /* no utf8 support yet !! */

    return obj_ptr.getStringLength();
  }

  /* ==================== utf8_index ===================================== */
  public int utf8_index(String str, int index) {
    int idx = 0;

    while (index-- > 0) {
      ArrayList<Character> c_ptr = new ArrayList<Character>();
      idx += utf8_tounicode(str.substring(idx), c_ptr);
    }
    return idx;

  }

  /* ==================== globMatch ===================================== */
  public int globMatch(String pattern, String str, int nocase) {
    ArrayList<Character> c = new ArrayList<Character>();
    ArrayList<Character> pchar = new ArrayList<Character>();
    int i = 0;
    int j = 0;

    while (i < pattern.length()) {
      switch (pattern.charAt(i)) {
      case '*':
        i++;
        while (pattern.charAt(i) == '*') {
          i++;
        }
        if (pattern.charAt(i) == '\0' || i >= pattern.length()) {
          return 1;   /* match */
        }
        pattern = pattern.substring(i);
        i = 0;
        while (str.length() > j) {
          /* Recursive call - Does the remaining pattern match anywhere? */
          if (globMatch(pattern.substring(i), str.substring(j), nocase) == 1) {
            return 1;       /* match */
	      }
          j += utf8_tounicode(str.charAt(j), c);
        }
        return 0;       /* no match */
      case '?':
        j += utf8_tounicode(str.charAt(j), c);
        break;
      case '[': 
        j += utf8_tounicode(str.charAt(j), c);
//        pattern = JimCharsetMatch(pattern + 1, c, nocase != 0 ? NOCASE : 0);
        if (pattern != "") {
          return 0;
        }
        if (pattern.length() <= i) {
          /* Ran out of pattern (no ']') */
          continue;
        }
        break;
      case '\\':
        if (pattern.length() < i) {
          i++;
        }
        /* fall through */
      default:
	    c = new ArrayList<Character>();
	    pchar = new ArrayList<Character>();
        int k = utf8_tounicode_case(str.charAt(j), c, nocase);
        str = str.substring(k);
        utf8_tounicode_case(pattern.charAt(i), pchar, nocase);
        if (pchar.get(0) != c.get(0)) {
          return 0;
        }
        break;
      }
      i += utf8_tounicode_case(pattern.charAt(0), pchar, nocase);
      if (str.length() <= j) {
        while (pattern.charAt(i) == '*') {
          i++;
        }
        break;
      }
    }
    if (pattern.length() <= i && str.length() <= j) {
      return 1;
    }
    return 0;
  }

  /* ==================== stringMatch ===================================== */
  public int stringMatch(String str, int nocase) {
    return globMatch(getString(), str, nocase);
  }


  /* ==================== stringMatchObj ===================================== */
  public int stringMatchObj(ApwtclObj obj_ptr, int nocase) {
    return stringMatch(obj_ptr.getString(), nocase);
  }

  /* ==================== getSign ===================================== */
  public int getSign(long wide_value) {
    if (wide_value == 0) {
      return 0;
    } else {
      if (wide_value < 0) {
        return -1;
      }
      return 1;
    }
  }

  /* ==================== stringCompareNoCase ===================================== */
  /*
   * No-case version.
   *
   * If maxchars is -1, compares to end of string.
   * Otherwise compares at most 'max_chars' characters.
   */
  public int stringCompareNoCase(String s1, String s2, int max_chars) {
    int idx1 = 0;
    int idx2 = 0;

    while (idx1 < s1.length() && idx2 < s2.length() && max_chars != 0) {
      ArrayList<Character> c1_ptr = new ArrayList<Character>();
      ArrayList<Character> c2_ptr = new ArrayList<Character>();
      idx1 += utf8_tounicode_case(s1.charAt(idx1), c1_ptr, 1);
      idx2 += utf8_tounicode_case(s2.charAt(idx2), c2_ptr, 1);
      if (c1_ptr.get(0) != c2_ptr.get(0)) {
        char c1 = c1_ptr.get(0);
        char c2 = c2_ptr.get(0);
        return getSign(c1 - c2);
      }
      max_chars--;
    }
    if (max_chars != 0) {
        return 0;
    }
    /* One string or both terminated */
    if (idx1 < s1.length()) {
        return 1;
    }
    if (idx2 < s2.length()) {
        return -1;
    }
    return 0;
  }

  /* ==================== stringCompare ===================================== */
  public int stringCompare(String s1, int l1, String s2, int l2) {
    int idx1 = 0;
    int idx2 = 0;

    while (idx1 < l1 && idx2 < l2) {
      ArrayList<Character> c1_ptr = new ArrayList<Character>();
      ArrayList<Character> c2_ptr = new ArrayList<Character>();
      idx1 += utf8_tounicode_case(s1.charAt(idx1), c1_ptr, 1);
      idx2 += utf8_tounicode_case(s2.charAt(idx2), c2_ptr, 1);
      if (c1_ptr.get(0) != c2_ptr.get(0)) {
        char c1 = c1_ptr.get(0);
        char c2 = c2_ptr.get(0);
        return getSign(c1 - c2);
      }
    }
    /* One string or both terminated */
    if (idx1 < l1) {
        return 1;
    }
    if (idx2 < l2) {
        return -1;
    }
    return 0;
  }

  /* ==================== stringCompareObj ===================================== */
  public int stringCompareObj(ApwtclObj second_obj_ptr, int nocase) {
    ApwtclObj first_obj_ptr = this;
    String s1;
    String s2;
    int l1;
    int l2;

    s1 = first_obj_ptr.getString();
    l1 = first_obj_ptr.getStringLength();
    s2 = second_obj_ptr.getString();
    l2 = second_obj_ptr.getStringLength();
    if (nocase != 0) {
      return stringCompareNoCase(s1, s2, -1);
    }
    return stringCompare(s1, l1, s2, l2);
  }

  /* ==================== stringEqObj ===================================== */
  public boolean stringEqObj(ApwtclObj second_obj_ptr) {
    String a_str;
    String b_str;
    int a_len;
    int b_len;

    if (this == second_obj_ptr) {
      return true;
    }
    a_str = getString();
    a_len = getStringLength();
    b_str = second_obj_ptr.getString();
    b_len = second_obj_ptr.getStringLength();
    if (a_len != b_len) {
      return false;
    }
    return (a_str == b_str);
  }

}
