package cz.cuni.amis.pogamut.base.communication.connection;

import com.google.inject.ImplementedBy;

import cz.cuni.amis.pogamut.base.communication.connection.socket.SocketConnection;
import cz.cuni.amis.pogamut.base.communication.exceptions.CommunicationException;
import cz.cuni.amis.pogamut.base.factory.guice.AgentScoped;
import cz.cuni.amis.utils.flag.ImmutableFlag;

/**
 * Interface for the connection to a remote world.
 * <p><p>
 * Simple methods that are needed to make connection read/write messages.
 * <p><p>
 * Implementor is required to implement the interface as reusable. Meaning that one
 * instance of object may serve first as the connection to one server and then to another.
 * <p><p>
 * There is a nice abstract implementation AbstractConnection where all you have to do 
 * is implement inner methods for connect / close / getting raw readers and writers.
 * 
 * @author Jimmy
 */
@ImplementedBy(SocketConnection.class)
@AgentScoped
public interface IWorldConnection<ConnectionAddress extends IWorldConnectionAddress> extends IWorldReaderProvider, IWorldWriterProvider {
	
	/**
	 * Default termination of the messages (info messages and commands).
	 */
	public static final String DEFAULT_LINE_END = "\r\n";

	/**
	 * Should connect to the server described in 'address'.
	 * <p><p>
	 * This method has precisely defined behavior, cases follows:
	 * <ol>
	 * <li>called when connection is down - should attempt to made a new connection</li>
	 * <li>called when connection is up AND 'address' describes address of <b>different</b> server then the current one - should close the connection and connect to the new address</li>
	 * <li>called when connection is up AND 'address' describes address of <b>the same</b> server - does nothing</li>
	 * <li>called with parameter null - should raise the exception that the address is null</li>
	 * </ol>  
	 * <p><p>
	 * This method MUST rise exception IFF connect fails, otherwise we're considering that
	 * we are connected to 'address'.
	 */
	public void connect(ConnectionAddress address) throws CommunicationException;
		
	/**
	 * Should report whether the connection is connected to the remote side.
	 * <p><p>
	 * Note that the implementor should take care of this flag and correctly set it.
	 * 
	 * @return whether we're connected
	 */
	public ImmutableFlag<Boolean> getConnected();
	
	/**
	 * If connected, close the connection unconditionally. No need to raise any exception,
	 * drop everything if exception occurs within this method.
	 * <p><p>
	 * Must close Writer as well as Reader provided by getReader(), getWriter() methods.
	 */
	public void close();
	
	/**
	 * Sets whether to log the messages that are sent/received through writer/reader. Note
	 * that logging those messages is costly operation and may slow things a lot. That's
	 * why this is <b>OFF as DEFAULT</b>. 
	 * 
	 * @param logMessages
	 */
	public void setLogMessages(boolean logMessages);
	
	/**
	 * Get the descriptor of the connection end.
	 * @return
	 */
	public ConnectionAddress getAddress();
		
}