package org.neo4j.tutor.matrix;

import org.neo4j.api.core.EmbeddedNeo;
import org.neo4j.api.core.Direction;
import org.neo4j.api.core.Node;
import org.neo4j.api.core.Relationship;
import org.neo4j.api.core.RelationshipType;
import org.neo4j.api.core.ReturnableEvaluator;
import org.neo4j.api.core.StopEvaluator;
import org.neo4j.api.core.TraversalPosition;
import org.neo4j.api.core.Traverser;
import org.neo4j.api.core.Traverser.Order;
import org.neo4j.api.core.Transaction;

public class Matrix
{
	private static EmbeddedNeo neo = null;
	
	private static enum MyRelationshipTypes implements RelationshipType
	{
		KNOWS,
		CODED_BY
	}
	
	
	public static void main( String args[] )
	{
		neo = new EmbeddedNeo( MyRelationshipTypes.class, "var/matrix", true );

		Transaction tx = Transaction.begin();
		try
		{
			Node neoNode = setupMatrix();
			printFriends( neoNode );
			findHackers( neoNode );
			tx.success();
		}
		finally
		{
			tx.finish();
			neo.shutdown();
		}
	}
	
	private static Node setupMatrix()
	{
		Node thomas = neo.createNode();
		thomas.setProperty( "name", "Thomas Andersson" );
		thomas.setProperty( "age", 29 );
		
		Node trinity = neo.createNode();
		trinity.setProperty( "name", "Trinity" );
		Relationship rel = thomas.createRelationshipTo( trinity, 
			MyRelationshipTypes.KNOWS );
		rel.setProperty( "age", "3 days" );
		
		Node morpheus = neo.createNode();
		morpheus.setProperty( "name", "Morpheus" );
		morpheus.setProperty( "rank", "Captain" );
		morpheus.setProperty( "occupation", "Total badass" );
		thomas.createRelationshipTo( morpheus, MyRelationshipTypes.KNOWS );		
		rel = morpheus.createRelationshipTo( trinity, 
			MyRelationshipTypes.KNOWS );
		rel.setProperty( "age", "12 years" );
		
		Node cypher = neo.createNode();
		cypher.setProperty( "name", "Cypher" );
		cypher.setProperty( "last name", "Reagan" );
		rel = morpheus.createRelationshipTo( cypher, 
			MyRelationshipTypes.KNOWS );
		rel.setProperty( "disclosure", "public" );
		
		Node smith = neo.createNode();
		smith.setProperty( "name", "Agent Smith" );
		smith.setProperty( "version", "1.0b" );
		smith.setProperty( "language", "C++" );
		rel = cypher.createRelationshipTo( smith, MyRelationshipTypes.KNOWS );
		rel.setProperty( "disclosure", "secret" );
		rel.setProperty( "age", "6 months" );
		
		Node architect = neo.createNode();
		architect.setProperty( "name", "The Architect" );
		smith.createRelationshipTo( architect, MyRelationshipTypes.CODED_BY );
		return thomas;
	}
	
	private static void printFriends( Node person )
	{
		System.out.println( person.getProperty( "name" ) + "'s friends:" );
		Traverser traverser = person.traverse( Order.BREADTH_FIRST, 
			StopEvaluator.END_OF_NETWORK, 
			ReturnableEvaluator.ALL_BUT_START_NODE, 
			MyRelationshipTypes.KNOWS, Direction.OUTGOING );
		for ( Node friend : traverser )
		{
			TraversalPosition position = traverser.currentPosition();
			System.out.println( "At depth " + position.depth() + 
				" => " + friend.getProperty( "name" ) );
		}
	}
	
	private static void findHackers( Node startNode )
	{
		System.out.println( "Hackers:" );
		Traverser traverser = startNode.traverse( Order.DEPTH_FIRST, 
			StopEvaluator.END_OF_NETWORK, new ReturnableEvaluator()
			{
				public boolean isReturnableNode( 
					TraversalPosition currentPosition )
				{
					Relationship rel = 
						currentPosition.lastRelationshipTraversed();
					if ( rel != null && rel.getType().equals( 
						MyRelationshipTypes.CODED_BY ) )
					{
						return true;
					}
					return false;
				}
			}, MyRelationshipTypes.CODED_BY, Direction.OUTGOING, 
			MyRelationshipTypes.KNOWS, Direction.OUTGOING );
		for ( Node hacker : traverser )
		{
			TraversalPosition position = traverser.currentPosition();
			System.out.println( "At depth " + position.depth() + 
				" => " + hacker.getProperty( "name" ) );
		}
	}
}
