/* -*- Mode: c++ -*-
 *  $Id: s.xotclMetaData.c 1.2 01/02/22 20:35:45-00:00 neumann $
 *  
 *  Extended Object Tcl (XOTcl)
 *
 *  Copyright (C) 1999-2000 Gustaf Neumann, Uwe Zdun
 *
 *
 *  xotclReference.c --
 *  
 *  XOTcl Object References
 *  
 */

#include "xotclInt.h"

#ifdef XOTCL_METADATA
/*
 * Meta Data
 */

void
XOTclMetaDataRemoveDepending(XOTclClass* cl, char* name) {
  XOTclClasses *saved = cl->order, *clPtr;
  cl->order = 0;
  clPtr = XOTclComputeDependents(cl);

  while (clPtr != 0) {
      Tcl_HashSearch hSrch;
      Tcl_HashEntry* hPtr = &clPtr->cl->instances ?
	Tcl_FirstHashEntry(&clPtr->cl->instances, &hSrch) : 0;
      for (; hPtr != 0; hPtr = Tcl_NextHashEntry(&hSrch)) {
	  XOTclObject* obj = (XOTclObject*)
	    Tcl_GetHashKey(&clPtr->cl->instances, hPtr);
	  Tcl_HashEntry* h1Ptr = Tcl_FindHashEntry(&obj->metaData, name);
	  if (h1Ptr) {
	      Tcl_DeleteHashEntry(h1Ptr);
	  }
      }
      clPtr = clPtr->next;
  }

  XOTclRemoveClasses(cl->order); 
  cl->order = saved;
}

int
XOTclMetaDataInheritance (XOTclObject* obj, char* name) {
    XOTclClasses *clPtr;
    assert(obj);

    if (!obj->cl->order)
	obj->cl->order = XOTclComputePrecedence(obj->cl);
    clPtr = obj->cl->order;
    if (clPtr) {
      while (clPtr != 0) {
	  if (Tcl_FindHashEntry(&clPtr->cl->object.metaData, name)) {
	      return 1;
	  }
	  clPtr = clPtr->next;
      }
    }
    return 0;
}

void
XOTclMetaDataDestroy(XOTclObject* obj) {
  Tcl_DeleteHashTable(&obj->metaData);
}

void
XOTclMetaDataInit(XOTclObject* obj) {
  Tcl_InitHashTable(&obj->metaData, TCL_STRING_KEYS);
}

int
XOTclOMetaDataMethod (ClientData cd, Tcl_Interp* in, int objc, Tcl_Obj *objv[]) {
  XOTclObject* obj = XOTclIsObject(in, cd);
  XOTclClass* cl = XOTclIsClass(in, cd);
  char* option; int add = -1;
  int result = TCL_OK;
  int oc; Tcl_Obj** ov; int i;

  if (!obj) return XOTclObjErrType(in, obj->cmdName, "Object");
  if (objc < 2)
    return XOTclObjErrArgCnt(in,obj->cmdName,
				  "metadata ?(add|remove)? metaDataList");

  option = ObjStr(objv[1]);
  switch (*option) {
  case 'a':
    if (strcmp(option,"add") == 0) add = 1; break;
  case 'r':
    if (strcmp(option,"remove") == 0) add = 0; break;
  }
  if (add == -1) {
    if (objc == 2) {
      Tcl_HashEntry* hPtr = Tcl_FindHashEntry(&obj->metaData, option);
      if (hPtr) {
	Tcl_Obj* entry = (Tcl_Obj *) Tcl_GetHashValue(hPtr);
	if (entry) {
	  Tcl_SetObjResult(in, (Tcl_Obj *) Tcl_GetHashValue(hPtr));
	} else {
	  Tcl_ResetResult(in);
	}
	return TCL_OK;	
      }
    }
    if (objc == 3) {
      Tcl_HashEntry* hPtr = Tcl_FindHashEntry(&obj->metaData, option);
      if (!hPtr) {
	int nw;
	if (XOTclMetaDataInheritance(obj, option)) {
	  hPtr = Tcl_CreateHashEntry(&obj->metaData, option, &nw);
	  if (!nw)
	    return XOTclVarErrMsg(in,
				       "MetaData: Can't create MetaData Entry: ",
				       option, (char*) NULL);
	}
      }	
      if (hPtr) {
	Tcl_Obj* entry = (Tcl_Obj *) Tcl_GetHashValue(hPtr);
	if (entry)
	  DECR_REF_COUNT(entry);
	INCR_REF_COUNT(objv[2]);
	Tcl_SetHashValue(hPtr, (ClientData) objv[2]);	
	return TCL_OK;
      }
    }
    return XOTclVarErrMsg(in,"MetaData: Unknown option;  given Option: ",
			  option, (char*) NULL);
  }

  if (Tcl_ListObjGetElements(in, objv[2], &oc, &ov) == TCL_OK) {
    for (i = 0; i < oc; i ++) {
      char* value = ObjStr (ov[i]);
      if (add) {	
	int nw;
	Tcl_HashEntry* hPtr = Tcl_FindHashEntry(&obj->metaData, value);
	if (hPtr)
	  return XOTclVarErrMsg(in,
				     "Can't add MetaData, MetaData exists: ",
				     value,
				     (char*) NULL);	
	hPtr = Tcl_CreateHashEntry(&obj->metaData, value, &nw);
	if (!nw)
	  return XOTclVarErrMsg(in,
				     "MetaData: Can't create MetaData Entry: ",
				     value,(char*) NULL);
      } else {
	Tcl_HashEntry* hPtr = Tcl_FindHashEntry(&obj->metaData, value);
	if (hPtr) {		
	  Tcl_DeleteHashEntry(hPtr);
	  if (cl) {
	    XOTclMetaDataRemoveDepending(cl, value);
	  }
	}
      }
    }
  }

  return result;
}
#endif
