package be.iminds.ilabt.jfed.ui.javafx.x509_certificate_gui;

import be.iminds.ilabt.jfed.ui.javafx.FXMLUtil;
import be.iminds.ilabt.jfed.util.KeyUtil;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Pair;

import java.net.URL;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.Collection;
import java.util.List;
import java.util.ResourceBundle;

/**
 * X509CertificatePanel
 */
public class X509CertificatePanel extends BorderPane implements Initializable {
    private static org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(X509CertificatePanel.class);

    private String certificatePEMString;
    private X509Certificate certificate;
    private boolean showPemCertificate;

    @FXML private Label pemLabel;
    @FXML private TextArea pemText;
    @FXML private TextArea subjectText;
    @FXML private Label subjectAltLabel;
    @FXML private TextArea subjectAltText;
    @FXML private TextArea issuerText;
    @FXML private Label issuerAltLabel;
    @FXML private TextArea issuerAltText;
    @FXML private TextField validFromText;
    @FXML private TextField validToText;
    @FXML private TextArea pubKeyOpenSSHText;
//    @FXML private TextArea pubKeyPEMText;

    public X509CertificatePanel() {
        this.showPemCertificate = true;

        FXMLUtil.injectFXML(this);
    }

    public void setCertificatePEMString(String certificatePEMString) {
        this.certificatePEMString = certificatePEMString;
        showCertificate();
    }

    private static String altIntToString(int altNr) {
        /*
            GeneralName ::= CHOICE {
                  otherName                       [0]     OtherName,
                  rfc822Name                      [1]     IA5String,
                  dNSName                         [2]     IA5String,
                  x400Address                     [3]     ORAddress,
                  directoryName                   [4]     Name,
                  ediPartyName                    [5]     EDIPartyName,
                  uniformResourceIdentifier       [6]     IA5String,
                  iPAddress                       [7]     OCTET STRING,
                  registeredID                    [8]     OBJECT IDENTIFIER
            }
      */
        switch (altNr) {
            case 0: return "otherName";
            case 1: return "rfc822Name";
            case 2: return "dNSName";
            case 3: return "x400Address";
            case 4: return "directoryName";
            case 5: return "ediPartyName";
            case 6: return "uniformResourceIdentifier";
            case 7: return "iPAddress";
            case 8: return "registeredID";
            default: return "invalid alt name type ("+altNr+")";
        }
    }

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        pemLabel.managedProperty().bind(pemLabel.visibleProperty());
        pemText.managedProperty().bind(pemText.visibleProperty());
        subjectAltLabel.managedProperty().bind(subjectAltLabel.visibleProperty());
        issuerAltLabel.managedProperty().bind(issuerAltLabel.visibleProperty());
        subjectAltText.managedProperty().bind(subjectAltText.visibleProperty());
        issuerAltText.managedProperty().bind(issuerAltText.visibleProperty());

        pubKeyOpenSSHText.managedProperty().bind(pubKeyOpenSSHText.visibleProperty());
//        pubKeyPEMText.managedProperty().bind(pubKeyPEMText.visibleProperty());

        pemLabel.visibleProperty().bind(pemText.visibleProperty());
        subjectAltLabel.visibleProperty().bind(subjectAltText.visibleProperty());
        issuerAltLabel.visibleProperty().bind(issuerAltText.visibleProperty());
    }

    public void setPemVisible(boolean showPemCertificate) {
        this.showPemCertificate = showPemCertificate;
        pemLabel.setVisible(this.showPemCertificate);
    }

    public void showCertificate() {
        certificate = KeyUtil.pemToX509Certificate(certificatePEMString);

        pemText.setText(certificatePEMString);

        if (certificate == null) {
            subjectText.setText("");
            issuerText.setText("");
            validFromText.setText("");
            validToText.setText("");
            pubKeyOpenSSHText.setText("");
//            pubKeyPEMText.setText("");
            subjectAltText.setVisible(false);
            issuerAltText.setVisible(false);
        } else {
            subjectText.setText(certificate.getSubjectX500Principal().toString());
            issuerText.setText(certificate.getIssuerX500Principal().toString());
            validFromText.setText(certificate.getNotBefore().toString());
            validToText.setText(certificate.getNotAfter().toString());
            pubKeyOpenSSHText.setText("");
//            pubKeyPEMText.setText("");
            subjectAltText.setVisible(false);
            issuerAltText.setVisible(false);

            Collection<List<?>> altNames = null;
            try {
                altNames = certificate.getSubjectAlternativeNames();
            } catch (CertificateParsingException e) {
                //ignore subject Alt name parsing exceptions
            }
            if (altNames != null && !altNames.isEmpty()) {
                subjectAltText.setVisible(true);

                String alts = "";
                for (List<?> altName : altNames) {
                    Integer nameType = (Integer) altName.get(0);
                    //                if (nameType == 6) {
                    //                    //uniformResourceIdentifier
                    //                    String urn = (String) altName.get(1);
                    //                }
//                           System.out.println("Subject alt name: type=" + nameType + " value=" + altName.get(1).toString());
                    if (alts.length() > 0)  alts += "\n";
                    alts += altIntToString(nameType) + " -> " + altName.get(1).toString();
                }
                subjectAltText.setText(alts);
            } else
                subjectAltText.setVisible(false);


            altNames = null;
            try {
                altNames = certificate.getIssuerAlternativeNames();
            } catch (CertificateParsingException e) {
                //ignore subject Alt name parsing exceptions
            }
            if (altNames != null && !altNames.isEmpty()) {
                issuerAltText.setVisible(true);

                String alts = "";
                for (List<?> altName : altNames) {
                    Integer nameType = (Integer) altName.get(0);
                    //                if (nameType == 6) {
                    //                    //uniformResourceIdentifier
                    //                    String urn = (String) altName.get(1);
                    //                }
//                           System.out.println("Subject alt name: type=" + nameType + " value=" + altName.get(1).toString());
                    if (alts.length() > 0)  alts += "\n";
                    alts += altIntToString(nameType) + " -> " + altName.get(1).toString();
                }
                issuerAltText.setText(alts);
            } else
                issuerAltText.setVisible(false);


            if (certificate.getPublicKey() instanceof RSAPublicKey)
                pubKeyOpenSSHText.setText(KeyUtil.rsaPublicKeyToOpenSshAuthorizedKeysFormat((RSAPublicKey) certificate.getPublicKey()));
            else
                pubKeyOpenSSHText.setText("(not an RSA key)");
//            pubKeyPEMText.setText(KeyUtil.publicKeyToPem(certificate.getPublicKey()));
        }

//        System.out.println("certificate=" + certificate);
    }


    private static Stage x509certificateStage = null;
    private static X509CertificatePanel x509certificatePanel = null;
    public static Pair<Stage, X509CertificatePanel> showX509Certificate(String x509CertificatePem) {
        return showX509Certificate(x509CertificatePem, null);
    }
    public static Pair<Stage, X509CertificatePanel> showX509Certificate(String x509CertificatePem, Stage stage) {
        try {
            if (x509certificateStage == null) {
                x509certificatePanel = new X509CertificatePanel();

                x509certificatePanel.setCertificatePEMString(x509CertificatePem);

                Scene scene = new Scene(x509certificatePanel);

                if (stage != null)
                    x509certificateStage = stage;
                else
                    x509certificateStage = new Stage();
                x509certificateStage.setScene(scene);
            }
            if (stage != null)
                assert x509certificateStage == stage;
            assert x509certificateStage != null;
            x509certificateStage.show();
            return new Pair<>(x509certificateStage, x509certificatePanel);
        } catch (Exception e) {
            throw new RuntimeException("Something went wrong showing the X509certificate: "+e.getMessage(), e);
        }
    }
}
