package org.neo4j.tutor.matrix;

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

public class Matrix
{
	private static EmbeddedNeo neo = null;
	
	// Relationship types that will be used in this assignment
	private static enum MyRelationshipTypes implements RelationshipType
	{
		KNOWS,
		CODED_BY
	}
	
	public static void main( String args[] )
	{
		// start Neo
		neo = new EmbeddedNeo( MyRelationshipTypes.class, "var/matrix", true );
		
		// start a transaction (every operation performed against the node 
		// space needs to be in a transactional context
		Transaction tx = Transaction.begin();
		try
		{
			Node neoNode = setupMatrix();
			printFriends( neoNode );
			findHackers( neoNode );
			
			// mark transaction as successful, if this is not done the 
			// tx.finish() in the finally clause will rollback the transaction
			tx.success();
		}
		finally
		{
			// commit transaction and shutdown Neo
			tx.finish();
			neo.shutdown();
		}
	}
	
	/**
	 * Creates a social network from the Matrix movie.
	 * 
	 * @return the node representing the person "Thomas Anderson"
	 */
	private static Node setupMatrix()
	{
		// TODO: create the matrix social network outlined in assignment 1
		// at http://wiki.neo4j.org/content/Neo_Workshop_Lab_Instructions
		// Nodes are already created for you and correct properties are also
		// added to the nodes, what needs to be done is to create the 
		// missing relationships between the nodes.
		
		Node thomas = neo.createNode();
		thomas.setProperty( "name", "Thomas Andersson" );
		thomas.setProperty( "age", 29 );
		
		Node trinity = neo.createNode();
		trinity.setProperty( "name", "Trinity" );
		
		// TODO: create KNOWS relationship between thomas and trinity node, 
		// set the property "age" on the created relationship to "3 days"
		
		Node morpheus = neo.createNode();
		morpheus.setProperty( "name", "Morpheus" );
		morpheus.setProperty( "rank", "Captain" );
		morpheus.setProperty( "occupation", "Total badass" );
		// TODO: create KNOWS relationship between thomas and morpheus,  
		// create KNOWS relationship between morpheus and trinity and set the 
		// "age" property to "12 years"
		
		Node cypher = neo.createNode();
		cypher.setProperty( "name", "Cypher" );
		cypher.setProperty( "last name", "Reagan" );
		// TODO: create KNOWS relationship between morpheus and cypher and 
		// set the property "disclosure" to "public"
		
		Node smith = neo.createNode();
		smith.setProperty( "name", "Agent Smith" );
		smith.setProperty( "version", "1.0b" );
		smith.setProperty( "language", "C++" );
		// TODO: create KNOWS relationship between cypher and smith, set the 
		// property "disclosure" to "secret" and property "age" to "6 months"
		
		Node architect = neo.createNode();
		architect.setProperty( "name", "The Architect" );
		// TODO: create CODED_BY relationship between smith and the architect

		return thomas;
	}
	
	/**
	 * Prints all the friends of a person using a traverser.
	 * 
	 * @param startNode the person to start the traversal from
	 */
	private static void printFriends( Node startNode )
	{
		String name = startNode.toString();
		if ( startNode.hasProperty( "name" ) )
		{
			name = (String) startNode.getProperty( "name" );
		}
		System.out.println( name + "'s friends: " );
		
		// TODO: modify the traverser to display all friends not just the first
		// level
		Traverser traverser = startNode.traverse( Order.BREADTH_FIRST, 
			StopEvaluator.DEPTH_ONE, 
			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" ) );
		}
	}
	
	/**
	 * Finds all hackers of the Matrix by traversing from a 
	 * <code>startNode</code> trying to make a jump via the 
	 * <code>CODED_BY</code> relationship.
	 * 
	 * @param startNode the node to start the traversal from
	 */
	private static void findHackers( Node startNode )
	{		
		System.out.println( "\nAll hackers: " );
		
		// TODO: find all hackers from startNode by traversing the 
		// KNOWS and CODED_BY relationships and print them to console.
		// Write your own returnable evaluator that use the current traversal
		// position and checks if last relationship traversed is CODED_BY, 
		// if so you found a hacker
	}
}
