/*
 * e4vertex.cpp --
 *
 *	Implementation of the e4_Vertex class defined in e4graph.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"

/*
 * This instance of e4_Vertex can be used (by assigning it to fields or
 * variables of type e4_Vertex) to remove remaining references to instances
 * that you want to discard. Especially useful for removing remaining
 * references in heap-allocated memory.
 */

e4_Vertex const invalidVertex;

/*
 * Default constructor:
 */

e4_Vertex::e4_Vertex() : e4_RefCount() {}

/*
 * Constructor which assigns a value to the the implementation pointer.
 */

e4_Vertex::e4_Vertex(e4_VertexImpl *ip) : e4_RefCount(ip) {}

/*
 * Copying constructor:
 */

e4_Vertex::e4_Vertex(const e4_Vertex &referrer) : e4_RefCount(referrer) {}

/*
 * Copying constructor that's given an e4_RefCount:
 */

e4_Vertex::e4_Vertex(const e4_RefCount &referrer)
    : e4_RefCount(referrer)
{
    if ((impl != NULL) && (impl->Kind() != E4_RKVERTEX)) {
	(void) e4_RefCount::operator=(invalidVertex);
    }
}

/*
 * Assignment operator:
 */

e4_Vertex & e4_Vertex::operator=(const e4_Vertex &referrer)
{
    return (e4_Vertex &) e4_RefCount::operator=(referrer);
}

/*
 * Get the value of a vertex.
 */

bool
e4_Vertex::Get(e4_Value &v) const
{
    e4_ValueImpl *vipp;

    if (impl == NULL) {
	return false;
    }
    if (((e4_VertexImpl *) impl)->Get(vipp) == false) {
	return false;
    }
    if (vipp == NULL) {
	return false;
    }
    v.vertexType = vipp->vertexType;
    switch (vipp->vertexType) {
    case E4_VTNODE:
        {
	    e4_Node n(vipp->u.n);

	    v.n = n;
	    break;
	}
    case E4_VTINT:
        v.u.i = vipp->u.i;
	break;
    case E4_VTDOUBLE:
        v.u.d = vipp->u.d;
	break;
    case E4_VTSTRING:
        v.u.s = vipp->u.s;
	break;
    case E4_VTBINARY:
        v.u.b.bytes = vipp->u.b.bytes;
	v.u.b.nbytes = vipp->u.b.nbytes;
	break;
    default:
        delete vipp;
	return false;
    }

    delete vipp;

    return true;
}

bool
e4_Vertex::Get(e4_Node &n) const
{
    e4_NodeImpl *nipp;

    if (impl == NULL) {
        return false;
    }
    if (!((e4_VertexImpl *) impl)->Get(nipp)) {
        return false;
    }

    e4_Node nn(nipp);
    n = nn;

    return true;
}

bool
e4_Vertex::Get(int &v) const
{
    if (impl == NULL) {
	return false;
    }
    return ((e4_VertexImpl *) impl)->Get(v);
}

bool
e4_Vertex::Get(double &v) const
{
    if (impl == NULL) {
	return false;
    }
    return ((e4_VertexImpl *) impl)->Get(v);
}

bool
e4_Vertex::Get(const char *&v) const
{
    if (impl == NULL) {
	return false;
    }
    return ((e4_VertexImpl *) impl)->Get(v);
}

bool
e4_Vertex::Get(const void *&v, int &nbv) const
{
    if (impl == NULL) {
	return false;
    }
    return ((e4_VertexImpl *) impl)->Get(v, nbv);
}

/*
 * Set the value of a vertex.
 */

bool
e4_Vertex::Set(int v) const
{
    if (impl == NULL) {
	return false;
    }
    return ((e4_VertexImpl *) impl)->Set(v);
}

bool
e4_Vertex::Set(double v) const
{
    if (impl == NULL) {
	return false;
    }
    return ((e4_VertexImpl *) impl)->Set(v);
}

bool
e4_Vertex::Set(const char *s) const
{
    if (impl == NULL) {
	return false;
    }
    return ((e4_VertexImpl *) impl)->Set(s);
}

bool
e4_Vertex::Set(const void *v, int nbv) const
{
    if (impl == NULL) {
	return false;
    }
    return ((e4_VertexImpl *) impl)->Set(v, nbv);
}

bool
e4_Vertex::Set(e4_Node n) const
{
    if ((impl == NULL) || (!n.IsValid())) {
	return false;
    }
    return ((e4_VertexImpl *) impl)->SetToNode(n.GetRawUniqueID());
}

bool
e4_Vertex::Set(const e4_Value &v) const
{
    switch (v.vertexType) {
    case E4_VTNODE:
	return Set(v.n);
    case E4_VTINT:
	return Set(v.u.i);
    case E4_VTDOUBLE:
	return Set(v.u.d);
    case E4_VTSTRING:
	return Set(v.u.s);
    case E4_VTBINARY:
	return Set(v.u.b.bytes, v.u.b.nbytes);
    default:
	return false;
    }
}

/*
 * Set the content of this vertex to a new node and return the new node.
 */

bool
e4_Vertex::SetNode(e4_Node &n) const
{
    e4_NodeImpl *nnip;

    if (impl == NULL) {
	return false;
    }
    nnip = ((e4_VertexImpl *) impl)->SetNode();
    if (nnip == NULL) {
	return false;
    }

    e4_Node nn(nnip);

    /*
     * Decrement the refcount to offset the artificially incremented refcount
     * for protection during callbacks.
     */

    nnip->DecrRefCount();

    n = nn;

    return true;
}

/*
 * Get the rank of this vertex within the node containing it.
 */

int
e4_Vertex::Rank() const
{
    if (impl == NULL) {
	return E4_VERTEXNOTFOUND;
    }
    return ((e4_VertexImpl *) impl)->Rank();
}

/*
 * Get the count of how many vertices up to this one in the containing
 * node have the same name as this one. Return -1 if the vertex is
 * detached.
 */

int
e4_Vertex::CountWithName() const
{
    if (impl == NULL) {
	return -1;
    }
    return ((e4_VertexImpl *) impl)->CountWithName();
}

/*
 * Get the total count of how many vertices in the node containing
 * this one have the same name. Return -1 if this vertex is detached.
 */

int
e4_Vertex::TotalCountWithName() const
{
    if (impl == NULL) {
	return -1;
    }
    return ((e4_VertexImpl *) impl)->TotalCountWithName();
}

/*
 * Get the count of vertices up to this one in the node containing this
 * vertex that have the same type as this vertex. If the vertex is
 * detached, return -1.
 */

int
e4_Vertex::CountWithType() const
{
    if (impl == NULL) {
	return -1;
    }
    return ((e4_VertexImpl *) impl)->CountWithType();
}

/*
 * Get the total count of vertices with the same type as this one within
 * the node containing this vertex. Return -1 if this vertex is detached.
 */

int
e4_Vertex::TotalCountWithType() const
{
    if (impl == NULL) {
	return -1;
    }
    return ((e4_VertexImpl *) impl)->TotalCountWithType();
}

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

bool
e4_Vertex::Detach() const
{
    if (impl == NULL) {
	return false;
    }
    return ((e4_VertexImpl *) impl)->Detach();
}

/*
 * Is this vertex detached?
 */

bool
e4_Vertex::IsDetached() const
{
    if (impl == NULL) {
	return true;
    }
    return ((e4_VertexImpl *) impl)->IsDetached();
}

/*
 * Get the vertex type for this vertex.
 */

e4_VertexType
e4_Vertex::Type() const
{
    if (impl == NULL) {
	return E4_VTUNKNOWN;
    }
    return ((e4_VertexImpl *) impl)->Type();
}

/*
 * Get the name of this vertex.
 */

const char *
e4_Vertex::Name() const
{
    if (impl == NULL) {
	return NULL;
    }
    return ((e4_VertexImpl *) impl)->Name();
}

/*
 * Rename this vertex.
 */

bool
e4_Vertex::Rename(const char *newname) const
{
    if (impl == NULL) {
	return false;
    }
    return ((e4_VertexImpl *) impl)->Rename(newname);
}

/*
 * Get a unique ID (unique within the storage) for this vertex.
 */

int
e4_Vertex::GetRawUniqueID() const
{
    if (impl == NULL) {
	return E4_VERTEXNOTFOUND;
    }
    return ((e4_VertexImpl *) impl)->GetUniqueID();
}

/*
 * Same as above but wrap it up into an e4_VertexUniqueID.
 */

bool
e4_Vertex::GetUniqueID(e4_VertexUniqueID &v) const
{
    int id;
    e4_StorageImpl *sp;

    if (impl == NULL) {
	return false;
    }
    id = ((e4_VertexImpl *) impl)->GetUniqueID();
    if (id == E4_VERTEXNOTFOUND) {
	return false;
    }
    sp = ((e4_VertexImpl *) impl)->GetStorage();
    if (sp == NULL) {
	return false;
    }

    e4_VertexUniqueID vv(id, sp->HashCode());

    v = vv;

    return true;
}

/*
 * Get the root node of the storage containing this vertex.
 */

bool
e4_Vertex::GetRootNode(e4_Node &rn) const
{
    e4_NodeImpl *rnip;
    e4_StorageImpl *ssip;

    if (impl == NULL) {
	return false;
    }
    ssip = ((e4_VertexImpl *) impl)->GetStorage();
    if (ssip == NULL) {
	return false;
    }
    rnip = ssip->GetRootNode();
    if (rnip == NULL) {
	return false;
    }

    e4_Node rrnn(rnip);

    rn = rrnn;

    return true;
}

/*
 * Get the storage in which this vertex occurs.
 */

bool
e4_Vertex::GetStorage(e4_Storage &s) const
{
    e4_StorageImpl *ssip;

    if (impl == NULL) {
	return false;
    }
    ssip = ((e4_VertexImpl *) impl)->GetStorage();
    if (ssip == NULL) {
	return false;
    }

    e4_Storage ss(ssip);

    s = ss;

    return true;
}

/*
 * Get the node containing this vertex.
 */

bool
e4_Vertex::GetNode(e4_Node &n) const
{
    e4_NodeImpl *nnip;

    if (impl == NULL) {
	return false;
    }
    nnip = ((e4_VertexImpl *) impl)->GetNode();
    if (nnip == NULL) {
	return false;
    }

    e4_Node nn(nnip);

    n = nn;

    return true;
}

/*
 * Get the next vertex after this in the node containing this vertex.
 */

bool
e4_Vertex::Next(int num, e4_Vertex &nf) const
{
    e4_VertexImpl *ffip;

    if (impl == NULL) {
	return false;
    }
    ffip = ((e4_VertexImpl *) impl)->Next(num);
    if (ffip == NULL) {
	return false;
    }

    e4_Vertex ff(ffip);

    nf = ff;

    return true;
}

/*
 * Get the vertex before this one in the node containing this vertex.
 */

bool
e4_Vertex::Prev(int num, e4_Vertex &pv) const
{
    e4_VertexImpl *ffip;

    if (impl == NULL) {
	return false;
    }
    ffip = ((e4_VertexImpl *) impl)->Prev(num);
    if (ffip == NULL) {
	return false;
    }

    e4_Vertex ff(ffip);

    pv = ff;

    return true;
}

/*
 * Move the given vertex into the node containing this vertex at
 * the indicated insertOrder and offset. If insertOrder is either
 * E4_IOBEFORE, offset is subtracted from the rank of this vertex
 * plus one, to get the rank at which the given vertex is inserted.
 * If insertOrder is E4_IOAFTER, offset is added to the rank of
 * this vertex to get the rank at which the given vertex is inserted.
 * Otherwise offset is ignored.
 *
 * Note that only moves within the same storage are allowed.
 */

bool
e4_Vertex::MoveVertex(const e4_Vertex &ff,
		      e4_InsertOrder order,
		      int offset) const
{
    int moveVertexID;
    e4_Storage myStorage, hisStorage;

    if ((impl == NULL) || (!ff.IsValid()) || (ff.impl == impl)) {
	return false;
    }
    if ((!GetStorage(myStorage)) || (!ff.GetStorage(hisStorage))) {
        return false;
    }
    if (myStorage != hisStorage) {
        return false;
    }
    moveVertexID = ff.GetRawUniqueID();

    switch (order) {
    case E4_IOFIRST:
	return ((e4_VertexImpl *) impl)->MoveVertex(moveVertexID, 1);
    case E4_IOLAST:
	return ((e4_VertexImpl *) impl)->MoveVertex(moveVertexID, -1);
    case E4_IOAT:
	return ((e4_VertexImpl *) impl)->MoveVertex(moveVertexID, Rank());
    case E4_IOBEFORE:
	if (offset < 1) {
	    return false;
	}
	return ((e4_VertexImpl *) impl)->MoveVertex(moveVertexID,
						    Rank() + 1 - offset);
    case E4_IOAFTER:
	if (offset < 1) {
	    return false;
	}
	return ((e4_VertexImpl *) impl)->MoveVertex(moveVertexID,
						    Rank() + offset);
    default:
	return false;
    }
}

/*
 * Return the kind identifier for this instance's type.
 */

e4_RefKind
e4_Vertex::Kind() const
{
    return E4_RKVERTEX;
}

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

bool
e4_Vertex::GetUserData(int &userData) const
{
    if (impl == NULL) {
	return false;
    }
    return ((e4_VertexImpl *) impl)->GetUserData(userData);
}

bool
e4_Vertex::SetUserData(int userData) const
{
    if (impl == NULL) {
	return false;
    }
    return ((e4_VertexImpl *) impl)->SetUserData(userData);
}
