package daruma.server;

import java.net.Socket;
import java.io.IOException;
import java.util.Date;
import java.util.Set;
import java.util.TreeSet;
import java.util.Map;
import java.util.TreeMap;

import daruma.util.LogWriter;


public class ConnectionInfoHolder
{
    private Object lock = new Object();

    public enum ConnectionStatus{ Waiting, Running };

    public class ConnectionInfo implements Comparable<ConnectionInfo>
    {
	private long connectionID;
	private Socket socket;
	private Date connectedDate;
	private ConnectionStatus status;

	public ConnectionInfo( Socket socket, long connectionID,
			       Date connectedDate )
	{
	    this.connectionID = connectionID;
	    this.socket = socket;
	    this.connectedDate = connectedDate;
	    this.status = ConnectionStatus.Waiting;
	}

	public long getConnectionID()
	{
	    return this.connectionID;
	}

	public Socket getSocket()
	{
	    return this.socket;
	}

	public Date getConnectedDate()
	{
	    return this.connectedDate;
	}

	public ConnectionStatus getStatus()
	{
	    return this.status;
	}

	private void updateStatus( ConnectionStatus status )
	{
	    this.status = status;
	}

	@Override
	public int compareTo( ConnectionInfo c )
	{
	    return (int)(this.connectionID - c.connectionID);
	}
    }

    public class DisconnectResult
    {
	private boolean success;
	private boolean done;

	public DisconnectResult( boolean success, boolean done )
	{
	    this.success = success;
	    this.done = done;
	}

	public boolean getSuccess()
	{
	    return this.success;
	}

	public boolean getDone()
	{
	    return this.done;
	}
    }


    private long currentClientConnectionID = -1;
    private Map<Long, ConnectionInfo>
		clientConnectionMap = new TreeMap<Long, ConnectionInfo>();

    public ConnectionInfoHolder()
    {
    }

    public void addClientSocket( Socket s, long connectionID,
				 Date date )
    {
	this.clientConnectionMap
	       .put( connectionID,
		     new ConnectionInfo( s, connectionID, date ) );
    }

    private ConnectionInfo getClientInfo( long connectionID )
    {
	return this.clientConnectionMap.get( connectionID );
    }


    public Set<ConnectionInfo> getClientSocketSet()
    {
	synchronized(lock)
	{
	    return new TreeSet<ConnectionInfo>
		       ( this.clientConnectionMap.values() );
	}
    }

    public void setCurrentClientSocket( long connectionID )
    {
	synchronized(lock)
	{
	    ConnectionInfo c = this.getClientInfo( connectionID );

	    this.currentClientConnectionID = connectionID;

	    c.updateStatus( ConnectionStatus.Running );
	}
    }

    public Socket getCurrentClientSocket()
    {
	synchronized(lock)
	{
	    return getClientInfo( this.currentClientConnectionID ).getSocket();
	}
    }

    public DisconnectResult forceDisconnectClient() throws IOException
    {
	synchronized(lock)
	{
	    if ( this.currentClientConnectionID != -1 )
	    {
		LogWriter.qwrite( "INFO",
				  "forced closing client Socket: ",
				  this.getClientInfo
				  ( this.currentClientConnectionID )
				  .getSocket() );

		return this.closeSocket( this.currentClientConnectionID );
	    }
	    else
	    {
		return new DisconnectResult( true, false );
	    }
	}
    }

    public DisconnectResult closeSocket( long connectionID ) throws IOException
    {
	synchronized(lock)
	{
	    ConnectionInfo c = this.getClientInfo( connectionID );

	    if ( c != null )
	    {
		Socket s = c.getSocket();

		try
		{
		    if ( connectionID == this.currentClientConnectionID )
		    {
			this.currentClientConnectionID = -1;
		    }

		    s.close();

		    return new DisconnectResult( true, true );
		}
		catch( IOException e )
		{
		    throw e;
		}
		finally
		{
		    this.clientConnectionMap.remove( connectionID );
		}
	    }
	    else
	    {
		return new DisconnectResult( false, false );
	    }
	}
    }
}
