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

import com.jscape.util.Pair;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
import org.bouncycastle.jce.spec.ECParameterSpec;

import java.security.Key;
import java.util.Enumeration;
import java.util.Map;

import static com.jscape.util.CollectionTools.map;
import static com.jscape.util.Pair.pair;

/**
 * @author Alex Uzun
 */
public class NamedEllipticCurveDomainParameters {

    private static final Map<String, String> REQUIRED_CURVES = map(pair("1.2.840.10045.3.1.7", "nistp256"),
                                                                   pair("1.3.132.0.34", "nistp384"),
                                                                   pair("1.3.132.0.35", "nistp521"));

    public static String oidFor(String identifier) {
        for (Map.Entry<String, String> entry : REQUIRED_CURVES.entrySet()) {
            if (entry.getValue().equals(identifier)) {
                return entry.getKey();
            }
        }

        return identifier;
    }

    public static String identifierFor(Key key) {
        if (key instanceof java.security.interfaces.ECPublicKey) {
            String identifier = identifierFor(EC5Util.convertSpec(((java.security.interfaces.ECPublicKey) key).getParams()));
            return REQUIRED_CURVES.getOrDefault(identifier, identifier);
        } else if (key instanceof org.bouncycastle.jce.interfaces.ECPublicKey) {
            String identifier = identifierFor(((org.bouncycastle.jce.interfaces.ECPublicKey) key).getParameters());
            return REQUIRED_CURVES.getOrDefault(identifier, identifier);
        } else if (key instanceof java.security.interfaces.ECPrivateKey) {
            String identifier = identifierFor(EC5Util.convertSpec(((java.security.interfaces.ECPrivateKey) key).getParams()));
            return REQUIRED_CURVES.getOrDefault(identifier, identifier);
        } else if (key instanceof org.bouncycastle.jce.interfaces.ECPrivateKey) {
            String identifier = identifierFor(((org.bouncycastle.jce.interfaces.ECPrivateKey) key).getParameters());
            return REQUIRED_CURVES.getOrDefault(identifier, identifier);
        }

        throw new IllegalArgumentException(String.format("Unsupported key type: %s.", key.getClass()));
    }

    public static X9ECParameters parametersFor(String identifier) {
        for (Map.Entry<String, String> entry : REQUIRED_CURVES.entrySet()) {
            if (entry.getValue().equals(identifier)) {
                return ECNamedCurveTable.getByOID(new ASN1ObjectIdentifier(entry.getKey()));
            }
        }

        X9ECParameters parameters = ECNamedCurveTable.getByOID(new ASN1ObjectIdentifier(identifier));

        if (parameters == null) {
            parameters = ECNamedCurveTable.getByName(identifier);
        }

        return parameters;
    }

    private static String identifierFor(ECParameterSpec spec) {
        for (Enumeration names = ECNamedCurveTable.getNames(); names.hasMoreElements();) {
            String name = (String) names.nextElement();
            X9ECParameters params = ECNamedCurveTable.getByName(name);

            if (params.getN().equals(spec.getN()) && params.getH().equals(spec.getH()) && params.getCurve().equals(spec.getCurve()) && params.getG().equals(spec.getG())) {
                return ECNamedCurveTable.getOID(name).toString();
            }
        }

        throw new IllegalArgumentException(String.format("Unsupported curve type: %s.", spec));
    }

    private NamedEllipticCurveDomainParameters() {
        // empty
    }
}
