package org.neo4j.tutor.security;

import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

import org.neo4j.api.core.EmbeddedNeo;
import org.neo4j.api.core.Node;
import org.neo4j.api.core.Relationship;
import org.neo4j.api.core.Transaction;
import org.neo4j.impl.event.Event;
import org.neo4j.impl.event.EventData;

// creates a file structure then prints it to console
public class ListFileStructure
{
	private static AbstractSecurityMgr securityManager;
	private static EmbeddedNeo neo = null;
	
	public static void main( String[] args )
	{
		neo = new EmbeddedNeo( MyRelationshipTypes.class, "var/security", 
			true );
		// Ugly dependency injection
		FileFactory.getFactory().injectNeo( neo );
		securityManager = new DummySecuirtyManager();
		File root = null;
		Transaction tx = Transaction.begin();
		try
		{
			if ( args[0].equals( "1" ) )
			{
				if ( args.length < 2 || !args[1].equals( "off" ) )
				{
					securityManager = new SecurityMgr1();
				}
				root = setupFileStructure1();
			}
			else if ( args.length > 1 )
			{
				if ( args[0].equals( "2" ) )
				{
					root = setupFileStructure2();
					securityManager = new SecurityMgr2();
				}
				else if ( args[0].equals( "3" ) )
				{
					root = setupFileStructure3();
					securityManager = new SecurityMgr3();
				}
				else if ( args[0].equals( "4" ) )
				{
					root = setupFileStructure4();
					securityManager = new SecurityMgr4();
					System.out.println( "Listing home directory:" );
				}
				
				if ( args[1].equals( "root" ) )
				{
					AuthorizationService.authenticate( rootUser );
				}
				else if ( args[1].equals(  "user" ) )
				{
					AuthorizationService.authenticate( user );
				}
			}
			else
			{
				System.out.println( "Please pass in <root> or <user>" );
				return;
			}
			// list files
			Stack<String> stack = new Stack<String>();
			stack.push( "/" );
			securityManager.registerEventListener();
			if ( root == null )
			{
				System.out.println( "No root found." );
			}
			for ( File child : root.getChildren() )
			{
				listFilesRecursive( child, stack );
			}
		}
		finally
		{
			cleanup();
			tx.success();
			tx.finish();
			neo.shutdown();
		}
	}

	// allow everything
	private static class DummySecuirtyManager extends AbstractSecurityMgr
	{
		public boolean proActiveEventReceived( Event event, EventData data )
		{
			return true;
		}
	}
	
	private static void listFilesRecursive( File file, Stack<String> stack )
	{
		String fileName = stack.peek() + file.getFileName();
		System.out.println( fileName );
		stack.push( fileName + "/" );
		for ( File child : file.getChildren() )
		{
			listFilesRecursive( child, stack );
		}
		stack.pop();
	}

	private static List<Node> nodesToDelete = new LinkedList<Node>();
	private static Node rootUser = null;
	private static Node user = null;
	
	private static void cleanup()
	{
		for ( Node node : nodesToDelete )
		{
			for ( Relationship rel : node.getRelationships() )
			{
				rel.delete();
			}
			node.delete();
		}
	}
	
	private static File setupFileStructure1()
	{
		FileFactory factory = FileFactory.getFactory();
		File root = factory.createNewFile( "root" );
		
		nodesToDelete.add( root.getUnderlyingNode() );
		File etc = factory.createNewFile( "etc" );
		nodesToDelete.add( etc.getUnderlyingNode() );
		File tmp = factory.createNewFile( "passwd" );
		tmp.getUnderlyingNode().setProperty( File.SECURITY, 
			new Boolean( false ) );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		etc.addChild( tmp );
		tmp = factory.createNewFile( "group" );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		etc.addChild( tmp );
		root.addChild( etc );
		
		File home = factory.createNewFile( "home" );
		nodesToDelete.add( home.getUnderlyingNode() );
		File user = factory.createNewFile( "user" );
		nodesToDelete.add( user.getUnderlyingNode() );
		home.addChild( user );
		tmp = factory.createNewFile( "application.jar" );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		user.addChild( tmp );
		tmp = factory.createNewFile( "readme.txt" );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		user.addChild( tmp );
		root.addChild( home );
		
		tmp = factory.createNewFile( "usr" );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		root.addChild( tmp );
		
		File sbin = factory.createNewFile( "sbin" );
		nodesToDelete.add( sbin.getUnderlyingNode() );
		tmp = factory.createNewFile( "reboot" );
		tmp.getUnderlyingNode().setProperty( File.SECURITY, 
			new Boolean( false ) );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		sbin.addChild( tmp );
		tmp = factory.createNewFile( "fsck" );
		tmp.getUnderlyingNode().setProperty( File.SECURITY, 
			new Boolean( false ) );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		sbin.addChild( tmp );
		root.addChild( sbin );
		
		return root;
	}
	
	private static File setupFileStructure2()
	{
		rootUser = neo.createNode();
		nodesToDelete.add(  rootUser );
		user = neo.createNode();
		nodesToDelete.add( user );
		
		FileFactory factory = FileFactory.getFactory();
		File root = factory.createNewFile( "root" );
		
		nodesToDelete.add( root.getUnderlyingNode() );
		File etc = factory.createNewFile( "etc" );
		nodesToDelete.add( etc.getUnderlyingNode() );
		File tmp = factory.createNewFile( "passwd" );
		tmp.getUnderlyingNode().createRelationshipTo( rootUser, 
			MyRelationshipTypes.ACCESS );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		etc.addChild( tmp );
		tmp = factory.createNewFile( "group" );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		etc.addChild( tmp );
		root.addChild( etc );
		
		File home = factory.createNewFile( "home" );
		nodesToDelete.add( home.getUnderlyingNode() );
		File userHome = factory.createNewFile( "user" );
		userHome.getUnderlyingNode().createRelationshipTo( user, 
			MyRelationshipTypes.ACCESS );
		userHome.getUnderlyingNode().createRelationshipTo( rootUser, 
			MyRelationshipTypes.ACCESS );
		nodesToDelete.add( userHome.getUnderlyingNode() );
		home.addChild( userHome );
		tmp = factory.createNewFile( "application.jar" );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		userHome.addChild( tmp );
		tmp = factory.createNewFile( "readme.txt" );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		userHome.addChild( tmp );
		root.addChild( home );
		
		tmp = factory.createNewFile( "usr" );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		root.addChild( tmp );
		
		File sbin = factory.createNewFile( "sbin" );
		nodesToDelete.add( sbin.getUnderlyingNode() );
		tmp = factory.createNewFile( "reboot" );
		tmp.getUnderlyingNode().createRelationshipTo( rootUser, 
			MyRelationshipTypes.ACCESS );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		sbin.addChild( tmp );
		tmp = factory.createNewFile( "fsck" );
		tmp.getUnderlyingNode().createRelationshipTo( rootUser, 
			MyRelationshipTypes.ACCESS );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		sbin.addChild( tmp );
		root.addChild( sbin );
		
		return root;
	}

	private static File setupFileStructure3()
	{
		rootUser = neo.createNode();
		nodesToDelete.add(  rootUser );
		user = neo.createNode();
		nodesToDelete.add( user );
		Node group = neo.createNode();
		user.createRelationshipTo( group, MyRelationshipTypes.MEMBER_OF );
		rootUser.createRelationshipTo( group, MyRelationshipTypes.MEMBER_OF );
		
		FileFactory factory = FileFactory.getFactory();
		File root = factory.createNewFile( "root" );
		
		nodesToDelete.add( root.getUnderlyingNode() );
		File etc = factory.createNewFile( "etc" );
		nodesToDelete.add( etc.getUnderlyingNode() );
		File tmp = factory.createNewFile( "passwd" );
		tmp.getUnderlyingNode().createRelationshipTo( rootUser, 
			MyRelationshipTypes.ACCESS );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		etc.addChild( tmp );
		tmp = factory.createNewFile( "group" );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		etc.addChild( tmp );
		root.addChild( etc );
		
		File home = factory.createNewFile( "home" );
		nodesToDelete.add( home.getUnderlyingNode() );
		File userHome = factory.createNewFile( "user" );
		userHome.getUnderlyingNode().createRelationshipTo( group, 
			MyRelationshipTypes.ACCESS );
		nodesToDelete.add( userHome.getUnderlyingNode() );
		home.addChild( userHome );
		tmp = factory.createNewFile( "application.jar" );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		userHome.addChild( tmp );
		tmp = factory.createNewFile( "readme.txt" );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		userHome.addChild( tmp );
		root.addChild( home );
		
		tmp = factory.createNewFile( "usr" );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		root.addChild( tmp );
		
		File sbin = factory.createNewFile( "sbin" );
		nodesToDelete.add( sbin.getUnderlyingNode() );
		tmp = factory.createNewFile( "reboot" );
		tmp.getUnderlyingNode().createRelationshipTo( rootUser, 
			MyRelationshipTypes.ACCESS );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		sbin.addChild( tmp );
		tmp = factory.createNewFile( "fsck" );
		tmp.getUnderlyingNode().createRelationshipTo( rootUser, 
			MyRelationshipTypes.ACCESS );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		sbin.addChild( tmp );
		root.addChild( sbin );
		
		return root;
	}

	// tries to list home directory
	private static File setupFileStructure4()
	{
		rootUser = neo.createNode();
		nodesToDelete.add(  rootUser );
		user = neo.createNode();
		nodesToDelete.add( user );
		Node otherUser = neo.createNode();
		
		FileFactory factory = FileFactory.getFactory();
		File root = factory.createNewFile( "root" );
		root.getUnderlyingNode().createRelationshipTo( rootUser, 
			MyRelationshipTypes.ACCESS );
		
		File home = factory.createNewFile( "home" );
		nodesToDelete.add( home.getUnderlyingNode() );

		File userHome = factory.createNewFile( "user" );
		userHome.getUnderlyingNode().createRelationshipTo( user, 
			MyRelationshipTypes.ACCESS );
		nodesToDelete.add( userHome.getUnderlyingNode() );
		home.addChild( userHome );
		File tmp = factory.createNewFile( "application.jar" );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		userHome.addChild( tmp );
		tmp = factory.createNewFile( "readme.txt" );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		userHome.addChild( tmp );
		
		File otherHome = factory.createNewFile( "otheruser" );
		otherHome.getUnderlyingNode().createRelationshipTo( otherUser, 
			MyRelationshipTypes.ACCESS );
		nodesToDelete.add( otherHome.getUnderlyingNode() );
		home.addChild( otherHome );
		tmp = factory.createNewFile( ".bashrc" );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		otherHome.addChild( tmp );
		tmp = factory.createNewFile( ".bash_profile" );
		nodesToDelete.add( tmp.getUnderlyingNode() );
		otherHome.addChild( tmp );
		
		root.addChild( home );
		
		return home;
	}
}
