/*  
 *  Extended Object Tcl (XOTcl)
 *
 *  Copyright (C) 1999-2014 Gustaf Neumann
 *  Copyright (C) 1999-2007 Uwe Zdun
 *
 *  xotclTrace.c --
 *  
 *  Tracing facilities for XOTcl
 *  
 */

#include "xotclInt.h"
#include "xotclAccessInt.h"

void
XOTclStackDump(Tcl_Interp *interp) {
  Interp *iPtr = (Interp *)interp;
  CallFrame *f = iPtr->framePtr, *v = iPtr->varFramePtr;
  Tcl_Obj *varCmdObj;

  XOTclNewObj(varCmdObj);
  fprintf (stderr, "     TCL STACK:\n");
  if (f == 0) fprintf(stderr, "- ");
  while (f) {
    Tcl_Obj *cmdObj;
    XOTclNewObj(cmdObj);
    fprintf(stderr, "\tFrame=%p ", f);
    if (f->isProcCallFrame && f->procPtr && f->procPtr->cmdPtr) {
      fprintf(stderr,"caller %p ",Tcl_CallFrame_callerPtr(f));
      fprintf(stderr,"callerV %p ",Tcl_CallFrame_callerVarPtr(f));
      Tcl_GetCommandFullName(interp, (Tcl_Command)  f->procPtr->cmdPtr, cmdObj);
      fprintf(stderr, "%s (%p) lvl=%d\n", ObjStr(cmdObj), f->procPtr->cmdPtr, f->level);
      DECR_REF_COUNT(cmdObj);
    } else fprintf(stderr, "- \n");

    f = f->callerPtr;
  }

  fprintf (stderr, "     VARFRAME:\n");
  fprintf(stderr, "\tFrame=%p", v);
  if (v) {fprintf(stderr, "caller %p", v->callerPtr);}
  if (v && v->isProcCallFrame && v->procPtr && v->procPtr->cmdPtr) {
    Tcl_GetCommandFullName(interp, (Tcl_Command)  v->procPtr->cmdPtr, varCmdObj);
    if (varCmdObj) {
      fprintf(stderr, " %s (%d)\n", ObjStr(varCmdObj), v->level);
    }
  } else fprintf(stderr, "- \n");
  DECR_REF_COUNT(varCmdObj);
}

void
XOTclCallStackDump(Tcl_Interp *interp) {
  XOTclCallStack *cs = &RUNTIME_STATE(interp)->cs;
  XOTclCallStackContent *csc;
  int i=1, entries = cs->top - cs->content;

  fprintf (stderr, "     XOTCL CALLSTACK: (%d entries, top: %p) \n", entries, cs->top);
  for (csc = &cs->content[1]; csc <= cs->top; csc++) {
    fprintf(stderr, "       %d: %p ",i++,csc);
    if (csc->self)
      fprintf(stderr, "OBJ %s (%p), ", ObjStr(csc->self->cmdName), csc->self);
    if (csc->cl)
      fprintf(stderr, "INSTPROC %s->", className(csc->cl));
    else
      fprintf(stderr, "PROC ");

    /*fprintf(stderr, " cmd %p, obj %p, ",csc->cmdPtr, csc->self);*/

    if (csc->cmdPtr && !csc->destroyedCmd)
      fprintf(stderr, "%s (%p), ", Tcl_GetCommandName(interp, (Tcl_Command)csc->cmdPtr),
	      csc->cmdPtr);
    else 
      fprintf(stderr, "NULL, ");

    fprintf(stderr, "frameType: %d, ", csc->frameType);
    fprintf(stderr, "callType: %d ", csc->callType);
    fprintf(stderr, "cframe %p  ", csc->currentFramePtr);

    if (csc->currentFramePtr) 
      fprintf(stderr,"l=%d ",Tcl_CallFrame_level(csc->currentFramePtr));

    if (csc->destroyedCmd)
      fprintf(stderr, "--destroyed cmd set (%p) ", csc->destroyedCmd);

    fprintf(stderr, "\n");
  }
  /*
  if (entries > 0) {
    XOTclCallStackContent *c;
    c = XOTclCallStackFindLastInvocation(interp);
    fprintf(stderr,"     --- findLastInvocation %p ",c);
    if (c) {
      if (c <= cs->top && c->currentFramePtr) 
	fprintf(stderr," l=%d", Tcl_CallFrame_level(c->currentFramePtr));
    }
    c = XOTclCallStackFindActiveFrame(interp, 1);
    fprintf(stderr,"     findActiveFrame    %p ",c);
    if (c) {
      if (c <= cs->top && c->currentFramePtr) 
	fprintf(stderr," l=%d", Tcl_CallFrame_level(c->currentFramePtr));
    }
    fprintf(stderr," --- \n");
  }
  */
}

/* helper function to print the vars dynamically created on a
  callframe
static void printLocalTable (CallFrame *c) {
  Tcl_HashEntry *entryPtr;
  Tcl_HashTable *localVarTablePtr = c->varTablePtr;
  Tcl_HashSearch search;

  fprintf(stderr, "LocalVars:");

  if (localVarTablePtr != NULL) {
    for (entryPtr = Tcl_FirstHashEntry(localVarTablePtr, &search);
	 entryPtr != NULL;
	 entryPtr = Tcl_NextHashEntry(&search)) {
      char *varName = Tcl_GetHashKey(localVarTablePtr, entryPtr);
      fprintf(stderr, " %s,", varName);
    }
  }
  fprintf(stderr,"\n");
}
*/

int
XOTcl_TraceObjCmd(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
  char *option;
  if (objc != 2)
    return XOTclObjErrArgCnt(interp, NULL, "::xotcl::trace");

  option = ObjStr(objv[1]);
  if (strcmp(option,"stack") == 0) {
    XOTclStackDump(interp);
    return TCL_OK;
  }
  if (strcmp(option,"callstack") == 0) {
    XOTclCallStackDump(interp);
    return TCL_OK;
  }
  return XOTclVarErrMsg(interp, "xotcltrace: unknown option", (char*) NULL);
}

#ifdef XOTCL_MEM_COUNT
void 
XOTclMemCountAlloc(char *id, void *p) {
  int new;
  XOTclMemCounter *entry;
  Tcl_HashTable *table = &xotclMemCount;
  Tcl_HashEntry *hPtr;
  hPtr = Tcl_CreateHashEntry(table, id, &new);
#ifdef XOTCL_MEM_TRACE
  fprintf(stderr, "+++ alloc %s %p\n",id,p);
#endif
  /*fprintf(stderr,"+++alloc '%s'\n",id);*/
  if (new) {
    entry = (XOTclMemCounter*)ckalloc(sizeof(XOTclMemCounter));
    entry->count = 1;
    entry->peak = 1;
    Tcl_SetHashValue(hPtr, entry);
  } else {
    entry = (XOTclMemCounter*) Tcl_GetHashValue(hPtr);
    entry->count++;
    if (entry->count > entry->peak)
      entry->peak = entry->count;
  }
}

void
XOTclMemCountFree(char *id, void *p) {
  XOTclMemCounter *entry;
  Tcl_HashTable *table = &xotclMemCount;
  Tcl_HashEntry *hPtr;
#ifdef XOTCL_MEM_TRACE
  fprintf(stderr, "+++ free %s %p\n",id,p);
#endif

  hPtr = Tcl_FindHashEntry(table, id);
  if (!hPtr) {
    fprintf(stderr, "******** MEM COUNT ALERT: Trying to free <%s>, but was not allocated\n", id);
    return;
  }
  entry = (XOTclMemCounter*)  Tcl_GetHashValue(hPtr);
  entry->count--;
}

void
XOTclMemCountDump() {
  Tcl_HashTable *table = &xotclMemCount;
  Tcl_HashSearch search;
  Tcl_HashEntry *hPtr;
  int count = 0;

  xotclMemCountInterpCounter--;
  if (xotclMemCountInterpCounter != 0) {
    return;
  }

  fprintf(stderr, "******** XOTcl MEM Count *********\n*  count peak\n");

  for (hPtr = Tcl_FirstHashEntry(table, &search);  hPtr != NULL;
       hPtr = Tcl_NextHashEntry(&search)) {
    char *id = Tcl_GetHashKey(table, hPtr);
    XOTclMemCounter *entry = (XOTclMemCounter*)  Tcl_GetHashValue(hPtr);
    count += entry->count;
    fprintf(stderr, "* %4d %6d %s\n", entry->count, entry->peak, id);
    ckfree ((char*) entry);
  }
  
  Tcl_DeleteHashTable(table);
  
  fprintf(stderr, "******** Count Overall = %d\n", count);
}

#endif
