package ca.juliusdavies.base64bench;

import com.twmacinta.util.MD5;

import java.io.File;
import java.security.MessageDigest;
import java.util.Random;

import static ca.juliusdavies.base64bench.Base64Bench.FACTOR;
import static ca.juliusdavies.base64bench.Base64Bench.PRINT_OUTPUT;
import static ca.juliusdavies.base64bench.Base64Bench.REPS;
import static ca.juliusdavies.base64bench.Base64Bench.printEncodeStat;

public class MD5BenchByte2Byte {

    private static boolean nativeLibLoaded = false;

    public static void main(String[] args) throws Exception {

        if (args.length > 0) {
            String s = args[0];
            if (s.endsWith("MD5.so") || s.endsWith("MD5.dll")) {
                File f = new File(s);
                System.setProperty("com.twmacinta.util.MD5.NO_NATIVE_LIB", f.getCanonicalPath());
                MD5.initNativeLibrary();

                if (MD5.native_lib_loaded) {
                    System.out.println("Native lib loaded successfully!  (" + f.getCanonicalPath() + ")");
                    nativeLibLoaded = true;
                } else {
                    throw new RuntimeException("Failed to load native lib... (" + f.getCanonicalPath() + ")");
                }

            }
        }

        System.out.println("\n   TINY DATA new byte[12]");
        FACTOR = 10000;
        test(12);

        System.out.println("\n  SMALL DATA new byte[123]");
        FACTOR = 1000;
        test(123);

        System.out.println("\n MEDIUM DATA new byte[1234]");
        FACTOR = 100;
        test(1234);

        System.out.println("\n  LARGE DATA new byte[12345]");
        FACTOR = 10;
        test(12345);

        System.out.println("\nX-LARGE DATA new byte[123456]");
        FACTOR = 1;
        test(123456);
    }

    private static void test(int dataSize) throws Exception {
        byte[] data = new byte[dataSize];
        Random r = new Random();
        r.nextBytes(data);


        System.gc();

        // Force Fast-MD5 to skip native.
        // fastMD5Byte2ByteNative() sets it back.
        MD5.native_lib_loaded = false;

        // WARMUP phase.
        PRINT_OUTPUT = false;
        javaByte2Byte(data);
        fastMD5Byte2Byte(data, "Java ");
        if (nativeLibLoaded) {
            fastMD5Byte2ByteNative(data);
        }

        // And now the results...
        PRINT_OUTPUT = true;
        javaByte2Byte(data);
        fastMD5Byte2Byte(data, "Java ");
        if (nativeLibLoaded) {
            fastMD5Byte2ByteNative(data);
        }
    }


    private static void javaByte2Byte(byte[] data) throws Exception {

        if (PRINT_OUTPUT) {
            System.out.println("\nJava builtin MD5...");
        }

        final MessageDigest md5 = MessageDigest.getInstance("MD5");
        byte[] digest = md5.digest(data);

        // and now let's bench!
        long start = System.currentTimeMillis();
        for (int i = 0; i < FACTOR * REPS; i++) {
            digest = md5.digest(data);
        }
        printEncodeStat(start, data);
        if (PRINT_OUTPUT) {System.out.println();}

        start = System.currentTimeMillis();
        for (int i = 0; i < FACTOR * REPS; i++) {
            digest = md5.digest(data);
        }
        printEncodeStat(start, data);
        if (PRINT_OUTPUT) {System.out.println();}
    }


    private static void fastMD5Byte2Byte(byte[] data, String name) throws Exception {

        if (PRINT_OUTPUT) {
            System.out.println("\n" + name + "Fast-MD5...");
        }

        MD5 md5 = new MD5();
        md5.Update(data);
        byte[] digest = md5.Final();

        // and now let's bench!
        long start = System.currentTimeMillis();
        for (int i = 0; i < FACTOR * REPS; i++) {
            md5.Update(data);
            digest = md5.Final();
        }
        printEncodeStat(start, data);
        if (PRINT_OUTPUT) {System.out.println();}

        start = System.currentTimeMillis();
        for (int i = 0; i < FACTOR * REPS; i++) {
            md5.Update(data);
            digest = md5.Final();
        }
        printEncodeStat(start, data);
        if (PRINT_OUTPUT) {System.out.println();}
    }


    private static void fastMD5Byte2ByteNative(byte[] data) throws Exception {

        MD5.native_lib_loaded = true;

        fastMD5Byte2Byte(data, "Native ");

        MD5.native_lib_loaded = false;

    }

}