package org.neo4j.kernel.impl.traversal;

import java.util.Collections;
import java.util.Iterator;

import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipExpander;
import org.neo4j.graphdb.traversal.TraversalBranch;
import org.neo4j.kernel.impl.traversal.TraverserImpl.TraverserIterator;

class ExpansionSourceImpl implements TraversalBranch
{
    private final TraversalBranch parent;
    private final Node source;
    private Iterator<Relationship> relationships;
    private final Relationship howIGotHere;
    private final int depth;
    final TraverserIterator traverser;
    private Path path;
    private int expandedCount;

    /*
     * For expansion sources for all nodes except the start node
     */
    ExpansionSourceImpl( TraverserIterator traverser, TraversalBranch parent, int depth,
            Node source, RelationshipExpander expander, Relationship toHere )
    {
        this.traverser = traverser;
        this.parent = parent;
        this.source = source;
        this.howIGotHere = toHere;
        this.depth = depth;
        expandRelationships( true );
    }

    /*
     * For the start node expansion source
     */
    ExpansionSourceImpl( TraverserIterator  traverser, Node source,
            RelationshipExpander expander )
    {
        this.traverser = traverser;
        this.parent = null;
        this.source = source;
        this.howIGotHere = null;
        this.depth = 0;
    }

    protected void expandRelationships( boolean doChecks )
    {
        boolean okToExpand = !doChecks || traverser.shouldExpandBeyond( this );
        relationships = okToExpand ?
                traverser.description.expander.expand( source ).iterator() :
                Collections.<Relationship>emptyList().iterator();
    }

    protected boolean hasExpandedRelationships()
    {
        return relationships != null;
    }

    public TraversalBranch next()
    {
        while ( relationships.hasNext() )
        {
            Relationship relationship = relationships.next();
            if ( relationship.equals( howIGotHere ) )
            {
                continue;
            }
            expandedCount++;
            Node node = relationship.getOtherNode( source );
            TraversalBranch next = new ExpansionSourceImpl( traverser, this, depth + 1, node,
                    traverser.description.expander, relationship );
            if ( traverser.okToProceed( next ) )
            {
                return next;
            }
        }
        return null;
    }

    public Path position()
    {
        if ( this.path == null )
        {
            this.path = new TraversalPath( this );
        }
        return this.path;
    }

    public int depth()
    {
        return depth;
    }

    public Relationship relationship()
    {
        return howIGotHere;
    }

    public Node node()
    {
        return source;
    }

    public TraversalBranch parent()
    {
        return this.parent;
    }

    public int expanded()
    {
        return expandedCount;
    }
}
