/*
 * Copyright (c) 2002-2010 "Neo Technology,"
 *     Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 * 
 * Neo4j 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.kernel;

import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.Map;

import org.neo4j.graphdb.GraphDatabaseService;

/**
 * Can start and stop a shell server via reflection if the shell should happen
 * to be on the classpath, else it will gracefully say that it isn't there.
 */
class ShellService
{
    private final GraphDatabaseService graphDb;
    private final Object shellServer;

    ShellService( GraphDatabaseService graphDb, Map<String,Serializable> config )
        throws ShellNotAvailableException, RemoteException
    {
        this.graphDb = graphDb;
        if ( !shellDependencyAvailable() )
        {
            throw new ShellNotAvailableException();
        }
        this.shellServer = startShellServer( config );
    }

    private boolean shellDependencyAvailable()
    {
        try
        {
            Class.forName( "org.neo4j.shell.ShellServer" );
            return true;
        }
        catch ( Throwable t )
        {
            return false;
        }
    }

    private Object startShellServer( Map<String,Serializable> config )
        throws RemoteException
    {
        Integer port = (Integer) getConfig( config, "port", "DEFAULT_PORT" );
        String name = (String) getConfig( config, "name", "DEFAULT_NAME" );
        try
        {
            Class<?> shellServerClass =
                Class
                    .forName( "org.neo4j.shell.kernel.GraphDatabaseShellServer" );
            Object shellServer =
                shellServerClass.getConstructor( GraphDatabaseService.class )
                    .newInstance( graphDb );
            shellServer.getClass().getMethod( "makeRemotelyAvailable",
                Integer.TYPE, String.class ).invoke( shellServer, port, name );
            return shellServer;
        }
        catch ( Exception e )
        {
            throw new RemoteException( "Couldn't start shell '" + name
                + "' at port " + port, e );
        }
    }

    private Serializable getConfig( Map<String,Serializable> config,
        String key, String defaultVariableName ) throws RemoteException
    {
        Serializable result = config.get( key );
        if ( result == null )
        {
            try
            {
                result = (Serializable) Class.forName(
                        "org.neo4j.shell.impl.AbstractServer" )
                        .getDeclaredField( defaultVariableName ).get( null );
            }
            catch ( Exception e )
            {
                throw new RemoteException( "Default variable not found", e );
            }
        }
        return result;
    }

    public boolean shutdown() throws ShellNotAvailableException
    {
        try
        {
            shellServer.getClass().getMethod( "shutdown" ).invoke( shellServer );
            return true;
        }
        catch ( Exception e )
        {
            // TODO Really swallow this? Why not, who cares?
            return false;
        }
    }

    static class ShellNotAvailableException extends Exception
    {
        public ShellNotAvailableException()
        {
            super();
        }
    }
}