/// <summary> CleanupVar -> cleanupVar
    /// 
    /// This procedure is called when it looks like it may be OK
    /// to free up the variable's record and hash table entry, and
    /// those of its containing parent.  It's called, for example,
    /// when a trace on a variable deletes the variable.
    /// 
    /// </summary>
    /// <param name="var">variable that may be a candidate for being expunged.
    /// </param>
    /// <param name="array">Array that contains the variable, or NULL if this
    /// variable isn't an array element.
    /// </param>

    static protected internal void cleanupVar( Var var, Var array )
    {
      if ( var.isVarUndefined() && ( var.refCount == 0 ) && ( var.traces == null ) && ( ( var.flags & VarFlags.IN_HASHTABLE ) != 0 ) )
      {
        if ( var.table != null )
        {
          SupportClass.HashtableRemove( var.table, var.hashKey );
          var.table = null;
          var.hashKey = null;
        }
      }
      if ( array != null )
      {
        if ( array.isVarUndefined() && ( array.refCount == 0 ) && ( array.traces == null ) && ( ( array.flags & VarFlags.IN_HASHTABLE ) != 0 ) )
        {
          if ( array.table != null )
          {
            SupportClass.HashtableRemove( array.table, array.hashKey );
            array.table = null;
            array.hashKey = null;
          }
        }
      }
    }
    /// <summary> TCL.Tcl_UnsetVar2 -> unsetVar
    /// 
    /// Unset a variable, given a two-part name consisting of array
    /// name and element within array.
    /// 
    /// </summary>
    /// <param name="part1">1st part of the variable name.
    /// </param>
    /// <param name="part2">2nd part of the variable name.
    /// </param>
    /// <param name="flags">misc flags that control the actions of this method.
    /// 
    /// If part1 and part2 indicate a local or global variable in interp,
    /// it is deleted.  If part1 is an array name and part2 is null, then
    /// the whole array is deleted.
    /// 
    /// </param>

    internal static void unsetVar( Interp interp, string part1, string part2, TCL.VarFlag flags )
    {
      Var dummyVar;
      Var var;
      Var array;
      //ActiveVarTrace active;
      TclObject obj;
      TCL.CompletionCode result;

      // FIXME : what about the null return vs exception thing here?
      Var[] lookup_result = lookupVar( interp, part1, part2, flags, "unset", false, false );
      if ( lookup_result == null )
      {
        if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
          throw new TclRuntimeError( "unexpected null reference" );
        else
          return;
      }

      var = lookup_result[0];
      array = lookup_result[1];

      result = ( var.isVarUndefined() ? TCL.CompletionCode.ERROR : TCL.CompletionCode.OK );

      if ( ( array != null ) && ( array.sidVec != null ) )
      {
        deleteSearches( array );
      }


      // The code below is tricky, because of the possibility that
      // a trace procedure might try to access a variable being
      // deleted. To handle this situation gracefully, do things
      // in three steps:
      // 1. Copy the contents of the variable to a dummy variable
      //    structure, and mark the original Var structure as undefined.
      // 2. Invoke traces and clean up the variable, using the dummy copy.
      // 3. If at the end of this the original variable is still
      //    undefined and has no outstanding references, then delete
      //	  it (but it could have gotten recreated by a trace).

      dummyVar = new Var();
      //FIXME: Var class really should implement clone to make a bit copy. 
      dummyVar.value = var.value;
      dummyVar.traces = var.traces;
      dummyVar.flags = var.flags;
      dummyVar.hashKey = var.hashKey;
      dummyVar.table = var.table;
      dummyVar.refCount = var.refCount;
      dummyVar.ns = var.ns;

      var.setVarUndefined();
      var.setVarScalar();
      var.value = null; // dummyVar points to any value object
      var.traces = null;
      var.sidVec = null;

      // Call trace procedures for the variable being deleted. Then delete
      // its traces. Be sure to abort any other traces for the variable
      // that are still pending. Special tricks:
      // 1. We need to increment var's refCount around this: CallTraces
      //    will use dummyVar so it won't increment var's refCount itself.
      // 2. Turn off the TRACE_ACTIVE flag in dummyVar: we want to
      //    call unset traces even if other traces are pending.

      if ( ( dummyVar.traces != null ) || ( ( array != null ) && ( array.traces != null ) ) )
      {
        var.refCount++;
        dummyVar.flags &= ~VarFlags.TRACE_ACTIVE;
        callTraces( interp, array, dummyVar, part1, part2, ( flags & ( TCL.VarFlag.GLOBAL_ONLY | TCL.VarFlag.NAMESPACE_ONLY ) ) | TCL.VarFlag.TRACE_UNSETS );

        dummyVar.traces = null;

        // Active trace stuff is not part of Jacl's interp

        var.refCount--;
      }

      // If the variable is an array, delete all of its elements. This must be
      // done after calling the traces on the array, above (that's the way
      // traces are defined). If it is a scalar, "discard" its object
      // (decrement the ref count of its object, if any).

      if ( dummyVar.isVarArray() && !dummyVar.isVarUndefined() )
      {
        deleteArray( interp, part1, dummyVar, ( flags & ( TCL.VarFlag.GLOBAL_ONLY | TCL.VarFlag.NAMESPACE_ONLY ) ) | TCL.VarFlag.TRACE_UNSETS );
      }
      if ( dummyVar.isVarScalar() && ( dummyVar.value != null ) )
      {
        obj = (TclObject)dummyVar.value;
        obj.release();
        dummyVar.value = null;
      }

      // If the variable was a namespace variable, decrement its reference count.

      if ( ( var.flags & VarFlags.NAMESPACE_VAR ) != 0 )
      {
        var.flags &= ~VarFlags.NAMESPACE_VAR;
        var.refCount--;
      }

      // Finally, if the variable is truly not in use then free up its Var
      // structure and remove it from its hash table, if any. The ref count of
      // its value object, if any, was decremented above.

      cleanupVar( var, array );

      // It's an error to unset an undefined variable.

      if ( result != TCL.CompletionCode.OK )
      {
        if ( ( flags & TCL.VarFlag.LEAVE_ERR_MSG ) != 0 )
        {
          throw new TclVarException( interp, part1, part2, "unset", ( ( array == null ) ? noSuchVar : noSuchElement ) );
        }
      }
    }