#ifndef __SP_URI_REFERENCES_H__
#define __SP_URI_REFERENCES_H__

/*
 * Helper methods for resolving URI References
 *
 * Authors:
 *   Lauris Kaplinski <lauris@kaplinski.com>
 *
 * Copyright (C) 2001-2002 Lauris Kaplinski
 * Copyright (C) 2001 Ximian, Inc.
 *
 * Released under GNU GPL, read the file 'COPYING' for more information
 */

#include <sigc++/connection.h>
#include <sigc++/trackable.h>

#include "bad-uri-exception.h"
#include "forward.h"

namespace Inkscape {

/**
 * A class encapsulating a reference to a particular URI; observers can
 * be notified when the URI comes to reference a different SPObject.
 *
 * The URIReference increments and decrements the SPObject's hrefcount
 * automatically.
 *
 * @see SPObject
 * @see sp_object_href
 * @see sp_object_hunref
 */
class URIReference : public sigc::trackable {
public:
	/**
	 * Constructor.
	 *
	 * @param owner The object on whose behalf this URIReference
	 *              is holding a reference to the target object.
	 */
	URIReference(SPObject *owner);

	/**
	 * Destructor.  Calls shutdown() if the reference has not been
	 * shut down yet.
	 */
	virtual ~URIReference();

	/**
	 * Attaches to a URI, relative to the specified document.
	 *
	 * Throws a BadURIException if the URI is unsupported,
	 * or the fragment identifier is xpointer and malformed.
	 *
	 * @param rel_document document for relative URIs
	 * @param uri the URI to watch
	 */
	void attach(const URI &uri) throw(BadURIException);

	/**
	 * Detaches from the currently attached URI target, if any;
	 * the current referrent is signaled as NULL.
	 */
	void detach();

	/**
	 * @brief Returns a pointer to the current referrent of the
	 * attached URI, or NULL.
	 *
	 * @return a pointer to the referenced SPObject or NULL
	 */
	SPObject *getObject() const { return _obj; }

	/**
	 * @brief Returns a pointer to the URIReference's owner
	 *
	 * @return a pointer to the URIReference's owner
	 */
	SPObject *getOwner() const { return _owner; }

	/**
	 * Accessor for the referrent change notification signal;
	 * this signal is emitted whenever the URIReference's
	 * referrent changes.
	 *
	 * Signal handlers take two parameters: the old and new
	 * referrents.
	 *
	 * @returns a signal
	 */
	sigc::signal<void, SPObject *, SPObject *> changedSignal() {
		return _changed_signal;
	}

	/**
	 * Returns a pointer to a URI containing the currently attached
	 * URI, or NULL if no URI is currently attached.
	 *
	 * @returns the currently attached URI, or NULL
	 */
	const URI *getURI() const {
		return _uri;
	}

	/**
	 * Returns true if there is currently an attached URI
	 *
	 * @returns true if there is an attached URI
	 */
	bool isAttached() const {
		return (bool)_uri;
	}

protected:
	virtual bool _acceptObject(SPObject *obj) const { return true; }

private:
	SPObject *_owner;
	sigc::connection _connection;
	SPObject *_obj;
	URI *_uri;

	sigc::signal<void, SPObject *, SPObject *> _changed_signal;

	void _setObject(SPObject *object);
	static void _release(SPObject *object, URIReference *reference);

	void operator=(const URIReference &ref);
	/* Private and definition-less to prevent accidental use. */
};

}

SPObject *sp_uri_reference_resolve (SPDocument *document, const gchar *uri);

#endif
