/*
 * Copyright 2002-2007 Network Engine for Objects in Lund AB [neotechnology.com]
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.neo4j.impl.shell;

import java.rmi.RemoteException;
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.Transaction;
import org.neo4j.impl.core.NodeManager;
import org.neo4j.util.shell.AbstractApp;
import org.neo4j.util.shell.AppCommandParser;
import org.neo4j.util.shell.Output;
import org.neo4j.util.shell.Session;
import org.neo4j.util.shell.ShellException;

/**
 * Completely server-side
 */
public abstract class NeoApp extends AbstractApp
{
	private static final String NODE_KEY = "CURRENT_NODE";
	
	protected static Node getCurrentNode( Session session )
	{
		Number id = ( Number ) safeGet( session, NODE_KEY );
		Node node = null;
		if ( id == null )
		{
			node = NodeManager.getManager().getReferenceNode();
			setCurrentNode( session, node );
		}
		else
		{
			node = NodeManager.getManager().getNodeById( id.intValue() );
		}
		return node;
	}
	
	protected static void setCurrentNode( Session session, Node node )
	{
		safeSet( session, NODE_KEY, node.getId() );
	}
	
	private NeoShellServer getNeoServer()
	{
		return ( NeoShellServer ) this.getServer();
	}
	
	protected RelationshipType getRelationshipType( String name )
	{
		// this.ensureRelTypesInitialized();
		RelationshipType result = ( ( EmbeddedNeo ) this.getNeoServer().
			getNeo() ).getRelationshipType( name );
		if ( result == null )
		{
			throw new RuntimeException( "No relationship type '" + name +
				"' found" );
		}
		return result;
	}
	
	protected Direction getDirection( String direction ) throws ShellException
	{
		return this.getDirection( direction, Direction.OUTGOING );
	}

	protected Direction getDirection( String direction,
		Direction defaultDirection ) throws ShellException
	{
		if ( direction == null )
		{
			return defaultDirection;
		}
		
		Direction result = null;
		try
		{
			result = Direction.valueOf( direction.toUpperCase() );
		}
		catch ( Exception e )
		{
			if ( direction.equalsIgnoreCase( "o" ) )
			{
				result = Direction.OUTGOING;
			}
			else if ( direction.equalsIgnoreCase( "i" ) )
			{
				result = Direction.INCOMING;
			}
		}
		
		if ( result == null )
		{
			throw new ShellException( "Unknown direction " + direction +
				" (may be " + directionAlternatives() + ")" );
		}
		return result;
	}
	
	protected Node getNodeById( long id ) throws ShellException
	{
		return this.getNeoServer().getNeo().getNodeById( id );
	}

	public final String execute( AppCommandParser parser, Session session,
		Output out ) throws ShellException
	{
		Transaction tx = Transaction.begin();
		try
		{
			String result = this.exec( parser, session, out );
			tx.success();
			return result;
		}
		catch ( RemoteException e )
		{
			throw new ShellException( e );
		}
		finally
		{
			tx.finish();
		}
	}
	
	protected String directionAlternatives()
	{
		return "OUTGOING, INCOMING, o, i";
	}
	
	protected abstract String exec( AppCommandParser parser, Session session,
		Output out ) throws ShellException, RemoteException;
	
	
	protected String getDisplayNameForCurrentNode()
	{
		return "(me)";
	}

	public static String getDisplayNameForNode( Node node )
	{
		return node != null
			? getDisplayNameForNode( node.getId() )
			: getDisplayNameForNode( (Long) null );
	}
	
	public static String getDisplayNameForNode( Long nodeId )
	{
		return "(" + nodeId + ")";
	}
}
