/*
** constant.c
** Series of routines to minimize memory usage through the use
** of shared strings
*/
#include "odieInt.h"

#include <strings.h>

Tcl_HashTable constant_strings;
Tcl_Obj *irmStatic[14];

typedef struct constObj {
  char    *string;
  Tcl_Obj *tclobj;
} constObj;

Tcl_Obj *Odie_shared_tclobj(int which) {
  if(which > ODIE_STATIC_MAX || which < 0) {
    Tcl_IncrRefCount(irmStatic[ODIE_STATIC_NULL]);
    return irmStatic[ODIE_STATIC_NULL];
  }
  Tcl_IncrRefCount(irmStatic[which]);
  return irmStatic[which];
}

Tcl_Obj *Odie_NewBooleanObj(int value) {
  if(value) {
    return Odie_shared_tclobj(ODIE_STATIC_ONE);
  }
  return Odie_shared_tclobj(ODIE_STATIC_ZERO);
}

Tcl_Obj *Odie_NewIntObj(int value) {
  if(value>=0 && value < 10) {
    int idx=ODIE_STATIC_ZERO+value;
    Tcl_IncrRefCount(irmStatic[idx]);
    return irmStatic[idx];
  }
  if(value==-1) {
    Tcl_IncrRefCount(irmStatic[ODIE_STATIC_NEG1]);
    return irmStatic[ODIE_STATIC_NEG1];
  }
  return Tcl_NewIntObj(value);
}


static constObj *Odie_constant(const char *zName,int create) {
  int len,isNew=0;
  Tcl_HashEntry *pEntry;
  constObj *p;
  if(zName==NULL) {
    return NULL;
  }
  if(create) {
    pEntry=Tcl_CreateHashEntry(&constant_strings,zName,&isNew);
  } else {
    pEntry=Tcl_FindHashEntry(&constant_strings,zName);
  }
  if(isNew) {
    len = strlen(zName);
    p =(constObj*)Odie_Alloc(sizeof(*p)+len+1);
    p->string=p+1;
    strncpy(p->string, zName, len+1);
    p->tclobj=Tcl_NewStringObj(zName,len);
    Tcl_IncrRefCount(p->tclobj);
    Tcl_SetHashValue(pEntry,(ClientData)p);
    return p;
  }
  if(pEntry) {
    p=(constObj*)Tcl_GetHashValue(pEntry);
    return p;
  }
  return NULL;
}

int Odie_SameString(char *aPtr,char *bPtr) {
  if(aPtr==bPtr) {
    return 1;
  }
  if(!bPtr || !aPtr) {
    return 0;
  }
  if(strcmp(aPtr,bPtr)==0) {
    return 1;
  }
  return 0;
}
char *Odie_constant_string(const char *zName) {
  constObj *p;
  p=Odie_constant(zName,1);
  return p->string;
}


Tcl_Obj *Odie_constant_tclobj(const char *zName) {
  constObj *p;
  p=Odie_constant(zName,1);
  Tcl_IncrRefCount(p->tclobj);
  return p->tclobj;
}

Tcl_Obj *Odie_NewStringObj(const char *str) {
  /*
  if(!str) {
    return Tcl_NewObj();
  }
  return Tcl_NewStringObj(str,-1);
  */
  return Odie_constant_tclobj(str);
}

static int constantMapCmd(
  void *pArg,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  char *newName;
  Tcl_Obj *result;

  if(objc != 2) {
      Tcl_WrongNumArgs(interp, 1, objv, "string");
      return TCL_ERROR;
  }
  newName=Tcl_GetString(objv[1]);
  result=Odie_constant_tclobj(newName);
  if (!result) return TCL_ERROR;
  Tcl_SetObjResult(interp,result);
  return TCL_OK;
}

DLLEXPORT int Odie_Constant_Init(Tcl_Interp *interp) {
  static int once = 1;

  if( once ){
    int i;
    once = 0;

    irmStatic[ODIE_STATIC_NULL]=Tcl_NewObj();
    Tcl_IncrRefCount(irmStatic[ODIE_STATIC_NULL]);
    
    irmStatic[ODIE_STATIC_ZERO] = Tcl_NewBooleanObj(0);
    Tcl_IncrRefCount(irmStatic[ODIE_STATIC_ZERO]);
    
    irmStatic[ODIE_STATIC_ONE] = Tcl_NewBooleanObj(1);
    Tcl_IncrRefCount(irmStatic[ODIE_STATIC_ONE]);
    for(i=2;i<10;i++) {
      int idx=ODIE_STATIC_ZERO+i;
      irmStatic[idx] = Tcl_NewIntObj(i);
      Tcl_IncrRefCount(irmStatic[idx]);
    }

    irmStatic[ODIE_STATIC_FZERO] = Tcl_NewDoubleObj(0.0);
    Tcl_IncrRefCount(irmStatic[ODIE_STATIC_FZERO]);

    irmStatic[ODIE_STATIC_NEG1] = Tcl_NewIntObj(-1);
    Tcl_IncrRefCount(irmStatic[ODIE_STATIC_NEG1]);

    Tcl_InitHashTable(&constant_strings,TCL_STRING_KEYS);
  }
  
  return TCL_OK;
}
