// Copyright (C) 1996 DIMACS Center, Rutgers, The State University of New Jersey
// Author(s): Jonathan Berry

// This software is copyrighted by the DIMACS Center at Rutgers, The State
// University of New Jersey.  IT IS PROVIDED AS IS, AND THE AUTHORS, DIMACS, AND
// RUTGERS, THE STATE UNIVERSITY OF NEW JERSEY  DISCLAIM
// ALL LIABILITY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
// DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

// THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
// IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
// NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
// MODIFICATIONS.

// The authors hereby grant permission to use, copy, modify, distribute,
// and license this software and its documentation for any purpose, provided
// that existing copyright notices are retained in all copies and that this
// notice is included verbatim in any distributions. No written agreement,
// license, or royalty fee is required for any of the authorized uses.
// Modifications to this software may be copyrighted by their authors
// and need not follow the licensing terms described here, provided that
// the new terms are clearly indicated on the first page of each file where
// they apply.

// Last File Update: 31-Jul-1996
// 

///////////////////////////////////////////////////////////////////////////
// LINK: Generic Graph Tool and Class Library
//
//      Function name: Attribute
//
//      Synopsis:
//	    GraphObjects contain pointers to permanent and temporary attributes
//
//      Description:
//
//      Creation: 1993 April 1, Patricia K. Fasel, pkf@lanl.gov
//
//      Routines used:
//
//      Related files:
//
//      Test suite:
//
//      User documentation:
//
//      Development History:
//	    93-04-22	pkf	removed IntAttr, ObjectAttr, use templates
//	    93-10-15	pkf	added attrManager as part of scoping
//				added setAttr() and getAttr() template functions
//	    93-10-21	pkf	added editAttr(), newAttr(), resetAttr()
//				displayAttr(), printAttr(), deleteAttr()
//	    94-07-04	pkf	removed AttrManager, AttrInfo
//
//      Testing History:
//
//      Code Review:
//
//      Bugs and Deficiencies:
//
//////////////////////////////////////////////////////////////////////////////

#include <LINK/graph/Attribute.h>
#include <iostream.h>
#include <string.h>
#include <fstream.h>
#include <LINK/graph/Graph.h>
#include <LINK/basic/general.h>


//
// constructor for a default attribute will be added to Graph* attribute list
// if an attribute is declared globally and redeclared locally, not added
// so when it goes out of scope it can't be deleted from the defaults
//
template <class Item>
//Attribute<Item>::Attribute(Graph* g, String n,const Item& v,int static_flag): 
//                        AttributeBase(strcpy(new char[strlen(n)+1], n), 
//				      g, static_flag), _value(v)
Attribute<Item>::Attribute(Graph* g, String n, const Item& v): 
                        AttributeBase(strcpy(new char[strlen(n)+1], n), 
				      g), _value(v)
{
    AttributeBase* attribute = g->getAttr(n);
    if (!attribute) {
        g->addAttr((AttributeBase*) this);
	if (strncmp(n, "x_", 2) != 0)
	    resetAttribute(g, n);
	_graph = g;
    } else
	warning("attempt to redefine attribute");
}


//
// constructor for a regular GraphObject Attribute
//
template <class Item>
//Attribute<Item>::Attribute(String n, const Item& v, int static_flag) : 
//                        AttributeBase(strcpy(new char[strlen(n)+1], n), 
//				      (Graph*)0, static_flag), _value(v)
Attribute<Item>::Attribute(String n, const Item& v) : 
                        AttributeBase(strcpy(new char[strlen(n)+1], n)), 
				      _value(v)
{
        //cout << "trying to make: " << n << " " << v << endl;
}



template <class Item>
Attribute<Item>::~Attribute()
{
    if (_graph) {
	// delete all copies in the graph objects before removing main attr
	if (strncmp(_name, "x_", 2) != 0)
	    resetAttribute(_graph, _name);
	_graph->removeAttr(this);
    }
    delete _name;
}


//
// return a copy of itself
//
template <class Item>
AttributeBase*
Attribute<Item>::copy()
{
    //if (!_name)
    //	cout << "Attr:copy() empty string error" << endl;
    //assert(_name);
    Attribute<Item>* a = new Attribute<Item>(_name, _value);
    return (AttributeBase*) a;
}


//
// return the value of the attribute as a string
//
template<class Item>
ostream&
Attribute<Item>::display(ostream& stream) const
{
    displayItem(stream, _value);
    //stream << _value;
    return stream;
}


//
// templated function which creates a new attribute and adds to Graph attributes
//
template <class Item>
int
//newAttribute(Graph* graph, String name, const Item& value, int static_flag)
newAttribute(Graph* graph, String name, const Item& value)
{
    if (!graph) {
	warning("newAttribute called for null object\n");
	return 0;
    }
    //Attribute<Item>* attribute = new Attribute<Item>(graph, name, value,
    //						     static_flag);
    Attribute<Item>* attribute = new Attribute<Item>(graph, name, value);
    if (attribute)
	return LINK_OK;
    else {
	error(LINK_NEW_ATTR_FAIL);
	error(name);
	return LINK_NEW_ATTR_FAIL;
    }
}


//
// templated function which can change an attribute value for an object
//
template <class Item>
int
//setAttribute(GraphObject* obj,String name,const Item& value, int static_flag)
setAttribute(GraphObject* obj, String name, const Item& value)
{
    if (!obj) {
	warning("setAttribute called for null object\n");
	return 0;
    }
    // see if this graph object has already this attribute
    // if it has, change the value
    Attribute<Item>* attr = (Attribute<Item>*) obj->getAttr(name);
    if (attr) {
	attr->value(value);
	//attr->static_flag(static_flag);
    }
    else {
	// if not, see if this is the parent graph, if so this is error
        // if not, get a copy of the default attribute from the parent graph
	// put the changed attribute in the list for this graph object
	Graph* owner = obj->owner();
	if (owner == obj)
	    return LINK_SET_ATTR_FAIL;
	AttributeBase* attribute = owner->getAttr(name);
	if (!attribute)  {
	    static Item empty;
	    //int i=newAttribute((Graph*) owner, name, empty, static_flag);
	    int i=newAttribute((Graph*) owner, name, empty);
	    if (i == LINK_NEW_ATTR_FAIL)
		error(LINK_SET_ATTR_FAIL);
	    attribute = owner->getAttr(name);
	}
	attr = (Attribute<Item>*) attribute->copy();
	attr->value(value);
	//attr->static_flag(static_flag);
	obj->addAttr(attr);
    }
    return LINK_OK;
}


//
// templated function which can get the value of an attribute
//
template <class Item>
int
getAttribute(GraphObject* obj, String name, Item &value)
{
    if (!obj) {
	warning("getAttribute called for null object\n");
	return 0;
    }
    // see if this graph object has its own copy
    Attribute<Item>* attribute = (Attribute<Item>*) obj->findAttr(name);
    if (!attribute) {
	if (strncmp(name, "x_", 2) != 0) {
	    error(LINK_GET_ATTR_FAIL);
	    error(name);
	}
	return LINK_GET_ATTR_FAIL;
    }
    value = attribute->value();
    return LINK_OK;
}

template <class Item>
ostream& 
AttributeElementOps<Item>::displayItem(ostream& os,  const Item& i) const 
{ 
	os << i; 
	return os;
}
