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

import com.jscape.inet.ssh.protocol.messages.Message;
import com.jscape.inet.ssh.protocol.v2.marshaling.primitive.ByteArrayCodec;
import com.jscape.inet.ssh.protocol.v2.marshaling.primitive.NameListCodec;
import com.jscape.inet.ssh.protocol.v2.marshaling.primitive.Uint32Codec;
import com.jscape.inet.ssh.protocol.v2.messages.SshMsgKexInit;
import com.jscape.util.IOTools;
import com.jscape.util.io.ByteArrayInputStream;
import com.jscape.util.io.ByteArrayOutputStream;
import com.jscape.util.io.DataCodec;
import com.jscape.util.io.primitive.BooleanCodec;
import com.jscape.util.io.primitive.ByteCodec;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

/**
 * @author Alex Usun
 */
public class SshMsgKexInitCodec
        implements DataCodec<Message> {

    private static final byte MESSAGE_CODE = 20;

    @Override
    public Message read(InputStream in)
            throws IOException {
        byte[] data = IOTools.readFully(in);

        ByteArrayInputStream buffer = new ByteArrayInputStream(data);
        byte[] cookie = ByteArrayCodec.readValue(SshMsgKexInit.COOKIE_LENGTH, buffer);
        List<String> kexAlgorithms = NameListCodec.readUsAsciiValue(buffer);
        List<String> serverHostKeyAlgorithms = NameListCodec.readUsAsciiValue(buffer);
        List<String> encryptionAlgorithmsClientToServer = NameListCodec.readUsAsciiValue(buffer);
        List<String> encryptionAlgorithmsServerToClient = NameListCodec.readUsAsciiValue(buffer);
        List<String> macAlgorithmsClientToServer = NameListCodec.readUsAsciiValue(buffer);
        List<String> macAlgorithmsServerToClient = NameListCodec.readUsAsciiValue(buffer);
        List<String> compressionAlgorithmsClientToServer = NameListCodec.readUsAsciiValue(buffer);
        List<String> compressionAlgorithmsServerToClient = NameListCodec.readUsAsciiValue(buffer);
        List<String> languagesClientToServer = NameListCodec.readUsAsciiValue(buffer);
        List<String> languagesServerToClient = NameListCodec.readUsAsciiValue(buffer);
        boolean firstKexPacketFollows = BooleanCodec.readValue(buffer);

        SshMsgKexInit message = new SshMsgKexInit(cookie,
                                                  kexAlgorithms,
                                                  serverHostKeyAlgorithms,
                                                  encryptionAlgorithmsClientToServer,
                                                  encryptionAlgorithmsServerToClient,
                                                  macAlgorithmsClientToServer,
                                                  macAlgorithmsServerToClient,
                                                  compressionAlgorithmsClientToServer,
                                                  compressionAlgorithmsServerToClient,
                                                  languagesClientToServer,
                                                  languagesServerToClient,
                                                  firstKexPacketFollows);
        message.rawData = messageRawData(data);

        return message;
    }

    @Override
    public void write(Message value, OutputStream out)
            throws IOException {
        SshMsgKexInit message = (SshMsgKexInit) value;

        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        ByteArrayCodec.writeValue(message.cookie, buffer);
        NameListCodec.writeUsAsciiValue(message.kexAlgorithms, buffer);
        NameListCodec.writeUsAsciiValue(message.serverHostKeyAlgorithms, buffer);
        NameListCodec.writeUsAsciiValue(message.encryptionAlgorithmsClientToServer, buffer);
        NameListCodec.writeUsAsciiValue(message.encryptionAlgorithmsServerToClient, buffer);
        NameListCodec.writeUsAsciiValue(message.macAlgorithmsClientToServer, buffer);
        NameListCodec.writeUsAsciiValue(message.macAlgorithmsServerToClient, buffer);
        NameListCodec.writeUsAsciiValue(message.compressionAlgorithmsClientToServer, buffer);
        NameListCodec.writeUsAsciiValue(message.compressionAlgorithmsServerToClient, buffer);
        NameListCodec.writeUsAsciiValue(message.languagesClientToServer, buffer);
        NameListCodec.writeUsAsciiValue(message.languagesServerToClient, buffer);
        BooleanCodec.writeValue(message.firstKexPacketFollows, buffer);
        Uint32Codec.writeValue(0, buffer);

        message.rawData = messageRawData(buffer.toByteArray());

        buffer.writeTo(out);
    }

    private byte[] messageRawData(byte[] data)
            throws IOException {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        ByteCodec.writeValue(MESSAGE_CODE, buffer);
        buffer.write(data);

        return buffer.toByteArray();
    }
}
