package org.neo4j.tutor.tree;

import java.util.AbstractCollection;
import java.util.Iterator;
import org.neo4j.api.core.Node;
import org.neo4j.api.core.RelationshipType;
import org.neo4j.api.core.Traverser;

public class File
{
	// property key for the file name
	static final String NAME = "filename";
	private static final RelationshipType FILE = MyRelationshipTypes.FILE;
	
	private Node underlyingNode;
	
	File( Node underlyingNode )
	{
		this.underlyingNode = underlyingNode;
	}
	
	Node getUnderlyingNode()
	{
		return underlyingNode;
	}
	
	/**
	 * Returns the name of this file.
	 * 
	 * @return the name of this file
	 */
	public String getFileName()
	{
		return (String) underlyingNode.getProperty( NAME );
	}
	
	/**
	 * Changes the name of this file.
	 * 
	 * @param newName the new name of the file
	 * @throws IllegalArgumentException if <code>null newName</code> 
	 */
	public void changeFileName( String newName )
	{
		// --- TODO set the new file name property on the underlying node
		// --- If new name is null throw IllegalArgumentException
	}
	
	/**
	 * Returns the parent of this file or <code>null</code> if this has no 
	 * parent.
	 * 
	 * @return the parent of this file
	 */
	public File getParent()
	{
		// --- TODO 
		// --- check if has parent then return it else return null
		// --- file has a parent if an incoming relationship of type FILE exist
		return null;
	}
	
	/**
	 * Adds a child to this file.
	 * 
	 * @param child the child to add
	 * @throws IllegalArgumentException if null child or child already has a 
	 * parent
	 */
	public void addChild( File child )
	{
		// --- TODO
		// --- verify that child doesn't have parent, if it has parent throw
		// --- IllegalArgumentException
		// --- connect this file to child via FILE relationship
	}

	/**
	 * Disconnects this file from its parent. If this file dosen't have a 
	 * parent nothing is done.
	 */
	public void disconnectFromParent()
	{
		// --- TODO
		// --- delete FILE relationship between this and parent
		// --- if this file doesn't have a parent do nothing
	}
	
	/**
	 * Returns all children of this file. If this file has no children an 
	 * empty iterable is returned.
	 * 
	 * @return the children of this file
	 */
	public Iterable<File> getChildren()
	{
		// --- TODO 
		// --- create traverser that returns all files under this file with
		// --- a depth limit of 1
		// --- also the start node (this) shouldn't be returned
		// --- pass the traverser into a FileIterable instance and return
		// --- something like:
		// --- return new FileIteratble( underlyingNode.traverse( ... )
		return null;
	}
	
	// use this to convert from a Iterable<Node> to Iterable<File>
	// ex. return new FileIterable( node.traverse( ... ) );
	private static class FileIterable extends AbstractCollection<File>
	{
		private final Traverser fileTraverser;
		
		FileIterable( Traverser fileTraverser )
		{
			this.fileTraverser = fileTraverser;
		}
		
		@Override
		public Iterator<File> iterator()
		{
			return new Iterator<File>()
			{
				public boolean hasNext()
				{
					return fileTraverser.iterator().hasNext();
				}

				public File next()
				{
					return new File( fileTraverser.iterator().next() );
				}

				public void remove()
				{
					throw new UnsupportedOperationException();
				}
			};
		}

		@Override
		public int size()
		{
			throw new UnsupportedOperationException();		
		}
	}
	
	/**
	 * Deletes this file. If this file has a parent it will be disconnected 
	 * from that parent. If this file has children they will be deleted too.
	 */
	public void delete()
	{
		// --- TODO
		// --- if has parent disconnect from it
		// --- delete all children
		// --- delete this node
 	}
	
	@Override
	public boolean equals( Object o )
	{
		if ( !( o instanceof File ) )
		{
			return false;
		}
		return this.getUnderlyingNode().equals( 
			( ( File ) o ).getUnderlyingNode() );
	}

	@Override
	public int hashCode()
	{
		return underlyingNode.hashCode();
	}
}
