/*
 * DBprop.c --
 *
 * Implement properties on database cells.  Properties are name-value pairs
 * and provide a flexible way of extending the data that is stored in a
 * CellDef.  Maybe in the future properties will be added to other database
 * objects.
 *
 *     *********************************************************************
 *     * Copyright (C) 1985, 1990 Regents of the University of California. *
 *     * Permission to use, copy, modify, and distribute this              *
 *     * software and its documentation for any purpose and without        *
 *     * fee is hereby granted, provided that the above copyright          *
 *     * notice appear in all copies.  The University of California        *
 *     * makes no representations about the suitability of this            *
 *     * software for any purpose.  It is provided "as is" without         *
 *     * express or implied warranty.  Export of this software outside     *
 *     * of the United States of America may require an export license.    *
 *     *********************************************************************
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/database/DBprop.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <string.h>
#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "utils/malloc.h"

/* Global variable */

bool DBPropCompat = TRUE;	/* If TRUE, then always save properties to
				 * .mag files as type "string" for backwards
				 * compatibility.  If FALSE, then properties
				 * are saved to the .mag file along with their
				 * type.  Regardless of the setting, properties
				 * which are reserved keywords are converted
				 * to the best internal representation on input.
				 */

/* ----------------------------------------------------------------------------
 *
 * DBPropPut --
 *
 * Put a property onto a celldef.
 *
 * Results:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

void
DBPropPut(cellDef, name, value)
    CellDef *cellDef;		/* Pointer to definition of cell. */
    char *name;			/* The name of the property desired. */
    PropertyRecord *value;	/* MUST point to a malloc'ed structure, or NULL.
				 * This will be freed when the CellDef is freed.
				 */

{
    HashTable *htab;
    HashEntry *entry;
    PropertyRecord *oldvalue;

    /* Honor the NOEDIT flag.  Note that the caller always assumes that */
    /* the value would be saved in the hash table, so if it is not	*/
    /* being used, then it must be freed here.				*/

    if (cellDef->cd_flags & CDNOEDIT)
    {
	if (value != (ClientData)NULL)
	    freeMagic((char *)value);
	return;
    }

    if (cellDef->cd_props == (ClientData) NULL)
    {
	cellDef->cd_props = (ClientData) mallocMagic(sizeof(HashTable));
	HashInit( (HashTable *) cellDef->cd_props, 8, 0);
    }
    htab = (HashTable *) cellDef->cd_props;

    /* Special handling of FIXED_BBOX, which uses CDFIXEDBBOX as a quick lookup */
    if (!strcmp(name, "FIXED_BBOX"))
    {
	if (value == (ClientData)NULL)
	    cellDef->cd_flags &= ~CDFIXEDBBOX;
	else
	    cellDef->cd_flags |= CDFIXEDBBOX;
    }

    /* Special handling of GDS_FILE, which uses CDVENDORGDS as a quick lookup */
    if (!strcmp(name, "GDS_FILE"))
    {
	if (value == (ClientData)NULL)
	    cellDef->cd_flags &= ~CDVENDORGDS;
	else
	    cellDef->cd_flags |= CDVENDORGDS;
    }

    entry = HashFind(htab, name);
    oldvalue = (PropertyRecord *)HashGetValue(entry);
    /* All properties are allocated as a single block and can just be freed,
     * except for plane properties, which require freeing the plane.
     */
    if (oldvalue != NULL)
    {
	if (oldvalue->prop_type == PROPERTY_TYPE_PLANE)
	{
	    DBFreePaintPlane(oldvalue->prop_value.prop_plane);
	    TiFreePlane(oldvalue->prop_value.prop_plane);
	}
	freeMagic((char *)oldvalue);
    }
    if (value == (PropertyRecord *)NULL)
	HashRemove(htab, name);
    else
	HashSetValue(entry, PTR2CD(value));
}

/* ----------------------------------------------------------------------------
 *
 * DBPropGet --
 *
 * Get a property from a celldef.
 *
 * Results:
 *	NULL if the property didn't exist, or if the property record was NULL.
 *	Otherwise, returns a pointer to the property record.
 *
 * ----------------------------------------------------------------------------
 */

PropertyRecord *
DBPropGet(cellDef, name, found)
    CellDef *cellDef;	/* Pointer to definition of cell. */
    char *name;		/* The name of the property desired. */
    bool *found;	/* If not NULL, filled in with TRUE iff the property
			 * exists.
			 */
{
    PropertyRecord *result;
    bool haveit;
    HashTable *htab;
    HashEntry *entry;

    result = (PropertyRecord *)NULL;
    haveit = FALSE;
    htab = (HashTable *) cellDef->cd_props;
    if (htab == (HashTable *) NULL) goto done;

    entry = HashLookOnly(htab, name);
    if (entry != NULL)
    {
	haveit = TRUE;
	result = (PropertyRecord *)HashGetValue(entry);
    }

done:
    if (found != (bool *) NULL) *found = haveit;
    return result;
}

/* ----------------------------------------------------------------------------
 *
 * DBPropGetString --
 *
 * Get a string property from a celldef.
 *
 * Results:
 *	NULL if the property didn't exist, or if the property record was NULL.
 *	Otherwise, returns a pointer to the property's string record.
 *
 * Notes:
 *	This is basically the original DBPropGet(), when properties were only
 *	allowed to be strings.
 *
 * ----------------------------------------------------------------------------
 */

char *
DBPropGetString(cellDef, name, found)
    CellDef *cellDef;	/* Pointer to definition of cell. */
    char *name;		/* The name of the property desired. */
    bool *found;	/* If not NULL, filled in with TRUE iff the property
			 * exists.
			 */
{
    char *result = NULL;
    PropertyRecord *proprec;
    bool haveit;
    HashTable *htab;
    HashEntry *entry;

    haveit = FALSE;
    htab = (HashTable *) cellDef->cd_props;
    if (htab == (HashTable *) NULL) goto pdone;

    entry = HashLookOnly(htab, name);
    if (entry != NULL)
    {
	proprec = (PropertyRecord *)HashGetValue(entry);
	if (proprec->prop_type == PROPERTY_TYPE_STRING)
	{
	    haveit = TRUE;
	    result = proprec->prop_value.prop_string;
	}
    }

pdone:
    if (found != (bool *) NULL) *found = haveit;
    return result;
}

/* ----------------------------------------------------------------------------
 *
 * DBPropGetDouble --
 *
 * Get a single double-long integer property from a celldef.
 *
 * Results:
 *	NULL if the property didn't exist, or if the property record was NULL.
 *	Otherwise, returns a pointer to the property's value record.
 *
 * ----------------------------------------------------------------------------
 */

dlong
DBPropGetDouble(cellDef, name, found)
    CellDef *cellDef;	/* Pointer to definition of cell. */
    char *name;		/* The name of the property desired. */
    bool *found;	/* If not NULL, filled in with TRUE iff the property
			 * exists.
			 */
{
    dlong result = 0;
    PropertyRecord *proprec;
    bool haveit;
    HashTable *htab;
    HashEntry *entry;

    haveit = FALSE;
    htab = (HashTable *) cellDef->cd_props;
    if (htab == (HashTable *) NULL) goto ddone;

    entry = HashLookOnly(htab, name);
    if (entry != NULL)
    {
	proprec = (PropertyRecord *)HashGetValue(entry);
	if (proprec->prop_type == PROPERTY_TYPE_DOUBLE)
	{
	    haveit = TRUE;
	    result = proprec->prop_value.prop_double[0];
	}
	else if (proprec->prop_type == PROPERTY_TYPE_STRING)
	{
	    haveit = TRUE;
	    sscanf(proprec->prop_value.prop_string, "%"DLONG_PREFIX"d", &result);
	}
    }

ddone:
    if (found != (bool *) NULL) *found = haveit;
    return result;
}

/* ----------------------------------------------------------------------------
 *
 * DBPropEnum --
 *
 * Enumerate all the properties on a cell.
 *
 * Results:
 *	0 if the search completed, else whatever value was returned by the
 *	called proc.
 *
 * Side effects:
 *	Depends on the called proc.
 * ----------------------------------------------------------------------------
 */

int
DBPropEnum(cellDef, func, cdata)
    CellDef *cellDef;	/* Pointer to definition of cell. */
    int (*func)();	/* Function of the form:
			 *
			 *	int foo(name, value, cdata)
			 *	    char *name;
			 *	    PropertyRecord *value;
			 *	    ClientData cdata;
			 *	{
			 *	    -- return 0 to continue,
			 *	    -- nonzero to abort.
			 *	    return result;
			 *	}
			 */
    ClientData	cdata;
{
    HashTable *htab;
    HashSearch hs;
    HashEntry *entry;
    int res;

    if (cellDef->cd_props == (ClientData) NULL) return 0;
    htab = (HashTable *) cellDef->cd_props;

    HashStartSearch(&hs);
    while ((entry = HashNext(htab, &hs)) != NULL)
    {
	res = (*func)(entry->h_key.h_name, (PropertyRecord *)entry->h_pointer, cdata);
	if (res != 0) return res;
    }

    return 0;
}


/* ----------------------------------------------------------------------------
 *
 * DBPropClearAll --
 *
 * Free up all properties and associated storage for a CellDef.
 *
 * Results:
 *	none.
 *
 * Side effects:
 *	Frees up storage, even for the property table itself.
 * ----------------------------------------------------------------------------
 */

void
DBPropClearAll(cellDef)
    CellDef *cellDef;	/* Pointer to definition of cell. */
{
    HashTable *htab;
    HashSearch hs;
    HashEntry *entry;

    if (cellDef->cd_props == (ClientData) NULL) return;
    htab = (HashTable *) cellDef->cd_props;

    HashStartSearch(&hs);
    while ((entry = HashNext(htab, &hs)) != NULL)
    {
	if (entry->h_pointer != NULL) freeMagic((char *) entry->h_pointer);
	HashSetValue(entry, NULL);
    }

    HashKill(htab);
    freeMagic((char *) htab);
    cellDef->cd_props = (ClientData) NULL;

    /* Since CDFIXEDBBOX requires a FIXED_BBOX property, clearing all	*/
    /* properties necessarily means this flag must be clear.		*/
    cellDef->cd_flags &= ~CDFIXEDBBOX;
}
