/**
 * Copyright (C) 2013 Alexander Szczuczko
 *
 * This file may be modified and distributed under the terms
 * of the MIT license. See the LICENSE file for details.
 */
package ca.szc.keratin.core.net;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;

import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;

import net.engio.mbassy.bus.BusConfiguration;

import org.pmw.tinylog.Logger;

import ca.szc.keratin.core.net.handlers.BusErrorHandler;
import ca.szc.keratin.core.net.handlers.DeadMessageHandler;
import ca.szc.keratin.core.net.handlers.ServerPingHandler;
import ca.szc.keratin.core.net.io.ConnectionThread;
import ca.szc.keratin.core.net.io.OutputQueue;
import ca.szc.keratin.core.net.mbassador.MBassadorWrapper;
import ca.szc.keratin.core.net.mbassador.TimeoutSubscriptionFactory;
import ca.szc.keratin.core.net.util.InvalidPortException;
import ca.szc.keratin.core.net.util.TrustAllTrustManager;

public class IrcConnection
{
    private final SocketFactory socketFactory;

    private InetSocketAddress endpoint;

    private MBassadorWrapper bus;

    private OutputQueue outputQueue;

    private ConnectionThread connectionThread;

    /**
     * Defines the different use states of SSL. See the javadoc of the members for more information.
     */
    public enum SslMode
    {
        /**
         * Do not use SSL
         */
        OFF,

        /**
         * Enable full SSL
         */
        ON,

        /**
         * Enable SSL without host verification
         */
        ON_NOHOST
    }

    /**
     * Create an IRC connection with a String address and SSL disabled. The connection is not active until connect is
     * called.
     * 
     * @param address A string representing the address to connect to
     * @param port The port number within the valid range to connect to
     * @throws UnknownHostException If no IP address for the host could be found, or if a scope_id was specified for a
     *             global IPv6 address.
     * @throws InvalidPortException If the port parameter is outside the specified range of valid port values.
     */
    public IrcConnection( String address, int port )
        throws UnknownHostException, InvalidPortException
    {
        this( InetAddress.getByName( address ), port, SslMode.OFF );
    }

    /**
     * Create an IRC connection with SSL disabled. The connection is not active until connect is called.
     * 
     * @param address A InetAddress representing the address to connect to
     * @param port The port number within the valid range to connect to
     * @throws InvalidPortException If the port parameter is outside the specified range of valid port values.
     */
    public IrcConnection( InetAddress address, int port )
        throws InvalidPortException
    {
        this( address, port, SslMode.OFF );
    }

    /**
     * Create an IRC connection with a String address. The connection is not active until connect is called.
     * 
     * @param address A string representing the address to connect to
     * @param port The port number within the valid range to connect to
     * @throws UnknownHostException If no IP address for the host could be found, or if a scope_id was specified for a
     *             global IPv6 address.
     * @throws InvalidPortException If the port parameter is outside the specified range of valid port values.
     */
    public IrcConnection( String address, int port, SslMode ssl )
        throws UnknownHostException, InvalidPortException
    {
        this( InetAddress.getByName( address ), port, ssl );
    }

    /**
     * Create an IRC connection. The connection is not active until connect is called.
     * 
     * @param address A InetAddress representing the address to connect to
     * @param port The port number within the valid range to connect to
     * @throws InvalidPortException If the port parameter is outside the specified range of valid port values.
     */
    public IrcConnection( InetAddress address, int port, SslMode ssl )
        throws InvalidPortException
    {
        Logger.trace( "IrcConnection instantiation" );
        try
        {
            endpoint = new InetSocketAddress( address, port );
        }
        catch ( IllegalArgumentException e )
        {
            throw new InvalidPortException( e );
        }

        // Bus has to be made in this class's constructor because we want to be able to subscribe stuff to the bus
        // before connecting.
        BusConfiguration busConf = BusConfiguration.Default();
        // busConf.setSubscriptionFactory( new TimeoutSubscriptionFactory() );
        bus = new MBassadorWrapper( busConf );

        if ( SslMode.ON.equals( ssl ) )
            socketFactory = SSLSocketFactory.getDefault();
        else if ( SslMode.ON_NOHOST.equals( ssl ) )
            socketFactory = TrustAllTrustManager.getSSLSocketFactory();
        else
            socketFactory = SocketFactory.getDefault();
    }

    /**
     * Get the IrcEvent bus for the connection. May be called immediately.
     * 
     * @return the central MBassador bus
     */
    public MBassadorWrapper getEventBus()
    {
        return bus;
    }

    /**
     * Get the output Queue. May be called after calling {@link #connect()}.
     */
    public OutputQueue getOutputQueue()
    {
        if (outputQueue != null)
            return outputQueue;
        else
            throw new IllegalStateException("OutputQueue cannot be requested before connection is made");
    }

    /**
     * Activate the connection. Will return immediately after starting the connection thread.
     */
    public void connect()
    {
        Logger.info( "Connecting" );

        Logger.trace( "Subscribing to event bus" );
        bus.addErrorHandler( new BusErrorHandler() );
        bus.subscribe( new ServerPingHandler() );
        bus.subscribe( new DeadMessageHandler() );

        Logger.trace( "Creating/starting connection thread" );
        connectionThread = new ConnectionThread( bus, endpoint, socketFactory );
        outputQueue = connectionThread.getOutputQueue();
        connectionThread.start();

        Logger.trace( "Done set up" );
    }

    /**
     * Deactivate the connection.
     */
    public void disconnect()
    {
        Logger.info( "Disconnecting" );

        Logger.trace( "Stopping worker thread" );
        connectionThread.interrupt();
        try
        {
            connectionThread.join();
        }
        catch ( InterruptedException e )
        {
        }

        Logger.trace( "Shutting down event bus" );
        bus.shutdown();
        bus = null;

        Logger.trace( "Done shut down" );
    }
}
@@ -790,77 +790,8 @@
 er;%0A
-import ca.szc.keratin.core.net.mbassador.TimeoutSubscriptionFactory;%0A
 impo

Remove unused import in IrcConnection