package kz.gamma;

import kz.gamma.cmp.CMPEncrypt;
import kz.gamma.jce.provider.GammaTechProvider;
import kz.gamma.tumarcsp.LibraryWrapper;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.security.Security;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.atomic.AtomicLong;

public class EncryptTest24129 {
    private final static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    private final static int THREAD_COUNT = 1000;
    private final static int INTERNAL_ITER_COUNT = 1000;
    private final static int TEXT_LEN_BYTES = 32;
    private final static AtomicLong totalSuccess = new AtomicLong();

    public static void main(String... args) throws Exception {
        log("Start");
        byte[] message = new byte[TEXT_LEN_BYTES];
        Security.addProvider(new GammaTechProvider());
        X509Certificate[] certs = new X509Certificate[]{loadCertFromFile("/keys/db_client1.cer")};
        try {
            Thread[] threads = new Thread[THREAD_COUNT];
            for (int i = 0; i < THREAD_COUNT; i++) {
                threads[i] = new Thread(() -> {
                    try {
                        for (int k = 0; k < INTERNAL_ITER_COUNT; k++) {
                            if (doJob(certs, message)) {
                                long count = totalSuccess.incrementAndGet();
                                if (count % 10000 == 0) {
                                    log("success count: " + count);
                                }
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
            }

            for (Thread thread : threads) {
                thread.start();
            }

            for (Thread thread : threads) {
                thread.join();
            }

            log("Total success count: " + totalSuccess.get());
            log("end");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static boolean doJob(X509Certificate[] certs, byte[] message) throws Exception {
        byte[] encrypted = encrypt(message, certs);
        byte[] decrypted = decrypt(encrypted);

        //проверка корректности расшифрованных данных:
        if (!Arrays.equals(message, decrypted)) {
           /* log("\tdata (original): \n\t" + UtilCM.array2hex(message));
            log("\tencrypted: \n\t" + UtilCM.array2hex(encrypted));
            log("\tdecrypted: \n\t" + UtilCM.array2hex(decrypted));
*/
            log("wrong decrypted message");
            return false;

        } else {
            /*log("\tdata (original): \n\t" + UtilCM.array2hex(message));
            log("\tencrypted: \n\t" + UtilCM.array2hex(encrypted));
            log("\tdecrypted: \n\t" + UtilCM.array2hex(decrypted));*/
            return true;
        }
    }

    public static byte[] encrypt(byte[] zippedMessage, X509Certificate[] recipients) throws Exception {
        try {
            CMPEncrypt cmpEncrypt = new CMPEncrypt();
            byte[] eMsg = cmpEncrypt.Encrypt(recipients, zippedMessage, true, LibraryWrapper.CALG_GOST);
            return eMsg;
        } catch (Exception e) {
            throw new Exception("Ошибка при шифровании (encrypt); " + e.getMessage());
        }
    }

    public static byte[] decrypt(byte[] cipherValue) throws Exception {
        try {
            CMPEncrypt cmpEncrypt = new CMPEncrypt();
            byte[] decryptedMessage = cmpEncrypt.Decrypt("profile://db_client1", cipherValue);
            return decryptedMessage;
        } catch (Exception e) {
            throw new Exception("Ошибка при расшифровывании (decrypt); " + e.getMessage());
        }
    }

    public static X509Certificate loadCertFromFile(String fileName) {
        X509Certificate cert = null;
        try {
            byte[] buf;
            FileInputStream f = new FileInputStream(fileName);
            buf = new byte[f.available()];
            f.read(buf, 0, f.available());
            cert = getCertFromByteArray(buf);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cert;
    }

    public static X509Certificate getCertFromByteArray(byte[] cert) throws Exception {
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509Certificate x509cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(cert));
        return x509cert;
    }

    public static void log(String text) {
        String log = dateFormat.format(new Date()) + " [" + Thread.currentThread().getName() + "] " + text;
        System.out.println(log);
    }
}
