package com.limegroup.gnutella.gui;

import com.limegroup.gnutella.bugs.BugManager;
import com.limegroup.gnutella.RouterService;
import com.limegroup.gnutella.gui.notify.NotifyUserProxy;
import com.limegroup.gnutella.settings.SettingsHandler;

/**
 * This class provides the "shutdown" method that should be
 * the only method for closing the application for production
 * (non_testing) code.  This method makes sure that all of
 * the necessary classes are notified that the virtual machine
 * is about to be exited.
 */
final class Finalizer {
    
    /** Stores the connection status before a shutdown
     * operation is initiated.
     */    
    private static boolean _wasConnected;
    
    /** Stores whether a shutdown operation has been
     * initiated.
     */    
    private static boolean _shutdownImminent;
    
    /** Indicates whether file uploads are complete.
     */    
    private static boolean _uploadsComplete;
    
    /** Indicates whether file downloads are complete.
     */    
    private static boolean _downloadsComplete;    
    
    /**
     * An update command to execute upon shutdown, if any.
     */
    private static volatile String _updateCommand;

	/**
	 * Suppress the default constructor to ensure that this class can never
	 * be constructed.
	 */
	private Finalizer() {}
    
    /** Indicates whether the application is waiting to
     * shutdown.
     * @return true if the application is waiting to
     * shutdown, false otherwise
     */    
    static boolean isShutdownImminent() {
        return _shutdownImminent;
    }
    
    /**
     * Exits the virtual machine, making calls to save
     * any necessary settings and to perform any
     * necessary cleanups.
     * @param toExecute a string to try to execute after shutting down.
     */
    static void shutdown() {
        GUIMediator.applyWindowSettings();
        
        GUIMediator.setAppVisible(false);
        new ShutdownWindow().setVisible(true);
        
        // remove any user notification icons
        NotifyUserProxy.instance().removeNotify();
        
        // Do shutdown stuff in another thread.
        // We don't want to lockup the event thread
        // (which this was called on).
        final String toExecute = _updateCommand;
        Thread shutdown = new Thread("Shutdown Thread") {
            public void run() {
                try {
                    BugManager.instance().shutdown();

                    RouterService router = GUIMediator.instance().getRouter();
                    if (router != null)
                        RouterService.shutdown(toExecute);
                    // if the router does not exist, we still need
                    // to write out the properties.
                    else
                        SettingsHandler.save();
                    
                    System.exit(0);
                } catch(Throwable t) {
                    t.printStackTrace();
                    System.exit(0);
                }
            }
        };
        shutdown.start();
    }
    
    static void flagUpdate(String toExecute) {
        _updateCommand = toExecute;
    }
    
    /** Exits the virtual machine, making calls to save
     * any necessary settings and to perform any
     * necessary cleanups, after all incoming and
     * outgoing transfers are complete.
     */    
    static void shutdownAfterTransfers() {
        if (isShutdownImminent())
            return;
        
        _shutdownImminent = true;
        
        _wasConnected = RouterService.isConnected();
	
		RouterService.setIsShuttingDown(true);
	
        if (_wasConnected)
            RouterService.disconnect();
        
        if (transfersComplete())
            GUIMediator.shutdown();
    }
    
    /** Cancels a pending shutdown operation.
     */    
    public static void cancelShutdown() {
        _shutdownImminent = false;
        _uploadsComplete = false;
        _downloadsComplete = false;
        
        if (_wasConnected) {
            RouterService.connect();
		}
		
		RouterService.setIsShuttingDown(false);

    }
    
    /** Notifies the <tt>Finalizer</tt> that all
     * downloads have been completed.
     */    
    static void setDownloadsComplete() {
        _downloadsComplete = true;
        checkForShutdown();
    }
    
    /** Notifies the <tt>Finalizer</tt> that all uploads
     * have been completed.
     */    
    static void setUploadsComplete() {
        _uploadsComplete = true;
        checkForShutdown();
    }
    
    /** Indicates whether all incoming and outgoing
     * transfers have completed at the time this method
     * is called.
     * @return true if all transfers have been
     * completed, false otherwise.
     */    
    private static boolean transfersComplete() {
		RouterService router = GUIMediator.instance().getRouter();
        if (router == null)
            return true;
        
        if (RouterService.getNumDownloads() == 0)
            _downloadsComplete = true;
        if (RouterService.getNumUploads() == 0)
            _uploadsComplete = true;
        
        return _uploadsComplete & _downloadsComplete;
    }
    
    /** Attempts to shutdown the application.  This
     * method does nothing if all file transfers are
     * not yet complete.
     */    
    private static void checkForShutdown() {
        if(_shutdownImminent && _uploadsComplete && _downloadsComplete) {
            GUIMediator.shutdown();
        }
    }
    
    /**
     * Adds the specified <tt>Finalizable</tt> instance to the list of
     * classes to notify prior to shutdown.
     * 
     * @param fin the <tt>Finalizable</tt> instance to register
     */
    static void addFinalizeListener(final FinalizeListener fin) {
        Thread t = new Thread("FinalizeItem") {
            public void run() {
                fin.doFinalize();
            }
        };
        RouterService.addShutdownItem(t);
    }
    
}
