/* Copyright (c) 1993 by Sanjay Ghemawat */
#include <assert.h>
#include <stdio.h>
#include <string.h>

#include "object.h"
#include "misc_tcl.h"

extern "C" {
    struct Tcl_HashEntry;
    struct Tcl_HashTable;
#include <tclHash.h>
}

static int obj_handle(ClientData, Tcl_Interp*, int, char*[]);

/*
 * Handle -> Object map.
 */
static Tcl_HashTable objectMap;
static int objectMapInited = 0;

Object::Object(Tcl_Interp* t, char const* type) {
    /* Generate handle */
    static int index = 0;
    char buffer[100];
    index++;
    sprintf(buffer, "h%d", index);
    init(t, type, buffer);
}

Object::Object(Tcl_Interp* t, char const* type, char const* h) {
    init(t, type, h);
}

void Object::init(Tcl_Interp* t, char const* ty, char const* h) {
    objtype = new char[strlen(ty)+1];
    strcpy(objtype, ty);

    name = new char[strlen(h)+1];
    strcpy(name, h);

    interp = t;

    /* Create TCL Command */
    Tcl_CreateCommand(interp, name, obj_handle, (ClientData) this, NULL);

    /* Add entry to object map */
    if (! objectMapInited) {
	objectMapInited = 1;
	Tcl_InitHashTable(&objectMap, TCL_STRING_KEYS);
    }
    int newentry;
    Tcl_HashEntry* entry = Tcl_CreateHashEntry(&objectMap, name, &newentry);
    assert(newentry);
    Tcl_SetHashValue(entry, (ClientData)this);
}

Object::~Object() {
    Tcl_HashEntry* entry = Tcl_FindHashEntry(&objectMap, name);
    assert((entry != 0) && (Tcl_GetHashValue(entry) == ((ClientData) this)));
    Tcl_DeleteHashEntry(entry);

    Tcl_DeleteCommand(interp, name);

    delete [] objtype;
    delete [] name;
}

Object* Object::find(char const* handle) {
    Tcl_HashEntry* entry = Tcl_FindHashEntry(&objectMap, (char*) handle);
    return ((entry == 0) ? 0 : ((Object*) Tcl_GetHashValue(entry)));
}

int Object::method(int, char*[]) {
    TCL_Error(interp, "Object has no methods");
}

int obj_handle(ClientData c, Tcl_Interp* tcl, int argc, char* argv[]) {
    Object* object = (Object*) c;

    assert(object->tcl() == tcl);
    return object->method(argc, argv);
}
