/*
 * t4graphrep --
 *
 *	This include file provides definitions for the internal representation
 *	of T4Graph objects.
 *
 *	Authors: Jacob Levy and Jean-Claude Wippler.
 *		 jyl@best.com	jcw@equi4.com
 *
 * Copyright (c) 2000-2003, JYL Software Inc.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE, EVEN IF
 * JYL SOFTWARE INC. IS MADE AWARE OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef	__T4GRAPHREP_H__
#define	__T4GRAPHREP_H__

/*
 * First include the public interface:
 */

#include "t4graph.h"

/*
 * Include the GenObject public interface:
 */

#include "genobject.h"

/*
 * HACK to get the code to compile cleanly with both Tcl 8.3 and Tcl 8.4 and
 * later.
 */

#ifndef	CONST84
#define CONST84
#endif

/*
 * These pointers are for public access to the GenObj type definitions for
 * each Tcl_Obj object type provided by TGraph:
 */

extern struct GO_Extension *vertexExt;
extern struct GO_Extension *nodeExt;

/*
 * The key for use of our private interpreter associated data.
 */

#define T4_ASSOCKEY	"tgraph::openstorages"

/*
 * Enumeration of the different T4Graph internal representations:
 */

typedef enum T4Kinds {
    T4GRAPH_ILLEGAL = -1,
    T4GRAPH_STORAGE = 0,
    T4GRAPH_NODE = 1,
    T4GRAPH_VERTEX = 2
} T4Kinds;

/*
 * This record contains information pertaining to one callback declaration.
 */

typedef struct T4CallbackRecord {
    T4Storage *storage;		/* The storage on which the callback occurs. */
    Tcl_Interp *interp;		/* The interp in which the callback occurs. */
    int kind;			/* The kind of callback this record is for. */
} T4CallbackRecord;

/*
 * Enumeration to allow selection of an object kind (for Get and Callback
 * operations).
 */

typedef enum T4ObjectKindSelector {
    T4_OKNODE = 0,
    T4_OKVERTEX = 1,
    T4_OKSTORAGE = 2
} T4ObjectKindSelector;

/*
 * Enumerations for managing the callback facility on a T4Storage object.
 */

typedef enum T4CallbackEventSelector {
    T4_CBESADD = 0,
    T4_CBESDET = 1,
    T4_CBESATT = 2,
    T4_CBESMOD = 3,
    T4_CBESCHG = 4
} T4CallbackEventSelector;

typedef enum T4CallbackActionSelector {
    T4_CBASADD = 0,
    T4_CBASDEL = 1,
    T4_CBASGET = 2,
    T4_CBASSET = 3,
    T4_CBASKND = 4,
    T4_CBASCNT = 5,
    T4_CBASHAS = 6
} T4CallbackActionSelector;

/*
 * Flags marking a T4Vertex.
 */

#define T4_VERTEXEXPORTED	(1 << 0)
#define T4_VERTEXUSED		(1 << 1)

/*
 * How many vertices can be cached between subsequent sweeps of the cache?
 */

#define T4_SWEEP_INTERVAL	256

/*
 * This structure contains the per-interpreter data for a storage object:
 */

typedef struct T4StoragePerInterp
{
    /*
     * doubly-linked chain of records.
     */

    struct T4StoragePerInterp *next;
    struct T4StoragePerInterp *prev;

    /*
     * Link back to the T4Storage that contains this structure:
     */

    T4Storage *storage;

    /*
     * The interpreter in which this set of information is valid.
     */

    Tcl_Interp *interp;

    /*
     * These hash tables contain the internal representations of
     * all objects in this storage that have been exported to Tcl.
     */

    Tcl_HashTable *exportedNodes;
    Tcl_HashTable *exportedVertices;

    /*
     * Hash table for callbacks.
     */

    Tcl_HashTable *callbacks;

    /*
     * Hash tables for storing information associated with vertices.
     */

    Tcl_HashTable *storedProcs;
    Tcl_HashTable *storedValues;
  
    /*
     * Counters for how many callbacks there are for each kind of event.
     * When a counter is zero, the C++ level callback is cancelled.
     */

    int cbaddnode;
    int cbaddvertex;
    int cbdetnode;
    int cbdetvertex;
    int cbattnode;
    int cbattvertex;
    int cbmodnode;
    int cbmodvertex;
    int cbchgstorage;

    /*
     * Callback tokens to be used when the callback is deleted.
     */

    T4CallbackRecord *cbAddNodeRecord;
    T4CallbackRecord *cbDetNodeRecord;
    T4CallbackRecord *cbAttNodeRecord;

    T4CallbackRecord *cbAddVertexRecord;
    T4CallbackRecord *cbDetVertexRecord;
    T4CallbackRecord *cbAttVertexRecord;

    T4CallbackRecord *cbChgStorageRecord;

} T4StoragePerInterp;

/*
 * This class serves as the common base class for all T4Graph internal
 * representations, so its possible to pass any of them throuh a
 * common procedure argument.
 */

class e4_TGRAPHDLL T4InternalRep
{
  private:

    /*
     * The Tcl_Obj * that represents this T4InternalRep in Tcl:
     */

    Tcl_Obj *tclPtr;

    /*
     * The name assigned to this T4Graph object in Tcl and its length.
     */

    char *nm;
    int nmlen;

  public:

    /*
     * Default constructor:
     */

    T4InternalRep();

    /*
     * Return a T4Kinds identifier for the type of this
     * T4Graph object.
     */

    virtual T4Kinds KindIdentifier() const;

    /*
     * Returns a string representing the kind identifier.
     */

    virtual const char *KindString() const;

    /*
     * Return the name by which this object is known in Tcl.
     */

    char *GetName();

    /*
     * Return the length of the name by which this object is known in Tcl.
     */

    int GetNameLen();

    /*
     * Set/get the Tcl object associated with this internal representation.
     */

    Tcl_Obj *GetTclObject();
    void SetTclObject(Tcl_Obj *obj);
};

/*
 * This class is the internal representation of a storage object:
 */

class e4_TGRAPHDLL T4Storage : public T4InternalRep
{
private:
    /*
     * The actual storage object.
     */

    e4_Storage s;

    /*
     * Chain of records, one per interp, for holding interpreter specific
     * information.
     */

    T4StoragePerInterp *spip;

    /*
     * The name of the file and driver for this storage.
     */

    char *fname;
    char *drivername;

    /*
     * Visitor methods:
     */

    int VisitNodes(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int VisitVertices(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);

    /*
     * Helper methods for callback registration mgmt:
     */

    int CBAddCallback(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int CBDelCallback(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int CBGetCallback(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int CBSetCallback(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int CBKindCallback(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int CBCountCallback(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int CBHasCallback(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);

    /*
     * Helper methods for configure operation:
     */

    int SetStorageOptions(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int GetStorageOptions(Tcl_Interp *interp);
    int GetStorageOption(Tcl_Interp *interp, Tcl_Obj *opt);

    /*
     * Clean up in-core state associated with this storage.
     */

    int InternalClose(Tcl_Interp *interp, bool selfdestruct);

    /*
     * Get the interpreter-specific data for this storage in the given
     * interpreter.
     */

    T4StoragePerInterp *GetStoragePerInterp(Tcl_Interp *interp);

  public:

    /*
     * Constructor:
     */

    T4Storage(e4_Storage ss, char *fn, char *dn);

    /*
     * Destructor:
     */

    virtual ~T4Storage();

    /*
     * This method returns the kind of internal representation that
     * is implemented by this class.
     */

    virtual inline T4Kinds KindIdentifier() const {
	return T4GRAPH_STORAGE;
    }

    /*
     * This method returns the string representing the kind identifier.
     */

    virtual const char *KindString() const {
	return "T4Storage";
    }

    /*
     * The following methods implement Tcl sub-commands:
     */

    int Close(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Commit(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Configure(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int IsStable(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int MarkUnstable(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int CopyTo(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Delete(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int DoGC(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Root(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int IsValid(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Name(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int NeedsGC(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Node(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Vertex(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Foreach(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Statistic(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Callback(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Get(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Share(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);

    /*
     * These operations return internal representations of T4Graph
     * objects identified by index or name. The object must already have
     * been created.
     */

    T4Node *GetNodeById(Tcl_Interp *interp, e4_NodeUniqueID nuid);
    T4Vertex *GetVertexById(Tcl_Interp *interp, e4_VertexUniqueID vuid);

    /*
     * These operations create a new entry in the respective hash
     * table when an object is exported to Tcl.
     */

    void StoreNode(Tcl_Interp *interp, T4Node *n, int id);
    void StoreVertex(Tcl_Interp *interp, T4Vertex *f, int id);

    /*
     * These operations remove an internal representation from the
     * respective hash table when the object is no longer being
     * exported to Tcl.
     */

    void RemoveNode(Tcl_Interp *interp, e4_NodeUniqueID nuid);
    void RemoveVertex(Tcl_Interp *interp, e4_VertexUniqueID vuid);

    /*
     * This operation removes all registered callbacks for this storage.
     */

    void RemoveAllCallbacks(T4StoragePerInterp *spp);

    /*
     * This operation externalizes the e4Graph storage object.
     */

    void ExternalizeStorage(e4_Storage &ss);

    /*
     * These methods get called when a callback event occurs.
     */

    void AddNodeCallback(Tcl_Interp *interp, e4_Node n);
    void AddVertexCallback(Tcl_Interp *interp, e4_Vertex v);
    void DetNodeCallback(Tcl_Interp *interp, e4_Node n);
    void DetVertexCallback(Tcl_Interp *interp, e4_Vertex v);
    void AttNodeCallback(Tcl_Interp *interp, e4_Node n);
    void AttVertexCallback(Tcl_Interp *interp, e4_Vertex v);
    void ChangeStorageCallback(Tcl_Interp *interp);

    /*
     * Special callbacks for node modification and vertex modification.
     * These callbacks are always installed, even when the Tcl program
     * does not have any callbacks of these kinds registered. These
     * methods do some housekeeping and then call the Tcl scritps registered
     * as callbacks, thereby ensuring that the housekeeping is always done
     * before any Tcl scripts are run.
     */

    void ModNodeCallback(T4StoragePerInterp *spp,
			 e4_Node n,
			 e4_ModNodeEventReason cbr);
    void ModVertexCallback(T4StoragePerInterp *spp,
			   e4_Vertex v,
			   e4_ModVertexEventReason cbr);

    /*
     * Register and unregister this storage with an interpreter.
     */

    void RegisterStoragePerInterp(Tcl_Interp *interp);
    void UnregisterStoragePerInterp(Tcl_Interp *interp);

    /*
     * Manage state for a vertex exported to Tcl.
     */

    void ClearVertexStoredState(Tcl_Interp *interp, e4_Vertex v) const;

    void SetVertexStoredObject(Tcl_Interp *interp, e4_Vertex v,
			       Tcl_Obj *obj) const;
    void SetVertexStoredCmdInfo(Tcl_Interp *interp, e4_Vertex v,
				T4CmdInfo *cmdInfo) const;

    Tcl_Obj *GetVertexStoredObject(Tcl_Interp *interp, e4_Vertex v) const;
    T4CmdInfo *GetVertexStoredCmdInfo(Tcl_Interp *interp, e4_Vertex v) const;
};

/*
 * This class is the internal representation of a node object.
 */

class e4_TGRAPHDLL T4Node : public T4InternalRep
{
  private:

    /*
     * The actual node object.
     */

    e4_Node n;

    /*
     * The T4Storage instance of the storage containing this vertex:
     */

    T4Storage *s;

    /*
     * Obtain an e4_Vertex for a vertex being modified.
     */

    int GetVertexRef(Tcl_Interp *interp, char *vn,
		     bool createifnotfound, e4_Vertex &v);

    /*
     * Set the value of an e4_Vertex to the given type using the supplied
     * value as the source of the converted type.
     */

    int SetAs(Tcl_Interp *interp, e4_Vertex v, Tcl_Obj *val, int rt);

    /*
     * Obtain a T4Vertex given a vertex name.
     */

    T4Vertex *ObtainVertex(Tcl_Interp *interp, char *vn, bool *crp);

    /*
     * Private visitor functions:
     */

    int VisitVertices(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int VisitParents(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);

  public:

    /*
     * Constructor:
     */

    T4Node(e4_Node nn, T4Storage *ss);

    /*
     * Destructor
     */

    virtual ~T4Node();

    /*
     * Cleanup done before destruction:
     */

    void CleanupInternal(Tcl_Interp *interp);

    /*
     * This method returns the kind of internal representation that
     * is implemented by this class.
     */

    virtual inline T4Kinds KindIdentifier() const {
	return T4GRAPH_NODE;
    }

    /*
     * This method returns a string representing the kind identifier.
     */

    virtual const char *KindString() const {
	return "T4Node";
    }

    /*
     * Retrieve the T4Storage instance for the storage containing
     * this vertex:
     */

    inline T4Storage *GetStorage() const {
	return s;
    }

    /*
     * The following methods implement Tcl sub-commands.
     */

    int VertexCount(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Set(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Add(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Get(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int SetNode(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int AddNode(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int GetVertex(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int MoveVertex(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int DetachVertex(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int VertexType(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int VertexRank(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int VertexName(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int RenameVertex(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Exists(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Parent(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int ParentCount(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int OccurrenceCount(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int ParentRank(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Root(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int IsRoot(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int RankInParent(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int NameInParent(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Detach(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Dispose(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int IsValid(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int IsDetached(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Method(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Call(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Foreach(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Id(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int UserData(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int DetachFirstVertexWithNode(Tcl_Interp *interp, int objc,
				  Tcl_Obj *CONST objv[]);
    int PreCache(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);

    /*
     * This operation externalizes the e4Graph node object.
     */

    void ExternalizeNode(e4_Node &nn);

    /*
     * This IsValid operation is only intended for use by the invoker
     * as a quick check whether the internal representation was lost.
     */

    bool IsValid() const;
};

/*
 * This class is the internal representation of a vertex object.
 */

class e4_TGRAPHDLL T4Vertex : public T4InternalRep
{
  private:

    /*
     * The actual vertex object.
     */

    e4_Vertex f;

    /*
     * The T4Storage instance of the storage containing this vertex:
     */

    T4Storage *s;

  public:

    /*
     * Constructor:
     */

    T4Vertex(e4_Vertex ff, T4Storage *ss);

    /*
     * Destructor:
     */

    virtual ~T4Vertex();

    /*
     * Cleanup done before destruction:
     */

    void CleanupInternal(Tcl_Interp *interp);

    /*
     * This method returns the kind of internal representation that
     * is implemented by this class.
     */

    virtual inline T4Kinds KindIdentifier() const {
	return T4GRAPH_VERTEX;
    }

    /*
     * This method returns a string representing the kind identifier.
     */

    virtual const char *KindString() const {
	return "T4Vertex";
    }

    /*
     * Retrieve the T4Storage instance for the storage containing
     * this vertex:
     */

    inline T4Storage *GetStorage() const {
	return s;
    }

    /*
     * The following operation is the same as Set() except that it
     * converts the given value to a requested type.
     */

    int SetAs(Tcl_Interp *interp, Tcl_Obj *val, int reqtype);

    /*  
     * The following methods implement Tcl sub-commands.
     */

    int Get(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Set(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int SetNode(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Rank(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Detach(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Dispose(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int IsValid(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int IsDetached(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Type(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Name(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Rename(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Root(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Node(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Move(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Next(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Prev(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Call(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int Id(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
    int UserData(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);

    /*
     * This operation externalizes the e4Graph vertex object.
     */

    void ExternalizeVertex(e4_Vertex &ff);

    /*
     * This operation is only intended to be used by the invoker for a
     * quick check whether the internal representation was lost.
     */

    bool IsValid() const;
};

#endif	/* __T4GRAPHREP_H__ */
