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

import com.jscape.inet.ssh.protocol.v2.marshaling.primitive.StringCodec;
import com.jscape.inet.ssh.protocol.v2.messages.PublicKeyAlgorithm;
import com.jscape.util.io.DataCodec;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;

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

/**
 * @author Alex Usun
 */
public class PublicKeyCodec
        implements DataCodec<PublicKey> {

    private final Map<String, Entry> entries;

    public PublicKeyCodec(Entry... entries) {
        this.entries = new HashMap<>();
        set(entries);
    }

    public PublicKeyCodec set(Entry... entries) {
        for (Entry entry : entries) {
            this.entries.put(entry.identifier, entry);
        }

        return this;
    }

    @Override
    public PublicKey read(InputStream in)
            throws IOException {
        String identifier = StringCodec.readUsAsciiValue(in);
        Entry entry = entryFor(identifier);

        return entry.codec.read(in);
    }

    @Override
    public void write(PublicKey value, OutputStream out)
            throws IOException {
        Entry entry = entryFor(value);
        StringCodec.writeUsAsciiValue(entry.identifier, out);
        entry.codec.write(value, out);
    }

    @Override
    public String toString() {
        return "PublicKeyCodec {"
                + "entries=" + this.entries
                + '}';
    }

    private Entry entryFor(String identifier)
            throws IOException {
        Entry entry = this.entries.get(identifier);

        if (entry == null) {
            throw new IOException(String.format("Unsupported signature format identifier: %s.", identifier));
        }

        return entry;
    }

    private Entry entryFor(PublicKey value)
            throws IOException {
        String identifier = PublicKeyAlgorithm.formatIdentifierFor(value);
        return entryFor(identifier);
    }

    public static class Entry {

        public final String identifier;
        public final DataCodec<PublicKey> codec;

        public Entry(String identifier, DataCodec<PublicKey> codec) {
            assertNotNull(identifier);
            this.identifier = identifier;

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

        @Override
        public String toString() {
            return "Entry {"
                    + "identifier='" + this.identifier + '\''
                    + ", codec=" + this.codec
                    + '}';
        }
    }
}
