package org.sagebionetworks.bridge.crypto;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.SystemUtils;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class PemUtils {

    public static String toPem(X509Certificate cert) throws CertificateEncodingException {
        checkNotNull(cert, "Cert cannot be null.");
        Base64 encoder = new Base64(Base64.PEM_CHUNK_SIZE);
        return BEGIN_CERT + NEW_LINE + encoder.encodeToString(cert.getEncoded()) + END_CERT;
    }

    public static X509Certificate loadCertificateFromPem(String pem) {
        Security.addProvider(new BouncyCastleProvider());
        try {
            List<String> lines = readLines(pem);
            byte[] decoded = decode(lines, BEGIN_CERT, END_CERT);
            X509CertificateHolder certHolder = new X509CertificateHolder(decoded);
            return new JcaX509CertificateConverter().setProvider(BcCmsConstants.PROVIDER).getCertificate(certHolder);
        } catch(IOException e) {
            throw new RuntimeException(e);
        } catch (CertificateException e) {
            throw new RuntimeException(e);
        }
    }

    public static String toPem(PrivateKey privateKey) throws CertificateEncodingException {
        checkNotNull(privateKey, "Private key cannot be null.");
        Base64 encoder = new Base64(Base64.PEM_CHUNK_SIZE);
        return BEGIN_PRIVATE_KEY + NEW_LINE + encoder.encodeToString(privateKey.getEncoded()) + END_PRIVATE_KEY;
    }

    public static PrivateKey loadPrivateKeyFromPem(String pem) {
        Security.addProvider(new BouncyCastleProvider());
        try {
            List<String> lines = readLines(pem);
            byte[] decoded = decode(lines, BEGIN_PRIVATE_KEY, END_PRIVATE_KEY);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
            KeyFactory keyFactory = KeyFactory.getInstance(BcCmsConstants.KEY_PAIR_ALGO, BcCmsConstants.PROVIDER);
            return keyFactory.generatePrivate(keySpec);
        } catch(IOException e) {
            throw new RuntimeException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException(e);
        } catch (NoSuchProviderException e) {
            throw new RuntimeException(e);
        }
    }

    private static byte[] decode(List<String> lines, String begin, String end) {
        checkNotNull(lines, "Lines cannot be null");
        checkArgument(lines.size() >= 2, "Two few lines.");
        StringBuilder base64Encoded = new StringBuilder();
        boolean addLine = false;
        for (String line : lines) {
            if (end.equals(line)) {
                break;
            }
            if (addLine) {
                base64Encoded.append(line);
                base64Encoded.append(NEW_LINE);
            }
            if (begin.equals(line)) {
                addLine = true;
            }
        }
        Base64 encoder = new Base64(Base64.PEM_CHUNK_SIZE);
        byte[] decoded = encoder.decode(base64Encoded.toString());
        return decoded;
    }

    private static List<String> readLines(String text) throws IOException {
        List<String> lines = new ArrayList<String>();
        try (
            StringReader stringReader = new StringReader(text);
            BufferedReader bufferedReader = new BufferedReader(stringReader)
        ) {
            String line = bufferedReader.readLine();
            while (line != null) {
                lines.add(line);
                line = bufferedReader.readLine();
            }
        }
        return lines;
    }

    private static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
    private static final String END_CERT = "-----END CERTIFICATE-----";
    private static final String BEGIN_PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----";
    private static final String END_PRIVATE_KEY = "-----END RSA PRIVATE KEY-----";
    private static final String NEW_LINE = SystemUtils.LINE_SEPARATOR;
}

@@ -188,79 +188,19 @@
 .io.
-BufferedReader;%0Aimport java.io.IOException;%0Aimport java.io.StringReader
+IOException
 ;%0Aim
@@ -635,59 +635,8 @@
 pec;
-%0Aimport java.util.ArrayList;%0Aimport java.util.List;
 %0A%0Aim
@@ -1293,32 +1293,89 @@
 m(String pem) %7B%0A
+        checkNotNull(pem, %22Pem string cannot be null.%22);%0A
         Security
@@ -1438,36 +1438,32 @@
             
-List%3C
 String
-%3E
+%5B%5D
  lines = rea
@@ -2315,32 +2315,89 @@
 m(String pem) %7B%0A
+        checkNotNull(pem, %22Pem string cannot be null.%22);%0A
         Security
@@ -2460,36 +2460,32 @@
             
-List%3C
 String
-%3E
+%5B%5D
  lines = rea
@@ -3218,28 +3218,24 @@
  decode(
-List%3C
 String
-%3E
+%5B%5D
  lines, 
@@ -3347,14 +3347,14 @@
 nes.
-size()
+length
  %3E= 
@@ -3358,17 +3358,17 @@
 %3E= 2, %22T
-w
+o
 o few li
@@ -3980,28 +3980,24 @@
  static 
-List%3C
 String
-%3E
+%5B%5D
  readLin
@@ -4045,436 +4045,36 @@
     
-List%3CString%3E lines = new ArrayList%3CString%3E();%0A        try (%0A            StringReader stringReader = new StringReader(text);%0A            BufferedReader bufferedReader = new BufferedReader(stringReader)%0A        ) %7B%0A            String line = bufferedReader.readLine();%0A            while (line != null) %7B%0A                lines.add(line);%0A                line = bufferedReader.readLine();%0A            %7D%0A        %7D%0A        return lines
+return text.split(%22%5C%5Cr?%5C%5Cn%22)
 ;%0A  

Update per code review