package com.jscape.inet.ssh;

import com.jscape.inet.ssh.protocol.messages.IdentificationString;
import com.jscape.inet.ssh.protocol.v2.marshaling.algorithms.*;
import com.jscape.inet.ssh.protocol.v2.messages.PublicKeyAlgorithm;
import com.jscape.inet.ssh.protocol.v2.transport.keyexchange.ClientKeyExchangeFactory;
import com.jscape.inet.ssh.protocol.v2.transport.keyexchange.ClientKeyExchanges;
import com.jscape.inet.ssh.protocol.v2.transport.keyexchange.ComponentClientKeyExchangeFactory;

import java.util.Arrays;

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

/**
 * A SSH protocol configuration.
 *
 * @author Alex Usun
 */
public class SshConfiguration {

    public static final int DEFAULT_WINDOW_SIZE = 2 * 1024 * 1024;
    public static final int DEFAULT_PACKET_SIZE = 32768;

    public IdentificationString identificationString;
    public PublicKeyAlgorithm[] publicKeyAlgorithms;
    public ClientKeyExchangeFactory keyExchangeFactory;
    public EncryptionFactory encryptionFactory;
    public MacFactory macFactory;
    public CompressionFactory compressionFactory;
    public int initialWindowSize;
    public int maxPacketSize;

    /**
     * Returns a configuration instance with default parameters for server side.
     *
     * @param softwareVersion a a software version
     *
     * @return a configuration instance
     */
    public static SshConfiguration defaultServerConfiguration(String softwareVersion) {
        return new SshConfiguration(softwareVersion,
                                    null,
                                    PublicKeyAlgorithm.values(),
                                    ClientKeyExchanges.initAll(new ComponentClientKeyExchangeFactory()),
                                    JceEncryptions.initAll(new JceEncryptionFactory()),
                                    JceMacs.initAll(new JceMacFactory()),
                                    Compressions.initAll(new ComponentCompressionFactory()),
                                    DEFAULT_WINDOW_SIZE,
                                    DEFAULT_PACKET_SIZE);
    }

    /**
     * Returns a configuration instance with default parameters for server side.
     *
     * @return a configuration instance
     */
    public static SshConfiguration defaultServerConfiguration() {
        return defaultServerConfiguration("MFT");
    }

    /**
     * Returns a configuration instance with default parameters for client side.
     *
     * @param softwareVersion a a software version
     *
     * @return a configuration instance
     */
    public static SshConfiguration defaultClientConfiguration(String softwareVersion) {
        return new SshConfiguration(softwareVersion,
                                    null,
                                    PublicKeyAlgorithm.values(),
                                    ClientKeyExchanges.initAllClient(new ComponentClientKeyExchangeFactory()),
                                    JceEncryptions.initAllClient(new JceEncryptionFactory()),
                                    JceMacs.initAllClient(new JceMacFactory()),
                                    Compressions.initAllClient(new ComponentCompressionFactory()),
                                    DEFAULT_WINDOW_SIZE,
                                    DEFAULT_PACKET_SIZE);
    }

    /**
     * Returns a configuration instance with default parameters for client side.
     *
     * @return a configuration instance
     */
    public static SshConfiguration defaultClientConfiguration() {
        return defaultClientConfiguration("MFT");
    }

    /**
     * Returns a configuration instance for specified parameters.
     *
     * @param keyExchangeFactory a a key exchange factory
     * @param encryptionFactory  an encryption factory
     * @param macFactory         a MAC factory
     * @param compressionFactory a compression factory
     *
     * @return a configuration instance
     */
    public static SshConfiguration configurationFor(ClientKeyExchangeFactory keyExchangeFactory,
                                                    EncryptionFactory encryptionFactory,
                                                    MacFactory macFactory,
                                                    CompressionFactory compressionFactory) {
        return new SshConfiguration("MFT-2.0",
                                    null,
                                    PublicKeyAlgorithm.values(),
                                    keyExchangeFactory,
                                    encryptionFactory,
                                    macFactory,
                                    compressionFactory,
                                    DEFAULT_WINDOW_SIZE,
                                    DEFAULT_PACKET_SIZE);
    }

    /**
     * Creates a new configuration instance.
     *
     * @param softwareVersion              a software version
     * @param identificationStringComments an identification string coments
     * @param keyExchangeFactory           a key exchange factory
     * @param encryptionFactory            an encryption factory
     * @param macFactory                   a MAC factory
     * @param compressionFactory           a compression factory
     * @param initialWindowSize            an initial window size in bytes
     * @param maxPacketSize                a max packet size in bytes
     */
    public SshConfiguration(String softwareVersion,
                            String identificationStringComments,
                            PublicKeyAlgorithm[] publicKeyAlgorithms,
                            ClientKeyExchangeFactory keyExchangeFactory,
                            EncryptionFactory encryptionFactory,
                            MacFactory macFactory,
                            CompressionFactory compressionFactory,
                            int initialWindowSize,
                            int maxPacketSize) {
        this.identificationString = new IdentificationString(null, "2.0", softwareVersion, identificationStringComments);

        assertNotNull(publicKeyAlgorithms);
        this.publicKeyAlgorithms = publicKeyAlgorithms;

        assertNotNull(keyExchangeFactory);
        this.keyExchangeFactory = keyExchangeFactory;

        assertNotNull(encryptionFactory);
        this.encryptionFactory = encryptionFactory;

        assertNotNull(macFactory);
        this.macFactory = macFactory;

        assertNotNull(compressionFactory);
        this.compressionFactory = compressionFactory;

        assertNotNull(initialWindowSize);
        this.initialWindowSize = initialWindowSize;

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

    @Override
    public String toString() {
        return "SshConfiguration {"
               + "identificationString=" + this.identificationString
               + ", publicKeyAlgorithms=" + Arrays.toString(this.publicKeyAlgorithms)
               + ", keyExchangeFactory=" + this.keyExchangeFactory
               + ", encryptionFactory=" + this.encryptionFactory
               + ", macFactory=" + this.macFactory
               + ", compressionFactory=" + this.compressionFactory
               + ", initialWindowSize=" + this.initialWindowSize
               + ", maxPacketSize=" + this.maxPacketSize
               + '}';
    }
}
