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

import com.jscape.inet.ssh.protocol.v2.marshaling.primitive.MpintCodec;
import com.jscape.util.io.DataCodec;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.Provider;
import java.security.PublicKey;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.DSAPublicKeySpec;

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

    private final Object provider;

    public static DsaPublicKeyCodec codecFor(String provider) {
        return new DsaPublicKeyCodec(provider);
    }

    public static DsaPublicKeyCodec codecFor(Provider provider) {
        return new DsaPublicKeyCodec(provider);
    }

    private DsaPublicKeyCodec(Object provider) {
        this.provider = provider;
    }

    @Override
    public PublicKey read(InputStream in)
            throws IOException {
        BigInteger p = MpintCodec.readValue(in);
        BigInteger q = MpintCodec.readValue(in);
        BigInteger g = MpintCodec.readValue(in);
        BigInteger y = MpintCodec.readValue(in);

        try {
            DSAPublicKeySpec keySpec = new DSAPublicKeySpec(y, p, q, g);
            KeyFactory keyFactory = this.provider instanceof String ? KeyFactory.getInstance("DSA", (String) this.provider) : this.provider instanceof Provider ? KeyFactory.getInstance("DSA", (Provider) this.provider) : KeyFactory.getInstance("DSA");

            return keyFactory.generatePublic(keySpec);
        } catch (Exception ex) {
            throw new IOException(ex);
        }
    }

    @Override
    public void write(PublicKey value, OutputStream out)
            throws IOException {
        DSAPublicKey publicKey = (DSAPublicKey) value;
        DSAParams spec = publicKey.getParams();
        MpintCodec.writeValue(spec.getP(), out);
        MpintCodec.writeValue(spec.getQ(), out);
        MpintCodec.writeValue(spec.getG(), out);
        MpintCodec.writeValue(publicKey.getY(), out);
    }
}
