/***********************************************************************/
/* Open Visualization Data Explorer                                    */
/* (C) Copyright IBM Corp. 1989,1999                                   */
/* ALL RIGHTS RESERVED                                                 */
/* This code licensed under the                                        */
/*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
/***********************************************************************/

#include <dxconfig.h>



#include <string.h>
#include "objectClass.h"
#include "internals.h"


/*
 * Tracing
 */

static int trace = 1;

void
_dxfTraceObjects(int d)
{
    trace = d;
}

#if DEBUGGED
static struct table {			/* table of New/DXDelete per class */
    lock_type lock;			/* lock for the table */
    struct counts {			/* the counts */
	struct object_class *class;	/* class vector */
	int new;			/* how many created */
	int deleted;			/* how many deleted */
    } counts[CLASS_MAX];		/* one per class, in global storage */
} *table;

#define EVERY 500			/* how often to print out */
#endif

#define BITS 24				/* this many bits in tag */
static int tag = 1;			/* per-processor */

Error
_dxf_initobjects(void)
{
#if DEBUGGED
    table = (struct table *) DXAllocateZero(sizeof(struct table));
    if (!table)
	return NULL;
    DXcreate_lock(&table->lock, "object statistics table");
#endif

    return OK;
}



/*
 *
 */

#define PERMANENT 999999999

Object _dxf_SetPermanent(Object o)
{
    o->count = PERMANENT;
    return o;
}


#if DEBUGGED
#define ID DXProcessorId()
#else
#define ID 0
#endif

Object DXReference(Object o)
{
    if (!o)
	return NULL;
    if (o->count==PERMANENT)
	return o;

    /* lock & increment count */
    DXfetch_and_add(&o->count, 1, &o->lock, ID);

    return o;
}


Error
DXDelete(Object o)
{
    int rc = 0, i, n;
    Class class;

    if (!o)
	return OK;
    if (o->count==PERMANENT)
	return OK;

    /* sanity checks - up thru dx 2.1.1, this was if DEBUGGED only */
    if (o->count < 0)
	DXErrorReturn(ERROR_INVALID_DATA,
		    "Object deleted too often! (or not an object)");
    class = DXGetObjectClass(o);
    if ((int)class<=(int)CLASS_MIN || (int)class>=(int)CLASS_MAX) {
	DXSetError(ERROR_INVALID_DATA,
		 "Deleting object of unknown class %d! (or not an object)",
		 class);
	return ERROR;
    }

    /* lock & decrement count */
    if (DXfetch_and_add(&o->count, -1, &o->lock, ID) > 1)
	return OK;

#if DEBUGGED
    /* tracing */
    if (trace>=2)
	DXDebug("Q","deleting object class %s at 0x%x", CLASS_NAME(o->class), o);
    if (trace>=1) {
	int n = (int) CLASS_CLASS(o->class);
	DXlock(&table->lock, 0);
	table->counts[n].deleted += 1;
	DXunlock(&table->lock, 0);
    }
#endif

    /*
     * delete attributes
     * XXX - this should by in an _DeleteObject to be called by subclass
     * i.e. _Delete should be handled like New and DXCopy
     */
    for (i=0, n=o->nattributes; i<n; i++)
	if (o->count != PERMANENT)
	    DXDelete(o->attributes[i].value);
    if (o->attributes!=o->local)
	DXFree((Pointer)o->attributes);

    /* user deletion */
    rc = _dxfDelete(o);

    /* in case we mistakenly delete this object again - same as above;
     *  was only if DEBUGGED up through dx 2.1.1
     */
    o->class_id = CLASS_DELETED;
    o->count = -1;
    o->class = NULL;

    /* finish deleting our stuff */
    DXdestroy_lock(&o->lock);
    DXFree((Pointer)o);
    return rc;
}


Error
DXUnreference(Object o)
{
    int rc = 0, i, n;
    Class class;

    if (!o)
	return NULL;
    if (o->count==PERMANENT || o->count==0)
	return OK;

    /* sanity checks  - same comment as in DXDelete */
    if (o->count < 0)
	DXErrorReturn(ERROR_INVALID_DATA,
		    "Object deleted too often! (or not an object)");
    class = DXGetObjectClass(o);
    if ((int)class<=(int)CLASS_MIN || (int)class>=(int)CLASS_MAX) {
	DXSetError(ERROR_INVALID_DATA,
		 "Deleting object of unknown class %d! (or not an object)",
		 class);
	return NULL;
    }

    /* lock & decrement count */
    DXfetch_and_add(&o->count, -1, &o->lock, ID);

    return OK;
}





Object
_dxf_NewObject(struct object_class *class)
{
    Object o;

    o = (Object) DXAllocate(CLASS_SIZE(class));
    if (!o)
	return NULL;

#if DEBUGGED
    /* tracing */
    if (trace>=2)
	DXDebug("Q", "creating object class %s at 0x%x", CLASS_NAME(class), o);
    if (trace>=1) {
	int n = (int) CLASS_CLASS(class);
	table->counts[n].class = class;
	DXlock(&table->lock, 0);
	table->counts[n].new += 1;
	if (table->counts[n].new%EVERY==0)
	    DXDebug("O", "%d %s objects created, %d deleted, net %d",
		  table->counts[n].new, CLASS_NAME(class),
		  table->counts[n].deleted,
		  table->counts[n].new-table->counts[n].deleted);
	DXunlock(&table->lock, 0);
    }
#endif

    memset(o, 0, CLASS_SIZE(class));
    o->class = class;
    o->class_id = CLASS_CLASS(class);
    DXcreate_lock(&o->lock, "object");
    o->count = 0;
    o->tag = (DXProcessorId()<<BITS) + tag++;
    o->attributes = o->local;
    o->nattributes = 0;
    o->attr_alloc = NATTRIBUTES;
    return o;
}


/*
 * Tags
 */

int
DXGetObjectTag(Object o)
{
    if (!o)
	return 0;
    return o->tag;
}

Object
DXSetObjectTag(Object o, int tag)
{
    if (tag>=0) {
	DXSetError(ERROR_INTERNAL,
		 "tag value %d is illegal: must be less than 0", tag);
	return NULL;
    }
    if (!o)
	return NULL;
    o->tag = tag;
    return o;
}



/*
 * Attributes
 */

Object
DXSetAttribute(Object o, char *name, Object value)
{
    int i, m, n = o->nattributes;
    struct attribute *a;

    if (!o)
	return NULL;

    if (!name)
	DXErrorReturn(ERROR_BAD_PARAMETER, "DXSetAttribute given null name");

    /* don't do quick check: assume usually not there */
    for (i=0, a=o->attributes; i<n; i++, a++)
	if (a->name && strcmp(a->name, name)==0)
	    break;
    
    /* attribute is not there - add it if we are setting a value,
     * or return ok if requesting to delete it.
     */
    if (i >= n) {
	if (!value)
	    return o;
	if (n >= o->attr_alloc) {
	    if (o->attr_alloc==NATTRIBUTES) {
		m = 2*NATTRIBUTES;
		a = (struct attribute *) DXAllocate(m*sizeof(struct attribute));
		if (a)
		    memcpy(a, o->local, sizeof(o->local));
	    } else {
		m = o->attr_alloc*2 + 1;
		a = (struct attribute *) DXReAllocate((Pointer)o->attributes,
					      m * sizeof(struct attribute));
	    }
	    if (!a)
		return NULL;
	    o->attributes = a;
	    o->attr_alloc = m;
	}
	a = o->attributes + n;
	o->nattributes = n+1;
	a->name = _dxfstring(name, 1);
	a->value = NULL;
	
    } else if (!value) {
	
	/* copy remaining attributes down */
	DXDelete(a->value);
	for (i=i+1; i<n; i++)
	    o->attributes[i-1] = o->attributes[i];
	o->nattributes -= 1;
	return o;
	
    }

    /* put value in */
    if (value!=a->value) {		/* MP performance */
	DXReference(value);		/* do first in case value==a->value */
	DXDelete(a->value);
	a->value = value;
    }

    return o;
}


Object
DXDeleteAttribute(Object o, char *name)
{
    int i, m, n = o->nattributes;
    struct attribute *a;

    if (!o)
	return NULL;

    if (!name)
	DXErrorReturn(ERROR_BAD_PARAMETER, 
		      "DXDeleteAttribute given null name");

    /* quick check for pointer equality */
    for (i=0, a=o->attributes; i<n; i++, a++)
	if (a->name==name)
	    break;

    /* now check for string equality */
    if (i >= n) 
	for (i=0, a=o->attributes; i<n; i++, a++)
	    if (a->name && strcmp(a->name, name)==0)
		break;
    
    /* attribute not there, return ok */
    if (i >= n)
	return o;
    
    /* copy remaining attributes down */
    DXDelete(a->value);
    for (i=i+1; i<n; i++)
	o->attributes[i-1] = o->attributes[i];
    o->nattributes -= 1;
    return o;
}


Object
DXGetAttribute(Object o, char *name)
{
    int i, n;
    struct attribute *a;

    if (!o)
	return NULL;
    if (!name)
	DXErrorReturn(ERROR_BAD_PARAMETER, "DXGetAttribute given null name");

    /* quick check for pointer equality */
    n = o->nattributes;
    for (i=0, a=o->attributes; i<n; i++, a++)
	if (a->name==name)
	    break;

    /* now check for string equality */
    if (i >= n) 
	for (i=0, a=o->attributes; i<n; i++, a++)
	    if (a->name && strcmp(a->name, name)==0)
		break;
    
    /* no */
    if (i >= n)
	return NULL;

    /* yes */
    return a->value;
}


Object
DXGetEnumeratedAttribute(Object o, int n, char **name)
{
    struct attribute *a;
    if (!o)
	return NULL;
    if (n >= o->nattributes)
	return NULL;
    a = o->attributes + n;
    if (name)
	*name = a->name;
    return a->value;
}

Object
DXSetFloatAttribute(Object o, char *name, double x)
{
    Array a;
    float *p;
    a = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 0);
    p = (float *)DXGetArrayData(DXAddArrayData(a, 0, 1, NULL));
    if (!p)
	return NULL;
    *p = x;
    if (!DXSetAttribute(o, name, (Object)a)) {
	DXDelete((Object)a);
	return NULL;
    }
    return o;
}

Object
DXSetIntegerAttribute(Object o, char *name, int x)
{
    Array a;
    int *p;
    a = DXNewArray(TYPE_INT, CATEGORY_REAL, 0);
    p = (int *)DXGetArrayData(DXAddArrayData(a, 1, 0, NULL));
    if (!p)
	return NULL;
    *p = x;
    if (!DXSetAttribute(o, name, (Object)a)) {
	DXDelete((Object)a);
	return NULL;
    }
    return o;
}

Object
DXSetStringAttribute(Object o, char *name, char *x)
{
    String s;
    s = DXNewString(x);
    if (!s)
	return NULL;
    if (!DXSetAttribute(o, name, (Object)s)) {
	DXDelete((Object)s);
	return NULL;
    }
    return o;
}

Object
DXGetFloatAttribute(Object o, char *name, float *x)
{
    Array a;
    float *p;

    if (!(a = (Array)DXGetAttribute(o, name)))
	return NULL;

    if (DXGetObjectClass((Object)a) != CLASS_ARRAY)
	return NULL;
    
    if (!DXTypeCheck(a, TYPE_FLOAT, CATEGORY_REAL, 0))
	return NULL;

    if (!(p = (float *)DXGetArrayData(a)))
	return NULL;

    if (x)
	*x = *p;

    return o;
}

Object
DXGetIntegerAttribute(Object o, char *name, int *x)
{
    Array a;
    int *p;

    if (!(a = (Array)DXGetAttribute(o, name)))
	return NULL;

    if (DXGetObjectClass((Object)a) != CLASS_ARRAY)
	return NULL;
    
    if (!DXTypeCheck(a, TYPE_INT, CATEGORY_REAL, 0))
	return NULL;

    if (!(p = (int *)DXGetArrayData(a)))
	return NULL;

    if (x)
	*x = *p;

    return o;
}


Object
DXGetStringAttribute(Object o, char *name, char **x)
{
    String s;

    if (!(s = (String)DXGetAttribute(o, name)))
	return NULL;

    if (DXGetObjectClass((Object)s) != CLASS_STRING)
	return NULL;
    
    if (x)
	*x = DXGetString(s);

    return o;
}


Object
DXCopyAttributes(Object dst, Object src)
{
    struct attribute *a;
    int i;
    if (!dst)
	return NULL;
    if (!src)
	return dst;
    for (i=0, a=src->attributes; i<src->nattributes; i++, a++)
	if (!DXSetAttribute(dst, a->name, a->value))
	    return NULL;
    return dst;
}


/*
 * Copying
 * XXX - why is copying attributes done both here and in DXCopyAttributes?
 */

Object
_dxf_CopyObject(Object new, Object old, enum copy copy)
{
    int i, n = old->nattributes;
    struct attribute *oa, *na;

    /*
     * DXCopy the attributes, no matter what the type of copy
     */

    /* allocate attributes */
    if (n<=NATTRIBUTES) {
	na = new->local;
	new->attr_alloc = NATTRIBUTES;
    } else {
	na = (struct attribute *) DXAllocate(n * sizeof(struct attribute));
	if (!na)
	    return NULL;
	new->attr_alloc = n;
    }
    new->attributes = na;
    new->nattributes = n;

    /* fill in attributes */
    for (i=0, oa=old->attributes; i<n; i++, oa++, na++) {
	na->name = oa->name;
	na->value = DXReference(oa->value);
    }
    return new;
}

Object
DXGetType(Object a, Type* b, Category* c, int* d, int* e)
{
    return _dxfGetType((Object)a,b,c,d,e);
}

Object
DXCopy(Object a, enum copy b)
{
    return _dxfCopy((Object)a,b);
}

Error
_dxfObject_Delete(Object o)
{
    return OK;
}

Object
_dxfObject_BoundingBox(Object o, Point *p, Matrix *m, int valid)
{
    return NULL;
}

Error
_dxfObject_Shade(Object o, struct shade *s)
{
    DXSetError(ERROR_BAD_CLASS, "#13880", CLASS_NAME(o->class));
    return ERROR;
}
