package be.iminds.ilabt.jfed.lowlevel;

import be.iminds.ilabt.jfed.util.DataConversionUtils;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.List;

/**
 * Gid a Geni GID is an X.509 certificate
 */
public class Gid {
    private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(Gid.class);

    private String encodedContent;
    private byte[] decodedContent;
    private X509Certificate certificate;

    private String subjectUuid;
    private String subjectUrn;
    private String subjectEmail;

    /** copy constructor */
    public Gid(Gid o) {
        this(o.getEncodedContent());
    }

    private static final String CERT_HEAD = "-----BEGIN CERTIFICATE-----";
    private static final String CERT_TAIL = "-----END CERTIFICATE-----";
    public Gid(String content) {
        if (content.startsWith(CERT_HEAD)) {
            int endIndex = content.indexOf(CERT_TAIL);
            if (endIndex < 0) {
                LOG.warn("Gid got a certificate with a begin but without end: "+content);
                return;
            } else {
                assert endIndex > CERT_HEAD.length();
                content = content.substring(CERT_HEAD.length(), endIndex);
            }
        }
        this.encodedContent = content;
        this.decodedContent = DataConversionUtils.decodeBase64(content);
        InputStream inStream = new ByteArrayInputStream(decodedContent);
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            this.certificate = (X509Certificate)cf.generateCertificate(inStream);
            inStream.close();
        } catch (Exception e) {
            LOG.warn("Error reading X509Certificate: " + e.getMessage(), e);
//            e.printStackTrace();
            this.certificate = null;
        }

        if (certificate != null)
            try {
                if (certificate.getSubjectAlternativeNames() != null)
                    for (List<?> l : certificate.getSubjectAlternativeNames()) {
                        if (l.size() == 2 && l.get(0) instanceof Integer) {
                            Integer nameType = (Integer) l.get(0);
                            Object name = l.get(1);
//                            LOG.debug("Parsing "+nameType+": "+name);
                            if (nameType == 6 && name instanceof String) {
                                //6 means it's a URI
                                String uri = (String) name;
                                if (uri.startsWith("urn:uuid:"))
                                    subjectUuid = uri.substring("urn:uuid:".length());
                                if (uri.startsWith("urn:publicid:IDN"))
                                    subjectUrn = uri;
                                if (uri.startsWith("email:"))
                                    subjectEmail = uri.substring("email:".length());
                            }
                        } else {
                            LOG.warn("Weird subject alt name: "+l);
                        }
                    }
            } catch (Exception e) {
                LOG.warn("Error processing X509Certificate", e);
            }
    }

    public String getEncodedContent() {
        return encodedContent;
    }

    public String getFullPemCertificate() {
        return "-----BEGIN CERTIFICATE-----\n"+encodedContent+"\n-----END CERTIFICATE-----\n";
    }

    public byte[] getDecodedContent() {
        return decodedContent;
    }

    public X509Certificate getCertificate() {
        return certificate;
    }

    public boolean hasError() {
        return certificate == null;
    }

    public String getSubjectUuid() {
        return subjectUuid;
    }

    public String getSubjectUrn() {
        return subjectUrn;
    }

    public String getSubjectEmail() {
        return subjectEmail;
    }

    public String toString() {
        if (certificate != null)
            return "X.509 certificate for "+certificate.getSubjectDN().toString()+"";
        else
            return "Certificate with read error: "+encodedContent.substring(0, 20)+"...";
    }
}
