/*
 * i4vertex.cpp --
 *
 *	This file provides the implementation of the e4_VertexImpl 
 *	class defined in e4graphimpl.h.
 *
 *	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.
 */

#include "e4graphimpl.h"

/*
 * Constructor:
 */

e4_VertexImpl::e4_VertexImpl(e4_StorageImpl *st, int i)
  : e4_RefCounter(),
    flags(0),
    vertexID(i),
    s(st)
{
}

/*
 * Destructor:
 */

e4_VertexImpl::~e4_VertexImpl()
{
    e4_StorageImpl *lsp = s;

    if (s != NULL) {

	/*
	 * Don't bother about cleanups if the storage itself is invalid.
	 */

	if (!s->IsValid()) {
	    return;
	}

	/*
	 * Remove this vertex from the cache.
	 */

	s->ForgetVertex(vertexID);

	/*
	 * Don't bother about GC if we cannot modify the underlying
	 * storage.
	 */

	if ((lsp->GetPermissions() & E4_SPMODIFY) == 0) {
	    return;
	}

	/*
	 * Can't use the "s" instance variable because
	 * ForgetVertex resets it to NULL.
	 *
	 * If this vertex is detached, it just became unreachable. If GC
	 * is not deferred, clean up right away.
	 */

	if (lsp->DRV_IsDetachedVertexID(vertexID)) {
	    lsp->RegisterUnreachableVertexID(vertexID);
	    if ((lsp->GetState() & E4_AUTOGC) == E4_AUTOGC) {
		lsp->DRV_DoGC(E4_AUTOGC);
	    } else {
		lsp->SetNeedsGC(true);
	    }
	}
    }
}

/*
 * This method is called when the reference count goes to (or below) zero.
 */

void
e4_VertexImpl::NotReferenced()
{
    delete this;
}

/*
 * Set the value of this vertex to a new node.
 */

e4_NodeImpl *
e4_VertexImpl::SetNode() const
{
    int nodeID;
    e4_NodeImpl *nip;

    if (s == NULL) {
	return NULL;
    }
    if ((s->GetPermissions() & E4_SPMODIFY) == 0) {
	return NULL;
    }

    nodeID = s->DRV_ReserveNodeID();
    nip = s->FindOrCreateNode(nodeID);
    if (nip == NULL) {
	return NULL;
    }
    s->MarkUnstable();
    nip->IncrRefCount();
    s->DRV_SetVertex(vertexID,
		     s->DRV_NameIDFromVertexID(vertexID),
		     E4_VTNODE,
		     nodeID);
    s->RecordTimeStamp(E4_ECADDNODE | E4_ECMODVERTEX);
    if (s->HasCallbacks(E4_ECADDNODE)) {
	s->CauseEventInternal(E4_ECADDNODE, nip, NULL);
    }
    if (s->HasCallbacks(E4_ECMODVERTEX)) {
	s->CauseEventInternal(E4_ECMODVERTEX,
			      (e4_RefCounter *) this,
			      (void *) E4_ERMVMODVALUE);
    }

    return nip;
}

/*
 * Detach this vertex from its containing node.
 */

bool
e4_VertexImpl::Detach()
{
    int nodeID, childID, mask;
    e4_NodeImpl *nip;
    bool detachedNode = false;
    bool hasNodeValue = false;

    if (s == NULL) {
	return false;
    }
    if ((s->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    nodeID = s->DRV_ContainingNodeIDFromVertexID(vertexID);

    /*
     * If already detached, just punt.
     */

    if (nodeID == E4_NODENOTFOUND) {
	return true;
    }

    /*
     * Since we're not detached, if the value is a node, that node may now
     * become detached.
     */

    if (s->DRV_VertexTypeFromVertexID(vertexID) == E4_VTNODE) {
	(void) s->DRV_GetRawValue(vertexID, childID);
	hasNodeValue = true;
    }

    /*
     * Detach the vertex.
     */

    if (!s->DRV_DetachVertexByID(vertexID)) {
	return false;
    }
    s->MarkUnstable();

    /*
     * If the previously containing node is referenced by the user, flush
     * its cache.
     */

    nip = s->FindReferencedNode(nodeID);
    if (nip != NULL) {
	nip->FlushCache();
    }

    /*
     * Do callback notifications.
     */

    mask = E4_ECMODNODE | E4_ECDETVERTEX;
    if ((hasNodeValue) && (s->DRV_IsDetachedNodeID(childID))) {
	mask |= E4_ECDETNODE;
	detachedNode = true;
    }
    s->RecordTimeStamp(mask);
    if (s->HasCallbacks(E4_ECMODNODE)) {
	if (nip != NULL) {
	    s->CauseEventInternal(E4_ECMODNODE,
				  nip, 
				  (void *) E4_ERMNDETVERTEX);
	}
    }
    if (s->HasCallbacks(E4_ECDETVERTEX)) {
	s->CauseEventInternal(E4_ECDETVERTEX,
			      this,
			      NULL);
	SetFlags(E4_CBDETACHDELIVERED);
    }
    if ((detachedNode) && (s->HasCallbacks(E4_ECDETNODE))) {
	nip = s->FindReferencedNode(childID);
	if (nip != NULL) {
	    s->CauseEventInternal(E4_ECDETNODE, nip, NULL);
	}
    }
    return true;
}

/*
 * Is this vertex detached?
 */

bool
e4_VertexImpl::IsDetached() const
{
    if (s == NULL) {
	return true;
    }
    return s->DRV_IsDetachedVertexID(vertexID);
}

/*
 * Set a vertex to contain an existing node.
 */

bool
e4_VertexImpl::SetToNode(int childID) 
{
    int nodeID = -1;
    bool wasdetached = false;

    if (s == NULL) {
	return false;
    }
    if ((s->GetPermissions() & E4_SPMODIFY) == 0) {
	fprintf(stderr, "1");
	return false;
    }

    /*
     * Only assign if the node is not already the value of this
     * vertex!!!
     */

    if (s->DRV_VertexTypeFromVertexID(vertexID) == E4_VTNODE) {
	if (!(s->DRV_GetNodeID(vertexID, nodeID)) ||
	    (nodeID == E4_NODENOTFOUND) ||
	    (nodeID == childID)) {
	    return false;
	}
    }
    wasdetached = s->DRV_IsDetachedNodeID(nodeID);
    if (!s->DRV_SetVertexByIndexToNode(vertexID, childID)) {
	return false;
    }
    s->MarkUnstable();

    /*
     * Fire the vertex modify event (even in the case where the
     * node is already the value of this vertex).
     */

    s->RecordTimeStamp(E4_ECMODVERTEX);
    if (s->HasCallbacks(E4_ECMODVERTEX)) {
	s->CauseEventInternal(E4_ECMODVERTEX, this, (void *) E4_ERMVMODVALUE);
    }
    return true;
}

/*
 * Get/Set user data associated with this vertex.
 */

bool
e4_VertexImpl::GetUserData(int &userData) const
{
    if (s == NULL) {
	return false;
    }
    return s->DRV_GetVertexUserData(vertexID, userData);
}

bool
e4_VertexImpl::SetUserData(int userData)
{
    if (s == NULL) {
	return false;
    }
    if ((s->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    if (s->DRV_SetVertexUserData(vertexID, userData)) {
	s->MarkUnstable();
	s->RecordTimeStamp(E4_ECMODVERTEX);
	if (s->HasCallbacks(E4_ECMODVERTEX)) {
	    s->CauseEventInternal(E4_ECMODVERTEX,
				  this,
				  (void *) E4_ERMVMODUSERDATA);
	}
	return true;
    }
    return false;
}

/*
 * Get and set the value:
 */

bool
e4_VertexImpl::Get(e4_ValueImpl *&v) const
{
    if (s == NULL) {
	return false;
    }
    return s->DRV_GetVertexByIndex(vertexID, v);
}

bool
e4_VertexImpl::Get(e4_NodeImpl *&node) const 
{
    if (s == NULL) {
	return false;
    }
    return s->DRV_GetVertexByIndex(vertexID, node);
}

bool
e4_VertexImpl::Get(int &v) const
{
    if (s == NULL) {
	return false;
    }
    return s->DRV_GetVertexByIndex(vertexID, v);
}

bool
e4_VertexImpl::Get(double &fv) const
{
    if (s == NULL) {
	return false;
    }
    return s->DRV_GetVertexByIndex(vertexID, fv);
}

bool
e4_VertexImpl::Get(const char *&str) const
{
    if (s == NULL) {
	return false;
    }
    return s->DRV_GetVertexByIndex(vertexID, str);
}

bool
e4_VertexImpl::Get(const void *&bytes, int &nbytes) const
{
    if (s == NULL) {
	return false;
    }
    return s->DRV_GetVertexByIndex(vertexID, bytes, nbytes);
}

bool
e4_VertexImpl::Set(int v)
{
    if (s == NULL) {
	return false;
    }
    if ((s->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    if (!s->DRV_SetVertexByIndex(vertexID, v)) {
	return false;
    }
    s->MarkUnstable();
    s->RecordTimeStamp(E4_ECMODVERTEX);
    if (s->HasCallbacks(E4_ECMODVERTEX)) {
	s->CauseEventInternal(E4_ECMODVERTEX,
			      (e4_RefCounter *) this,
			      (void *) E4_ERMVMODVALUE);
    }
    return true;
}

bool
e4_VertexImpl::Set(double fv)
{
    if (s == NULL) {
	return false;
    }
    if ((s->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    if (!s->DRV_SetVertexByIndex(vertexID, fv)) {
	return false;
    }
    s->MarkUnstable();
    s->RecordTimeStamp(E4_ECMODVERTEX);
    if (s->HasCallbacks(E4_ECMODVERTEX)) {
	s->CauseEventInternal(E4_ECMODVERTEX,
			      (e4_RefCounter *) this,
			      (void *) E4_ERMVMODVALUE);
    }
    return true;
}

bool
e4_VertexImpl::Set(const char *str)
{
    if (s == NULL) {
	return false;
    }
    if ((s->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    if (!s->DRV_SetVertexByIndex(vertexID, str)) {
	return false;
    }
    s->MarkUnstable();
    s->RecordTimeStamp(E4_ECMODVERTEX);
    if (s->HasCallbacks(E4_ECMODVERTEX)) {
	s->CauseEventInternal(E4_ECMODVERTEX,
			      (e4_RefCounter *) this,
			      (void *) E4_ERMVMODVALUE);
    }
    return true;
}

bool
e4_VertexImpl::Set(const void *bytes, int nbytes)
{
    if (s == NULL) {
	return false;
    }
    if ((s->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    if (!s->DRV_SetVertexByIndex(vertexID, bytes, nbytes)) {
	return false;
    }
    s->MarkUnstable();
    s->RecordTimeStamp(E4_ECMODVERTEX);
    if (s->HasCallbacks(E4_ECMODVERTEX)) {
	s->CauseEventInternal(E4_ECMODVERTEX,
			      (e4_RefCounter *) this,
			      (void *) E4_ERMVMODVALUE);
    }
    return true;
}

/*
 * Get the rank of this vertex in its containing node.
 */

int
e4_VertexImpl::Rank() const
{
    int nodeID;
        
    if (s == NULL) {
	return E4_VERTEXNOTFOUND;
    }
    nodeID = s->DRV_NodeIDFromVertexID(vertexID);
    if (nodeID == E4_NODENOTFOUND) {
	return E4_VERTEXNOTFOUND;
    }
    return s->DRV_RankFromVertexID(nodeID, vertexID);
}

/*
 * Get the count of vertices "up to this one" with the same name
 * as this one, in the node containing this vertex. Returns -1
 * if this vertex is detached.
 */

int
e4_VertexImpl::CountWithName() const
{
    int nodeID;

    if (s == NULL) {
	return -1;
    }
    nodeID = s->DRV_NodeIDFromVertexID(vertexID);
    if (nodeID == E4_NODENOTFOUND) {
	return -1;
    }
    return
	s->DRV_VertexCountWithNameIDFromNodeID(
			nodeID,
			vertexID,
			s->DRV_NameIDFromVertexID(vertexID));
}

/*
 * Get the total number of vertices in the node containing this one
 * that have the same name as this vertex. Returns -1 if this vertex
 * is detached.
 */

int
e4_VertexImpl::TotalCountWithName() const
{
    int nodeID;

    if (s == NULL) {
	return -1;
    }
    nodeID = s->DRV_NodeIDFromVertexID(vertexID);
    if (nodeID == E4_NODENOTFOUND) {
	return -1;
    }
    return
	s->DRV_VertexCountWithNameIDFromNodeID(
				nodeID,
				E4_VERTEXNOTFOUND,
				s->DRV_NameIDFromVertexID(vertexID));
}

/*
 * Retrieve the number of vertices "up to this one" in the node containing
 * this vertex, with the same type as this one. Returns -1 if this vertex
 * is detached.
 */

int
e4_VertexImpl::CountWithType() const
{
    int nodeID;

    if (s == NULL) {
	return -1;
    }
    nodeID = s->DRV_NodeIDFromVertexID(vertexID);
    if (nodeID == E4_NODENOTFOUND) {
	return -1;
    }
    return
	s->DRV_VertexCountWithTypeFromNodeID(
			nodeID,
			vertexID,
			s->DRV_VertexTypeFromVertexID(vertexID));
}

/*
 * Get the total number of vertices in the node containing this one
 * that have the same type as this vertex. Returns -1 if this vertex
 * is detached.
 */

int
e4_VertexImpl::TotalCountWithType() const
{
    int nodeID;

    if (s == NULL) {
	return -1;
    }
    nodeID = s->DRV_NodeIDFromVertexID(vertexID);
    if (nodeID == E4_NODENOTFOUND) {
	return -1;
    }
    return
	s->DRV_VertexCountWithTypeFromNodeID(
			nodeID,
			E4_VERTEXNOTFOUND,
			s->DRV_VertexTypeFromVertexID(vertexID));
}

/*
 * Rename this vertex.
 */

bool
e4_VertexImpl::Rename(const char *newname) const
{
    int nid;
    int nameID;
    e4_NodeImpl *nip;

    if ((s == NULL) || (newname == NULL)) {
	return false;
    }
    if ((s->GetPermissions() & E4_SPMODIFY) == 0) {
	return false;
    }
    nameID = s->InternName(newname, true);
    if (nameID == E4_NEXTNONE) {
	return false;
    }
    if (!s->DRV_RenameVertexByVertexID(vertexID, nameID)) {
	return false;
    }
    s->MarkUnstable();
    s->RecordTimeStamp(E4_ECMODVERTEX | E4_ECMODNODE);
    if (s->HasCallbacks(E4_ECMODVERTEX)) {
	s->CauseEventInternal(E4_ECMODVERTEX,
			      (e4_RefCounter *) this,
			      (void *) E4_ERMVRENAME);
    }
    if (s->HasCallbacks(E4_ECMODNODE)) {
	nid = s->DRV_ContainingNodeIDFromVertexID(vertexID);
	nip = s->FindReferencedNode(nid);
	if (nip != NULL) {
	    nip->FlushCache();
	    s->CauseEventInternal(E4_ECMODNODE,
				  nip,
				  (void *) E4_ERMNRENVERTEX);
	}
    }
    return true;
}

/*
 * Move the vertex identified by moveVertexID to the given position
 * (by rank) in the node containing this vertex.
 */

bool
e4_VertexImpl::MoveVertex(int moveVertexID, int rank) const
{
    if ((s == NULL) ||
	(s->DRV_IsDetachedVertexID(vertexID)) ||
	((s->GetPermissions() & E4_SPMODIFY) == 0)) {
	return false;
    }
    return s->MoveVertex(s->DRV_ContainingNodeIDFromVertexID(vertexID),
			 moveVertexID,
			 E4_IOAT,
			 rank);
}
