package com.jscape.inet.ssh.protocol.v2.authentication;

import com.jscape.inet.ssh.protocol.messages.Message;
import com.jscape.inet.ssh.protocol.v2.marshaling.MessageCodec;
import com.jscape.inet.ssh.protocol.v2.messages.SshMsgUserAuthBanner;
import com.jscape.inet.ssh.protocol.v2.messages.SshMsgUserAuthFailure;
import com.jscape.inet.ssh.protocol.v2.messages.SshMsgUserAuthSuccess;
import com.jscape.inet.ssh.protocol.v2.transport.TransportConnection;
import com.jscape.util.net.connection.MessageConnection;

import static com.jscape.util.Parameters.assertNotNull;

/**
 * SSH client authentication base. Provides support for client authentication operations.
 *
 * @author Alex Usun
 */
public abstract class AbstractClientAuthentication
        implements ClientAuthentication,
                   SshMsgUserAuthBanner.Handler {

    protected final MessageCodec.Factory codecFactory;
    protected String banner;
    protected String bannerLanguageTag;

    public AbstractClientAuthentication(MessageCodec.Factory codecFactory) {
        assertNotNull(codecFactory);
        this.codecFactory = codecFactory;
    }

    @Override
    public void handle(SshMsgUserAuthBanner message, MessageConnection<Message> connection) {
        this.banner = message.message;
        this.bannerLanguageTag = message.languageTag;
    }

    /**
     * Returns server authentication banner if any.
     *
     * @return server authentication banner
     */
    public String banner() {
        return this.banner;
    }

    /**
     * Returns server authentication banner language tag.
     *
     * @return server authentication banner language tag
     */
    public String bannerLanguageTag() {
        return this.bannerLanguageTag;
    }

    @Override
    public String toString() {
        return "AbstractClientAuthentication {"
               + "banner='" + this.banner + '\''
               + ", bannerLanguageTag='" + this.bannerLanguageTag + '\''
               + '}';
    }

    protected void init(TransportConnection connection) {
        this.banner = null;
        this.bannerLanguageTag = null;
    }

    @SuppressWarnings("unchecked")
    protected Message receive(TransportConnection connection)
            throws Exception {
        Message message;

        do {
            message = connection.read();

            if (intermediateMessage(message)) {
                message.accept(this, connection);
                message = null;
            }
        } while (message == null);

        return message;
    }

    @SuppressWarnings("unchecked")
    protected boolean intermediateMessage(Message message) {
        return message.handlerClass().isAssignableFrom(getClass());
    }

    protected void assertSuccess(Message response)
            throws PartialSuccessException, InvalidCredentialsException {
        if (response instanceof SshMsgUserAuthSuccess) {
            return;
        }

        if (response instanceof SshMsgUserAuthFailure) {
            SshMsgUserAuthFailure failureResponse = (SshMsgUserAuthFailure) response;

            if (failureResponse.partialSuccess) {
                throw new PartialSuccessException();
            }
        }

        throw new InvalidCredentialsException();
    }
}
