/* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */

/* AbiSource Program Utilities
 * 
 * Copyright (C) 2003 Francis James Franklin <fjf@alinameridon.com>
 * Copyright (C) 2003 AbiSource, Inc.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  
 * 02111-1307, USA.
 */
 
#ifndef UTIL_TREE_H
#define UTIL_TREE_H

#include "ut_xml.h"
#include "ut_IntStrMap.h"

class UT_Element;
class UT_ElementHash;
class UT_Tree;

class ABI_EXPORT UT_Node
{
public:
	class ABI_EXPORT Writer
	{
	public:
		virtual ~Writer () { }

		/* if this returns false the current node (elements call the next method) isn't written
		 */
		virtual bool treeNodeWrite (const UT_Node * node) = 0;

		/* in the case of elements, all descendant nodes are affected also if you return
		 * false and set descend to false
		 */
		virtual bool treeNodeWrite (const UT_Element * element, bool & descend) = 0;
		virtual void treeAscending (const UT_Element * element) = 0; // notify that element is about to write the end-tag

		/* if this returns false the write process is stopped
		 */
		virtual bool treeNodeWrite (const UT_UTF8String & text) = 0;
	};

	enum NodeType
		{
			nt_element,
			nt_text,
			nt_cdata,
			nt_pi,
			nt_comment,
			nt_default,
			nt_other
		};

protected:
	UT_Node (NodeType nt);
public:
	virtual ~UT_Node () { }
private:
	const NodeType		m_type;

	UT_Element *		m_parent;
public:
	inline NodeType					type () const { return m_type; }

	inline const UT_Element *		parent () const					{ return m_parent;   };
	inline void						parent (UT_Element * parent)	{ m_parent = parent; };

	virtual bool					write (UT_UTF8String & cache, Writer & writer) const = 0;
};

class ABI_EXPORT UT_Text_Node : public UT_Node
{
private:
	UT_UTF8String		m_text;

public:
	inline const UT_UTF8String &	text () const	{ return m_text; }
	inline       UT_UTF8String &	edit ()			{ return m_text; }

protected:
	UT_Text_Node (NodeType nt, const char * text);
public:
	UT_Text_Node (const char * text = 0);

	virtual ~UT_Text_Node ();

	virtual bool					write (UT_UTF8String & cache, Writer & writer) const;
};

class ABI_EXPORT UT_CDATA_Node : public UT_Text_Node
{
public:
	UT_CDATA_Node (const char * text = 0);

	bool							write (UT_UTF8String & cache, Writer & writer) const;
};

class ABI_EXPORT UT_PI_Node : public UT_Text_Node
{
private:
	UT_UTF8String		m_target;

public:
	inline const UT_UTF8String &	target () const	{ return m_target; }

	UT_PI_Node (const char * pi_target, const char * text = 0);

	bool							write (UT_UTF8String & cache, Writer & writer) const;
};

class ABI_EXPORT UT_Comment_Node : public UT_Text_Node
{
public:
	UT_Comment_Node (const char * text = 0);

	bool							write (UT_UTF8String & cache, Writer & writer) const;
};

class ABI_EXPORT UT_Default_Node : public UT_Text_Node
{
public:
	UT_Default_Node (const char * text = 0);
};

class ABI_EXPORT UT_Element : public UT_Node, public UT_GenericBase
{
public:
	static const char *		GBID;

	virtual const char *	GenericBaseID () const;

private:
	UT_Tree *			m_tree;

	UT_UTF8String		m_ElementTag;
	UT_UTF8String		m_ElementID;

	UT_uint32			m_sibling_number;

	UT_Node **			m_node;
	UT_uint32			m_node_count;
	UT_uint32			m_node_max;

	UT_uint32			m_children;

	UT_UTF8Hash *		m_Attrs;
protected:
	UT_UTF8Hash *		m_Props;
private:
	UT_uint32			m_ref_count;

public:
	inline const UT_Tree *			tree () const		{ return m_tree; }

	inline const UT_UTF8String &	ElementTag () const	{ return m_ElementTag; }
	inline const UT_UTF8String &	ElementID () const	{ return m_ElementID; }

	inline UT_uint32				sibling_number () const	{ return m_sibling_number; }

	inline const UT_Node *			operator[] (UT_uint32 i) const	{ return (i < m_node_count) ? m_node[i] : 0; };

	inline UT_uint32				nodes () const		{ return m_node_count; }
	inline UT_uint32				children () const	{ return m_children;   } // element-children only

	inline const UT_UTF8String *	Attr (const char *          attr) const	{ return m_Attrs ? (*m_Attrs)[attr] : 0; }
	inline const UT_UTF8String *	Attr (const UT_UTF8String & attr) const	{ return m_Attrs ? (*m_Attrs)[attr] : 0; }

	inline const UT_UTF8String *	Prop (const char *          prop) const	{ return m_Props ? (*m_Props)[prop] : 0; }
	inline const UT_UTF8String *	Prop (const UT_UTF8String & prop) const	{ return m_Props ? (*m_Props)[prop] : 0; }

	inline const UT_UTF8Hash *		Attrs () const { return m_Attrs; }
	inline const UT_UTF8Hash *		Props () const { return m_Props; }

	inline UT_uint32				refs () const		{ return m_ref_count; }

public:
	UT_Element (const char * element_tag, const char * element_id);

	virtual ~UT_Element ();

protected:
	/* change the element's ID;
	 * fails if the new element_id is already registered with the element's tree (if any)
	 */
	bool							ElementID (const UT_UTF8String & element_id);

public:
	/* adding/changing attributes
	 */
	virtual bool					insAttr (const char ** atts);
	virtual bool					insAttr (const char *  name, const char * value);
	virtual void					delAttr (const char *  name);

	const UT_Element *				child (UT_uint32 sn) const;

	/* responsibility for node passes to element with appendNode() and insertNode(),
	 * and passes back with detachNode().
	 */
	inline bool						appendNode (UT_Node * node)	{ return insertNode (node, m_node_count); }
	bool							insertNode (UT_Node * node, UT_uint32 index, bool delete_if_fail = true);
	bool							deleteNode (UT_uint32 index);
	UT_Node *						detachNode (UT_uint32 index);

protected:
	/* this is called by insertNode() to check whether the supplied node is permitted
	 * at the suggested index; UT_Element default implementation returns true always
	 */
	virtual bool					acceptNode (const UT_Node * node, UT_uint32 index) const;

public:
	/* for moving nodes about within a tree; on failure the node stays where it started
	 */
	bool							shiftNode (UT_uint32 src_index, UT_Element * dest, UT_uint32 dest_index);

	bool							descendsFrom (const UT_Element * element) const;

	virtual bool					write (UT_UTF8String & cache, Writer & writer) const;

	static bool						map (UT_Tree * Tree, UT_Element * element);
private:
	inline bool						map (UT_Element * element) { return UT_Element::map (m_tree, element); }

	bool							setUniqueID (UT_Tree * m_tree, UT_UTF8Hash & map_Abi, UT_UTF8Hash & map_Named);
protected:
	virtual void					remap_IDREFs (UT_UTF8Hash & map_Abi, UT_UTF8Hash & map_Named); // empty implementation

private:
	/* don't call register() unless you're sure it will succeed; use map() first
	 * returns false if part (or all) of element & children failed to register
	 */
	bool							enregister (UT_Tree * Tree);
	void							deregister ();
public:
	/* used by UT_Tree for deregistering root elements
	 */
	static void						deregister (UT_Element * element);

	inline void						relabel (UT_uint32 sn)	{ m_sibling_number = sn; }
	void							relabelChildren ();

	bool							grow ();
};

class ABI_EXPORT UT_ElementHash : public UT_GenericUTF8Hash
{
public:
	UT_ElementHash (UT_uint32 increment);

	~UT_ElementHash ();

	inline void clear ()
	{
		UT_GenericUTF8Hash::clear (false); // don't delete elements
	}

	/* for easy sequential access of map members:
	 */
	bool pair (UT_uint32 index, const UT_UTF8String *& element_id, const UT_Element *& element) const;

	/* The ElementHash stores pointers only, does not delete them ever
	 */
	inline bool ins (const UT_UTF8String & element_id, const UT_Element * element)
	{
		return element ? UT_GenericUTF8Hash::ins (element_id, const_cast<UT_Element *>(element)) : false;
	}

	/* returns false if no element with ID; does not delete element, if any
	 */
	bool del (const UT_UTF8String & element_id);

	inline const UT_Element * operator[] (const UT_UTF8String & key)
	{
		return static_cast<const UT_Element *>(UT_GenericUTF8Hash::lookup (key));
	}
};

class ABI_EXPORT UT_ID_Generator
{
public:
	static bool		isInternal (const UT_UTF8String & element_id); // true if element_id matches "Abi_??????"

	UT_ID_Generator ();

	/* cycles through 56.8 billion unique 10-character IDs,
	 * each starting with the 4-character sequence "Abi_"
	 */
	const char *	next ();

private:
	int		i1,i2,i3,i4,i5,i6;
	char	buffer[11];
};

class ABI_EXPORT UT_ElementFactory
{
public:
	virtual UT_Element *	createElement (const char * element_tag, const char * element_id) const;

	virtual ~UT_ElementFactory () { }
};

class ABI_EXPORT UT_Tree : public UT_XML::ExpertListener
{
private:
	UT_XML *					m_parser; // used when building tree
	const UT_ElementFactory *	m_factory;
	UT_Element *				m_current;
	bool						m_cdata;
	bool						m_valid;

	/* IDs fall into 2 categories: (1) internal "Abi_??????"; (2) user-defined
	 */
	UT_ElementHash		m_ElementID_Abi;
	UT_ElementHash		m_ElementID_Named;

	UT_Element *		m_root;

	UT_Node **			m_node;
	UT_uint32			m_node_count;
	UT_uint32			m_node_max;

	UT_ID_Generator		m_Generator;

public:
	inline const UT_Element *		root () const	{ return m_root; }

	inline const UT_Node *			operator[] (UT_uint32 i) const	{ return (i < m_node_count) ? m_node[i] : 0; };

	inline UT_uint32				nodes () const		{ return m_node_count; }

	/* return an internal ID (i.e., of type "Abi_??????") unique in this tree:
	 */
	inline const char *				uniqueEID ()	{ return m_Generator.next (); }

	const UT_Element *				lookupEID (const UT_UTF8String & element_id);

public:
	UT_Tree ();

	~UT_Tree ();

	/* responsibility for node passes to element with appendNode() and insertNode(),
	 * and passes back with detachNode().
	 */
	inline bool						appendNode (UT_Node * node)	{ return insertNode (node, m_node_count); }
	bool							insertNode (UT_Node * node, UT_uint32 index, bool delete_if_fail = true);
	bool							deleteNode (UT_uint32 index);
	UT_Node *						detachNode (UT_uint32 index);

private:
	bool							grow ();

public:
	bool							write (UT_Node::Writer & writer) const;

	/* Element Registration
	 * 
	 * register()/deregister() perform some checks, but it's up to the caller really to ensure
	 * that IDs are properly assigned to elements - use lookupEID() to ensure IDs are unique
	 */

	/* fails if an element with the same ID has already been registered, or if the element
	 * belongs to a tree; if registration succeeds then the element should set its tree to this
	 */
	bool							enregister (const UT_Element * element);

	/* fails if element has registered parent or is root element; if deregistration succeeds then the
	 * element should set its tree to 0
	 */
	bool							deregister (const UT_Element * element);

	/* attempts to deregister element, but this may fail silently; registers element with new ID
	 * if reregistration succeeds, the element should change its ID to new_EID
	 */
	bool							reregister (const UT_Element * element, const UT_UTF8String & new_EID);


	/* building the tree from file/memory using the XML parser
	 */

	inline bool			parse (const char * filename, const UT_ElementFactory * factory = 0)
	{
		return parse (filename, 0, factory, false);
	}
	inline bool			parse (const char * buffer, UT_uint32 length, const UT_ElementFactory * factory = 0)
	{
		return parse (buffer, length, factory, true);
	}
private:
	bool				parse (const char * buffer, UT_uint32 length, const UT_ElementFactory * factory, bool bIsBuffer);
public:
	/* implementation of UT_XML::ExpertListener
	 */
	void StartElement (const XML_Char * name, const XML_Char ** atts);
	void EndElement (const XML_Char * name);
	void CharData (const XML_Char * buffer, int length);
	void ProcessingInstruction (const XML_Char * target, const XML_Char * data);
	void Comment (const XML_Char * data);
	void StartCdataSection ();
	void EndCdataSection ();
	void Default (const XML_Char * buffer, int length);
};

#endif /* ! UTIL_TREE_H */