package org.limeprotocol.client;

import org.limeprotocol.*;
import org.limeprotocol.network.Channel;
import org.limeprotocol.network.ChannelBase;
import org.limeprotocol.network.SessionChannel;
import org.limeprotocol.network.Transport;
import org.limeprotocol.security.Authentication;

import java.io.IOException;
import java.util.UUID;

import static org.limeprotocol.Session.SessionState.*;

public class ClientChannelImpl extends ChannelBase implements ClientChannel {
    public ClientChannelImpl(Transport transport, boolean fillEnvelopeRecipients) {
        super(transport, fillEnvelopeRecipients);
    }

    /**
     * Sends a new session envelope to the server and listen for the response.
     *
     * @param sessionListener
     */
    @Override
    public void startNewSession(SessionChannelListener sessionListener) throws IOException {
        if (getState() != NEW) {
            throw new IllegalStateException(String.format("Cannot start a session in the '%s' state.", getState()));
        }
        addSessionListener(sessionListener);
        Session session = new Session();
        session.setState(NEW);
        sendSession(session);
    }

    /**
     * Sends a negotiate session envelope to accepts the session negotiation options
     * and listen for the server confirmation.
     *
     * @param sessionCompression
     * @param sessionEncryption
     * @param sessionListener
     */
    @Override
    public void negotiateSession(SessionCompression sessionCompression, SessionEncryption sessionEncryption, SessionChannelListener sessionListener) throws IOException {
        if (getState() != NEGOTIATING) {
            throw new IllegalStateException(String.format("Cannot negotiate a session in the '%s' state.", getState()));
        }
        addSessionListener(sessionListener);
        Session session = new Session();
        session.setId(super.getSessionId());
        session.setState(NEGOTIATING);
        session.setCompression(sessionCompression);
        session.setEncryption(sessionEncryption);
        sendSession(session);
    }

    /**
     * Sends a authenticate session envelope to the server to establish an authenticated session
     * and listen for the established session envelope.
     *
     * @param identity
     * @param authentication
     * @param instance
     * @param sessionListener
     */
    @Override
    public void authenticateSession(Identity identity, Authentication authentication, String instance, SessionChannelListener sessionListener) throws IOException {
        if (getState() != AUTHENTICATING) {
            throw new IllegalStateException(String.format("Cannot authenticate a session in the '%s' state", getState()));
        }
        if (identity == null) {
            throw new IllegalArgumentException("identity");
        }
        if (authentication == null) {
            throw new IllegalArgumentException("authentication");
        }
        addSessionListener(sessionListener);
        Session session = new Session();
        session.setId(getSessionId());
        session.setFrom(new Node(identity.getName(), identity.getDomain(), instance));
        session.setState(AUTHENTICATING);
        session.setAuthentication(authentication);
        sendSession(session);
    }

    /**
     * Notify to the server that the specified message was received by the peer.
     *
     * @param messageId
     * @param to
     */
    @Override
    public void sendReceivedNotification(final UUID messageId, final Node to) throws IOException {
        if (to == null) {
            throw new IllegalArgumentException("to");
        }

        Notification notification = new Notification() {{
            setId(messageId);
            setTo(to);
            setEvent(Event.RECEIVED);
        }};

        sendNotification(notification);
    }

    /**
     * Sends a finishing session envelope to the server.
     */
    @Override
    public void sendFinishingSession() throws IOException {
        if (getState() != ESTABLISHED) {
            throw new IllegalStateException(String.format("Cannot finish a session in the '%s' state", getState()));
        }

        Session session = new Session() {{
            setId(getSessionId());
            setState(FINISHING);
        }};

        sendSession(session);
    }

    @Override
    protected synchronized void raiseOnReceiveSession(Session session) {
        super.raiseOnReceiveSession(session);
        setSessionId(session.getId());
        setState(session.getState());
        
        if (session.getState() == ESTABLISHED) {
            setLocalNode(session.getTo());
            setRemoteNode(session.getFrom());
        } else if (session.getState() == FINISHED ||
                session.getState() == FAILED) {
            try {
                getTransport().close();
            } catch (Exception e) {
                raiseOnTransportException(e);
            }
        }
    }
}

@@ -89,24 +89,28 @@
 work.Channel
+Base
 ;%0Aimport org
@@ -127,35 +127,33 @@
 col.network.
-ChannelBase
+Transport
 ;%0Aimport org
@@ -170,73 +170,31 @@
 col.
-network.SessionChannel;%0Aimport org.limeprotocol.network.Transport
+security.Authentication
 ;%0Aim
@@ -215,39 +215,32 @@
 rotocol.
-security.Authentication
+util.StringUtils
 ;%0A%0Aimpor
@@ -4254,24 +4254,834 @@
 on);%0A    %7D%0A%0A
+    /**%0A     *  Fills the envelope recipients%0A     *  using the session information%0A     */%0A    @Override%0A    protected void fillEnvelope(Envelope envelope, boolean isSending)%0A    %7B%0A        super.fillEnvelope(envelope, isSending);%0A%0A        if (isSending &&%0A                this.getLocalNode() != null)%0A        %7B%0A            if (envelope.getPp() == null)%0A            %7B%0A                if (envelope.getFrom() != null &&%0A                        !envelope.getFrom().equals(this.getLocalNode()))%0A                %7B%0A                    envelope.setPp(this.getLocalNode().copy());%0A                %7D%0A            %7D%0A            else if (StringUtils.isNullOrWhiteSpace(envelope.getPp().getDomain()))%0A            %7B%0A                envelope.getPp().setDomain(this.getLocalNode().getDomain());%0A            %7D%0A        %7D%0A    %7D%0A%0A
     @Overrid

Create fillEnvelope override on ClientChannel