/*************************************************************************/
/*                                                                       */
/*                  Language Technologies Institute                      */
/*                     Carnegie Mellon University                        */
/*                        Copyright (c) 1999                             */
/*                        All Rights Reserved.                           */
/*                                                                       */
/*  Permission is hereby granted, free of charge, to use and distribute  */
/*  this software and its documentation without restriction, including   */
/*  without limitation the rights to use, copy, modify, merge, publish,  */
/*  distribute, sublicense, and/or sell copies of this work, and to      */
/*  permit persons to whom this work is furnished to do so, subject to   */
/*  the following conditions:                                            */
/*   1. The code must retain the above copyright notice, this list of    */
/*      conditions and the following disclaimer.                         */
/*   2. Any modifications must be clearly marked as such.                */
/*   3. Original authors' names are not deleted.                         */
/*   4. The authors' names are not used to endorse or promote products   */
/*      derived from this software without specific prior written        */
/*      permission.                                                      */
/*                                                                       */
/*  CARNEGIE MELLON UNIVERSITY AND THE CONTRIBUTORS TO THIS WORK         */
/*  DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING      */
/*  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT   */
/*  SHALL CARNEGIE MELLON UNIVERSITY NOR THE CONTRIBUTORS BE LIABLE      */
/*  FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES    */
/*  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN   */
/*  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,          */
/*  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF       */
/*  THIS SOFTWARE.                                                       */
/*                                                                       */
/*************************************************************************/
/*             Author:  Alan W Black (awb@cs.cmu.edu)                    */
/*               Date:  December 1999                                    */
/*************************************************************************/
/*                                                                       */
/*  Items (and Item Contents)                                            */
/*                                                                       */
/*************************************************************************/
#include "cst_alloc.h"
#include "cst_item.h"
#include "cst_relation.h"
#include "cst_utterance.h"

/* Define functions for using items, rels and utts as vals */
CST_VAL_REGISTER_TYPE_NODEL(relation,cst_relation)
CST_VAL_REGISTER_TYPE_NODEL(item,cst_item)
CST_VAL_REGISTER_TYPE(utterance,cst_utterance)
CST_VAL_REGISTER_FUNCPTR(itemfunc,cst_itemfunc)

cst_item *new_item_relation(cst_relation *r,cst_item *i)
{
    cst_item *ni;

    ni = cst_utt_alloc(r->utterance, cst_item, 1);
    ni->contents = 0;
    ni->n = ni->p = ni->u = ni->d = 0;
    ni->relation = r;
    item_contents_set(ni,i);
    return ni;
}

void item_contents_set(cst_item *current, cst_item *i)
{
    cst_item_contents *c = 0;
    cst_item *nn_item;
    if (i == 0)
	c = new_item_contents(current);
    else
	c = i->contents;
    if (c != current->contents)
    {
	item_unref_contents(current);
	current->contents = c;
	/* If this contents is already in this relation         */
	/* empty the other reference                            */
	if (feat_present(current->contents->relations,current->relation->name))
	{   /* oops this is already in this relation */
	    nn_item = val_item(feat_val(current->contents->relations,
					current->relation->name));
	    feat_set(nn_item->contents->relations,
		     current->relation->name,
		     item_val(nn_item));
	       
	}
	/* Add back reference */
	feat_set(current->contents->relations,
		 current->relation->name,
		 item_val(current));
    }
}

void delete_item(cst_item *item)
{
    cst_item *ds, *nds;

    if (item->n != NULL) 
    { 
	item->n->p = item->p;
	item->n->u = item->u;  /* in trees if this is first daughter */
    }
    if (item->p != NULL) item->p->n = item->n;
    if (item->u != NULL) item->u->d = item->n; /* when first daughter */
    
    if (item->relation)
    {
	if (item->relation->head == item)
	    item->relation->head = item->n;
	if (item->relation->tail == item)
	    item->relation->tail = item->p;
    }

    /* Delete all the daughters of item */
    for (ds = item->d; ds; ds=nds)
    {
	nds = ds->n;
	delete_item(ds);
    }
    
    item_unref_contents(item);
    cst_utt_free(item->relation->utterance, item);
}

void item_unref_contents(cst_item *item)
{
    /* unreference this item from contents, and delete contents */
    /* if no one else is referencing it                         */

    if (item && item->contents)
    {
	feat_remove(item->contents->relations,item->relation->name);
	if (feat_length(item->contents->relations) == 0)
	{
	    delete_features(item->contents->relations);
	    delete_features(item->contents->features);
	    cst_utt_free(item->relation->utterance,item->contents);
	}
	item->contents = NULL;
    }
}

cst_item_contents *new_item_contents(cst_item *i)
{
    cst_item_contents *ic;

    ic = cst_utt_alloc(i->relation->utterance,cst_item_contents,1);
    ic->features = new_features_local(i->relation->utterance->ctx);
    ic->relations = new_features_local(i->relation->utterance->ctx);

    return ic;
}

cst_item *item_as(const cst_item *i,const char *rname)
{
    /* return i as relation rname or null */
    const cst_val *v;

    if (i == NULL)
	return NULL;
    else 
    {
        v = feat_val(i->contents->relations,rname);
        if (v != NULL)
            return val_item(v);
        else
            return NULL;
    }
}

/********************************************************************/
/*    List relation related functions                               */
/********************************************************************/

cst_item *item_next(const cst_item *i)
{
    if (i == NULL)
	return NULL;
    else
	return i->n;
}

cst_item *item_prev(const cst_item *i)
{
    if (i == NULL)
	return NULL;
    else
	return i->p;
}

cst_item *item_append(cst_item *current, cst_item *ni)
{
    cst_item *rni = 0;

    if (ni && (ni->relation == current->relation))
    {
	/* got to delete it first as an item can't be in a relation twice */
    }
    else
	rni = new_item_relation(current->relation,ni);

    rni->n = current->n;
    if (current->n != NULL)
	current->n->p = rni;
    rni->p = current;
    current->n = rni;

    if (current->relation->tail == current)
	current->relation->tail = rni;

    return rni;
}

cst_item *item_prepend(cst_item *current, cst_item *ni)
{
    cst_item *rni = 0;

    if (ni && (ni->relation == current->relation))
    {
	/* got to delete it first as an item can't be in a relation twice */
    }
    else
	rni = new_item_relation(current->relation,ni);

    rni->p = current->p;
    if (current->p != NULL)
	current->p->n = rni;
    rni->n = current;
    current->p = rni;

    if (current->u)  /* in a tree */
    {
	current->u->d = rni;
	rni->u = current->u;
	current->u = NULL;
    }

    if (current->relation->head == current)
	current->relation->head = rni;

    return rni;
}

/********************************************************************/
/*    Tree relation related functions                               */
/********************************************************************/
cst_item *item_parent(const cst_item *i)
{
    const cst_item *n;

    for (n=i; item_prev(n); n=item_prev(n));
    if (n == NULL)
	return NULL;
    else 
	return n->u;
}

cst_item *item_daughter(const cst_item *i)
{
    if (i == NULL)
	return NULL;
    else 
	return i->d;
}

cst_item *item_nth_daughter(const cst_item *i,int n)
{
    int d;
    cst_item *p;

    for (d=0,p=item_daughter(i); p && (d < n); p=item_next(p),d++);
    return p;
}

cst_item *item_last_daughter(const cst_item *i)
{
    cst_item *p;

    for (p=item_daughter(i); item_next(p); p=item_next(p));
    return p;
}

cst_item *item_first(const cst_item *i)
{
    cst_item *p;

    for (p=(cst_item *)(void *)i; item_prev(p); p=item_prev(p));
    return p;
}

cst_item *item_last(const cst_item *i)
{
    cst_item *p;

    for (p=(cst_item *)(void *)i; item_next(p); p=item_next(p));
    return p;
}

cst_item *item_add_daughter(cst_item *i,cst_item *nd)
{
    cst_item *p,*rnd;

    p = item_last_daughter(i);

    if (p)
	rnd=item_append(p,nd);
    else
    {   /* first new daughter */
	if (nd && (nd->relation == i->relation))
	{
	    /* got to delete it first as nd can't be in a relation twice */
	    cst_errmsg("item_add_daughter: already in relation\n");
	    return 0;
	}
	else
	    rnd = new_item_relation(i->relation,nd);

	rnd->u = i;
	i->d = rnd;
    }

    return rnd;
}

/********************************************************************/
/*    Feature functions                                             */
/********************************************************************/

int item_feat_present(const cst_item *i,const char *name)
{
    return feat_present(item_feats(i),name);
}

int item_feat_remove(const cst_item *i,const char *name)
{
    return feat_remove(item_feats(i),name);
}

cst_features *item_feats(const cst_item *i)
{
    return (i ? i->contents->features : NULL);
}

const cst_val *item_feat(const cst_item *i,const char *name)
{
    return feat_val(item_feats(i),name);
}

int item_feat_int(const cst_item *i,const char *name)
{
    return feat_int(item_feats(i),name);
}

float item_feat_float(const cst_item *i,const char *name)
{
    return feat_float(item_feats(i),name);
}

const char *item_feat_string(const cst_item *i,const char *name)
{
    return feat_string(item_feats(i),name);
}

void item_set(const cst_item *i,const char *name,const cst_val *val)
{
    feat_set(item_feats(i),name,val);
}

void item_set_int(const cst_item *i,const char *name,int val)
{
    feat_set_int(item_feats(i),name,val);
}

void item_set_float(const cst_item *i,const char *name,float val)
{
    feat_set_float(item_feats(i),name,val);
}

void item_set_string(const cst_item *i,const char *name,const char *val)
{
    feat_set_string(item_feats(i),name,val);
}

cst_utterance *item_utt(const cst_item *i)
{
    if (i && i->relation)
	return i->relation->utterance;
    else
	return NULL;
}

int item_equal(const cst_item *a, const cst_item *b)
{
    if ((a == b) ||
	(a && b && (a->contents == b->contents)))
	return TRUE;
    else
	return FALSE;
}
