package com.jscape.inet.ssh.protocol.v2.marshaling.algorithms;

import com.jscape.inet.ssh.protocol.v2.messages.SshMsgKexInit;
import com.jscape.util.SuppressObfuscation;

import java.util.List;

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

/**
 * @author Alex Usun
 */
public class Algorithms {

    public final String keyExchange;
    public final String serverHostKey;
    public final String encryptionClientToServer;
    public final String encryptionServerToClient;
    public final String macClientToServer;
    public final String macServerToClient;
    public final String compressionClientToServer;
    public final String compressionServerToClient;
    public final String languageClientToServer;
    public final String languageServerToClient;

    public static Algorithms algorithmsFor(SshMsgKexInit clientMessage, SshMsgKexInit serverMessage)
            throws CommonAlgorithmsNotFoundException {
        String keyExchange = select(clientMessage.kexAlgorithms, serverMessage.kexAlgorithms, true);
        String serverHostKey = select(clientMessage.serverHostKeyAlgorithms, serverMessage.serverHostKeyAlgorithms, true);
        String encryptionClientToServer = select(clientMessage.encryptionAlgorithmsClientToServer, serverMessage.encryptionAlgorithmsClientToServer, true);
        String encryptionServerToClient = select(clientMessage.encryptionAlgorithmsServerToClient, serverMessage.encryptionAlgorithmsServerToClient, true);
        String macClientToServer = select(clientMessage.macAlgorithmsClientToServer, serverMessage.macAlgorithmsClientToServer, true);
        String macServerToClient = select(clientMessage.macAlgorithmsServerToClient, serverMessage.macAlgorithmsServerToClient, true);
        String compressionClientToServer = select(clientMessage.compressionAlgorithmsClientToServer, serverMessage.compressionAlgorithmsClientToServer, true);
        String compressionServerToClient = select(clientMessage.compressionAlgorithmsServerToClient, serverMessage.compressionAlgorithmsServerToClient, true);
        String languageClientToServer = select(clientMessage.languagesClientToServer, serverMessage.languagesClientToServer, false);
        String languageServerToClient = select(clientMessage.languagesServerToClient, serverMessage.languagesServerToClient, false);

        return new Algorithms(keyExchange,
                              serverHostKey,
                              encryptionClientToServer,
                              encryptionServerToClient,
                              macClientToServer,
                              macServerToClient,
                              compressionClientToServer,
                              compressionServerToClient,
                              languageClientToServer,
                              languageServerToClient);
    }

    private static String select(List<String> clientList, List<String> serverList, boolean strict)
            throws CommonAlgorithmsNotFoundException {
        for (String name : clientList) {
            if (serverList.contains(name)) {
                return name;
            }
        }

        if (strict) {
            throw new CommonAlgorithmsNotFoundException();
        }

        return "";
    }

    public Algorithms(String keyExchange,
                      String serverHostKey,
                      String encryptionClientToServer,
                      String encryptionServerToClient,
                      String macClientToServer,
                      String macServerToClient,
                      String compressionClientToServer,
                      String compressionServerToClient,
                      String languageClientToServer,
                      String languageServerToClient) {
        assertNotNull(keyExchange);
        this.keyExchange = keyExchange;

        assertNotNull(serverHostKey);
        this.serverHostKey = serverHostKey;

        assertNotNull(encryptionClientToServer);
        this.encryptionClientToServer = encryptionClientToServer;

        assertNotNull(encryptionServerToClient);
        this.encryptionServerToClient = encryptionServerToClient;

        assertNotNull(macClientToServer);
        this.macClientToServer = macClientToServer;

        assertNotNull(macServerToClient);
        this.macServerToClient = macServerToClient;

        assertNotNull(compressionClientToServer);
        this.compressionClientToServer = compressionClientToServer;

        assertNotNull(compressionServerToClient);
        this.compressionServerToClient = compressionServerToClient;

        assertNotNull(languageClientToServer);
        this.languageClientToServer = languageClientToServer;

        assertNotNull(languageServerToClient);
        this.languageServerToClient = languageServerToClient;
    }

    @Override
    public String toString() {
        return "Algorithms {"
                + "keyExchange='" + this.keyExchange + '\''
                + ", serverHostKey='" + this.serverHostKey + '\''
                + ", encryptionClientToServer='" + this.encryptionClientToServer + '\''
                + ", encryptionServerToClient='" + this.encryptionServerToClient + '\''
                + ", macClientToServer='" + this.macClientToServer + '\''
                + ", macServerToClient='" + this.macServerToClient + '\''
                + ", compressionClientToServer='" + this.compressionClientToServer + '\''
                + ", compressionServerToClient='" + this.compressionServerToClient + '\''
                + ", languageClientToServer='" + this.languageClientToServer + '\''
                + ", languageServerToClient='" + this.languageServerToClient + '\''
                + '}';
    }

    @SuppressObfuscation
    public static class CommonAlgorithmsNotFoundException
            extends Exception {

        public CommonAlgorithmsNotFoundException() {
            super("Common algorithms not found.");
        }
    }
}
