/*
 * e4vvisitor.cpp --
 *
 *	Implementation of the e4_VertexVisitor 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"

/*
 * Default constructor:
 */

e4_VertexVisitor::e4_VertexVisitor() 
    : done(true),
      detachchoice(E4_DCATTACHED),
      s(invalidStorage),
      f(invalidVertex),
      vm(E4_VMUNKNOWN),
      vf(E4_VFNONE),
      nameID(E4_VERTEXNOTFOUND),
      nodeID(E4_NODENOTFOUND),
      typeID(E4_VTUNKNOWN)
{}

/*
 * Copying constructor:
 */

e4_VertexVisitor::e4_VertexVisitor(const e4_VertexVisitor &referrer)
    : s(referrer.s),
      f(referrer.f),
      vm(referrer.vm),
      vf(referrer.vf),
      nameID(referrer.nameID),
      nodeID(referrer.nodeID),
      typeID(referrer.typeID),
      done(referrer.done),
      detachchoice(referrer.detachchoice)
{
}

/*
 * Assignment operator:
 */

e4_VertexVisitor &
e4_VertexVisitor::operator=(const e4_VertexVisitor &referrer)
{
    s = referrer.s;
    f = referrer.f;
    vm = referrer.vm;
    vf = referrer.vf;
    nameID = referrer.nameID;
    nodeID = referrer.nodeID;
    typeID = referrer.typeID;
    done = referrer.done;
    detachchoice = referrer.detachchoice;

    return * this;
}

/*
 * Comparison operators:
 */

bool
e4_VertexVisitor::operator==(const e4_VertexVisitor &compared) const
{
    return ((s == compared.s) && 
	    (f == compared.f) &&
	    (vm == compared.vm) &&
	    (vf == compared.vf) &&
	    (nameID == compared.nameID) &&
	    (nodeID == compared.nodeID) &&
	    (typeID == compared.typeID) &&
	    (done == compared.done) &&
	    (detachchoice == compared.detachchoice))
	? true : false;
}

bool
e4_VertexVisitor::operator!=(const e4_VertexVisitor &compared) const
{
    return ((s == compared.s) && 
	    (f == compared.f) &&
	    (vm == compared.vm) &&
	    (vf == compared.vf) &&
	    (nameID == compared.nameID) &&
	    (nodeID == compared.nodeID) &&
	    (typeID == compared.typeID) &&
	    (done == compared.done) &&
	    (detachchoice == compared.detachchoice))
	? false : true;
}

/*
 * Constructors that initialize from a given node.
 */

e4_VertexVisitor::e4_VertexVisitor(const e4_Node &n)
    : done(true)
{
    (void) SetNode(n);
}

e4_VertexVisitor::e4_VertexVisitor(const e4_Node &n,
				   const char *nm, 
				   e4_VertexType vt)
    : done(true)
{
    SetNode(n, nm, vt);
}

e4_VertexVisitor::e4_VertexVisitor(const e4_Node &child,
				   const e4_Node &parent,
				   e4_DetachChoice dc)
{
    SetParentVertex(child, parent, dc, NULL);
}

e4_VertexVisitor::e4_VertexVisitor(const e4_Node &child,
				   const e4_Node &parent,
				   e4_DetachChoice dc,
				   const char *nm)
{
    SetParentVertex(child, parent, dc, nm);
}

/*
 * Constructors that initialize from a given vertex.
 */

e4_VertexVisitor::e4_VertexVisitor(const e4_Vertex &vv)
    : done(true)
{
    SetVertex(vv);
}

e4_VertexVisitor::e4_VertexVisitor(const e4_Vertex &vv, 
				   bool useVertexName,
				   bool useVertexType)
    : done(true)
{
    SetVertex(vv, useVertexName, useVertexType);
}

e4_VertexVisitor::e4_VertexVisitor(const e4_Vertex &vv,
				   bool useVertexName,
				   bool useVertexType,
				   e4_VisitMethod vmvm)
    : done(true)
{
    SetVertex(vv, useVertexName, useVertexType, vmvm);
}

/*
 * Constructors that initialize from a storage.
 */

e4_VertexVisitor::e4_VertexVisitor(const e4_Storage &ss)
    : done(true)
{
    SetStorage(ss, E4_DCATTACHED);
}

e4_VertexVisitor::e4_VertexVisitor(const e4_Storage &ss,
				   const char *nm,
				   e4_VertexType vt)
    : done(true)
{
    SetStorage(ss, nm, vt, E4_DCATTACHED);
}

e4_VertexVisitor::e4_VertexVisitor(const e4_Storage &ss,
				   e4_DetachChoice dc)
    : done(true)
{
    SetStorage(ss, dc);
}

e4_VertexVisitor::e4_VertexVisitor(const e4_Storage &ss,
				   e4_DetachChoice dc,
				   const char *nm,
				   e4_VertexType vt)
{
    SetStorage(ss, nm, vt, dc);
}

/*
 * Destructor:
 */

e4_VertexVisitor::~e4_VertexVisitor()
{
    f = invalidVertex;
    s = invalidStorage;
}

/*
 * Is this visitor done visiting its node?
 */

bool
e4_VertexVisitor::IsDone()
{
    if (done) {
	return true;
    }
    if (!f.IsValid() || !s.IsValid()) {
	done = true;
    }
    return done;
}

/*
 * Retrieve the next vertex; if none, set done to true and return false.
 */

bool
e4_VertexVisitor::NextVertex(e4_Vertex &vv)
{
    if (done) {
	return false;
    }
    done = (!s.FindNextVertex(f.GetRawUniqueID(),
			      vm,
			      vf,
			      nameID,
			      nodeID,
			      parentID,
			      typeID,
			      detachchoice,
			      vv));

    if (done) {
	return false;
    }

    f = vv;
    return true;
}

/*
 * Retrieve the current vertex.
 */

bool
e4_VertexVisitor::CurrentVertex(e4_Vertex &vv)
{
    if (!f.IsValid() || !s.IsValid()) {
        done = true;
	return false;
    }
    vv = f;
    return true;
}

/*
 * Advance to the next vertex without returning it.
 */

bool
e4_VertexVisitor::Advance()
{
    if (done) {
	return false;
    }
    done = (!s.FindNextVertex(f.GetRawUniqueID(),
			      vm,
			      vf,
			      nameID,
			      nodeID,
			      parentID,
			      typeID,
			      detachchoice,
			      f));
    if (done) {
	return false;
    }
    return true;
}

/*
 * Retrieve the current vertex and advance to the next one.
 */

bool
e4_VertexVisitor::CurrentVertexAndAdvance(e4_Vertex &vv)
{
    if (!CurrentVertex(vv) || IsDone()) {
	return false;
    }
    (void) Advance();
    return true;
}

/*
 * Reset the iterator to visit all vertices in the node containing vv.
 */

bool
e4_VertexVisitor::SetVertex(const e4_Vertex &vv)
{
    e4_Storage ss;
    e4_Node n;

    /*
     * If the given vertex is invalid or we cannot get a storage from it,
     * bail out.
     */

    if (!vv.IsValid() || !vv.GetStorage(ss) || !ss.IsValid() ||
	!vv.GetNode(n) || !n.IsValid()) {
	done = true;
	return false;
    }

    f = vv;
    s = ss;
    vm = E4_VMNODE;
    vf = E4_VFNONE;
    nameID = E4_VERTEXNOTFOUND;
    nodeID = n.GetRawUniqueID();
    typeID = E4_VTUNKNOWN;
    done = false;
    detachchoice = E4_DCATTACHED;
    return true;
}

/*
 * Visit all vertices in the node containing the given vertex that
 * match the name and type constraints imposed by the given vertex.
 * If no constraints are given, vertices are visited in rank order
 * starting with the given vertex. If constraints are given, vertices
 * are visited in an implementation dependent order.
 */

bool
e4_VertexVisitor::SetVertex(const e4_Vertex &vv,
			    bool useVertexName,
			    bool useVertexType)
{
    e4_Storage ss;
    e4_Node n;

    /*
     * If the given vertex is invalid or we cannot get a storage from it,
     * bail out.
     */

    if (!vv.IsValid() || !vv.GetStorage(ss) || !ss.IsValid() ||
	!vv.GetNode(n) || !n.IsValid()) {
	return false;
    }

    detachchoice = E4_DCATTACHED;
    s = ss;
    vm = E4_VMNODE;
    vf = E4_VFNONE;
    nodeID = n.GetRawUniqueID();
    if (useVertexName) {
	vf |= E4_VFNAME;
	nameID = s.InternName(vv.Name());
    } else {
	nameID = E4_VERTEXNOTFOUND;
    }
    if (useVertexType) {
        vf |= E4_VFTYPE;
	typeID = vv.Type();
    }
    if (vf == E4_VFNONE) {
	f = vv;
	done = false;
    } else {
	done = (!s.FindNextVertex(E4_VERTEXNOTFOUND,
				  vm,
				  vf,
				  nameID,
				  nodeID,
				  parentID,
				  typeID,
				  detachchoice,
				  f));
    }
    return true;
}

/*
 * Same as above except use the supplied visit method to find the
 * next vertex to visit.
 *
 * E4_VMNODE		Visit vertices in the node that is the value of the
 *			given vertex. Visit order is rank order.
 * E4_VMNODERANDOM	Same as above except visit order is random.
 * E4_VMPARENT		The given vertex is expected to have a node value.
 *			Visit all attached vertices that have this node as
 *			their value.
 */

bool
e4_VertexVisitor::SetVertex(const e4_Vertex &vv,
			    bool useVertexName,
			    bool useVertexType,
			    e4_VisitMethod vmvm)
{
    e4_Storage ss;
    e4_Node n;

    if (!vv.IsValid() || !vv.GetStorage(ss) || !ss.IsValid() ||
	!vv.GetNode(n) || !n.IsValid()) {
	return false;
    }
    detachchoice = E4_DCATTACHED;
    s = ss;
    vm = vmvm;
    vf = E4_VFNONE;
    nameID = E4_VERTEXNOTFOUND;
    nodeID = n.GetRawUniqueID();
    typeID = E4_VTUNKNOWN;
    if (useVertexName) {
	vf |= E4_VFNAME;
	nameID = s.InternName(vv.Name());
    }
    if (useVertexType) {
	vf |= E4_VFTYPE;
	typeID = vv.Type();
    }
    if (vm == E4_VMNODE) {
	f = vv;
	done = false;
    } else {
        done = (!s.FindNextVertex(E4_VERTEXNOTFOUND,
				  vm,
				  vf,
				  nameID,
				  nodeID,
				  parentID,
				  typeID,
				  detachchoice,
				  f));
    }
    return true;
}

/*
 * Reset the instance to the first vertex of the given node.
 */

bool
e4_VertexVisitor::SetNode(const e4_Node &n)
{
    e4_Storage ss;

    /*
     * Bail out if we cannot set the visitor up to visit the
     * provided node.
     */

    if (!n.IsValid() || !n.GetStorage(ss) || !ss.IsValid()) {
	done = true;
	return false;
    }

    /*
     * If the node is empty, mark this visitor as done.
     */

    done = (!n.GetVertexRefByRank(1, f) || !f.IsValid());
    s = ss;
    vm = E4_VMNODE;
    vf = E4_VFNONE;
    nameID = E4_VERTEXNOTFOUND;
    nodeID = n.GetRawUniqueID();
    parentID = E4_NODENOTFOUND;
    typeID = E4_VTUNKNOWN;
    detachchoice = E4_DCATTACHED;

    return true;
}

/*
 * Reset this visitor to the first vertex of the given node that
 * matches the given name and type constraints.
 */

bool
e4_VertexVisitor::SetNode(const e4_Node &n, const char *nm, e4_VertexType vt)
{
    e4_Storage ss;

    /*
     * Bail out if the visitor cannot be set up using the
     * supplied node.
     */

    if (!n.IsValid() || !n.GetStorage(ss)) {
        done = true;
        return false;
    }
    s = ss;
    vf = E4_VFNONE;
    vm = E4_VMNODE;
    typeID = vt;
    nodeID = n.GetRawUniqueID();
    parentID = E4_NODENOTFOUND;
    if (nm != NULL) {
	nameID = s.InternName(nm);
	vf |= E4_VFNAME;
    }
    if (vt != E4_VTUNKNOWN) {
	vf |= E4_VFTYPE;
    }

    /*
     * If the node is empty or contains no vertices matching the
     * requested constraints, mark this visitor as done.
     */

    detachchoice = E4_DCATTACHED;
    done = (!s.FindNextVertex(E4_VERTEXNOTFOUND, 
			      vm,
			      vf,
			      nameID,
			      nodeID, 
			      parentID,
			      typeID,
			      detachchoice,
			      f));
    return true;
}

/*
 * Reset the iterator to visit vertices that have the child node as their
 * value. If the parent is valid, the iterator only visits vertices within
 * that parent of the child, otherwise it visits all vertices that have the
 * child node as their value, as selected by the detachchoice.
 */

bool
e4_VertexVisitor::SetParentVertex(const e4_Node &child,
				  const e4_Node &parent,
				  e4_DetachChoice dc,
				  const char *nm)
{
    e4_Storage ss;

    if (!child.IsValid() || !child.GetStorage(ss) || !ss.IsValid()) {
	done = true;
	return false;
    }
    nodeID = child.GetRawUniqueID();
    typeID = E4_VTNODE;
    detachchoice = dc;
    s = ss;
    if (parent.IsValid()) {
	if (!parent.GetStorage(ss) || !ss.IsValid() || (s != ss)) {
	    done = true;
	    return false;
	}
	parentID = parent.GetRawUniqueID();
    } else {
	parentID = E4_NODENOTFOUND;
    }
    if (nm == NULL) {
	vf = E4_VFNONE;
	nameID = E4_INVALIDUNIQUEID;
    } else {
	vf = E4_VFNAME;
	nameID = s.InternName(nm);
    }
    vm = E4_VMPARENT;
    done = (!s.FindNextVertex(E4_VERTEXNOTFOUND,
			      vm,
			      vf,
			      nameID,
			      nodeID,
			      parentID,
			      typeID,
			      detachchoice,
			      f));
    return true;
}

/*
 * Reset the iterator to visit all attached vertices in a given storage.
 */

bool
e4_VertexVisitor::SetStorage(const e4_Storage &ss)
{
    return SetStorage(ss, E4_DCATTACHED);
}

/*
 * Reset the iterator to visit all vertices in a given storage. The
 * order in which vertices are visited is implementation dependent.
 */

bool
e4_VertexVisitor::SetStorage(const e4_Storage &ss, e4_DetachChoice dc)
{
    if (!ss.IsValid()) {
	return false;
    }
    s = ss;
    vf = E4_VFNONE;
    vm = E4_VMSTORAGE;
    nameID = E4_VERTEXNOTFOUND;
    nodeID = E4_NODENOTFOUND;
    parentID = E4_NODENOTFOUND;
    typeID = E4_VTUNKNOWN;

    /*
     * If the given storage is empty, mark this visitor as done.
     */

    detachchoice = dc;
    done = (!s.FindNextVertex(E4_VERTEXNOTFOUND,
			      vm,
			      vf,
			      nameID,
			      nodeID,
			      parentID,
			      typeID,			      
			      detachchoice,
			      f));
    return true;
}

/*
 * Visit all attached vertices in the given storage that match the supplied
 * name and type constraints.
 */

bool
e4_VertexVisitor::SetStorage(const e4_Storage &ss,
			     const char *nm,
			     e4_VertexType vt)
{
    return SetStorage(ss, nm, vt, E4_DCATTACHED);
}

/*
 * Visit all vertices in the given storage that match the supplied
 * name and type constraints. Vertices are visited in an implementation
 * dependent order.
 */

bool
e4_VertexVisitor::SetStorage(const e4_Storage &ss, 
			     const char *nm, 
			     e4_VertexType vt,
			     e4_DetachChoice dc)
{
    if (!ss.IsValid()) {
	return false;
    }
    s = ss;
    vf = E4_VFNONE;
    vm = E4_VMSTORAGE;
    nodeID = E4_NODENOTFOUND;
    parentID = E4_NODENOTFOUND;
    typeID = vt;
    if (nm != NULL) {
	nameID = s.InternName(nm);
	vf |= E4_VFNAME;
    } else {
	nameID = E4_VERTEXNOTFOUND;
    }
    if (vt != E4_VTUNKNOWN) {
	vf |= E4_VFTYPE;
    }

    /*
     * If the storage is empty or contains no vertices matching the
     * supplied name and type constraints, mark this visitor
     * as done.
     */

    detachchoice = dc;
    done = (!s.FindNextVertex(E4_VERTEXNOTFOUND, 
			      vm,
			      vf,
			      nameID,
			      nodeID,
			      parentID,
			      typeID,
			      detachchoice,
			      f));
    return true;
}

/*
 * Is this instance of e4_VertexVisitor valid?
 */

bool
e4_VertexVisitor::IsValid()
{
    if (!s.IsValid()) {
	done = true;
	return false;
    }
    return true;
}

/*
 * Return the vertex name used to select the vertices to visit.
 */

const char *
e4_VertexVisitor::NameFilter() const
{
    if ((!((e4_VertexVisitor *) this)->IsValid()) || 
	(nameID == E4_VERTEXNOTFOUND)) {
	return NULL;
    }
    return s.GetName(nameID);
}

/*
 * Return the node currently being visited (if in visitmode E4_VMNODE or
 * E4_VMNODERANDOM). Return the node for which this iterator visits all
 * parent vertices, if visitmode is E4_VMPARENT.
 */

bool
e4_VertexVisitor::NodeVisited(e4_Node &n) const
{
    e4_NodeImpl *nnip;

    if (!s.IsValid()) {
	return false;
    }
    nnip = s.GetNode(nodeID);
    if (nnip == NULL) {
	return false;
    }

    e4_Node nn(nnip);

    n = nn;

    return true;
}
