import java.util.*;
import net.sourceforge.sizeof.*;
import java.io.*;
import java.lang.reflect.*;

/**
 * Testet Set-Varianten auf ihre Effizenz
 *
 * @author  Rainer Helfrich
 * @version November 2020
 */
public class BenchmarkBase
{
    protected Set theSet;
    Random zufallszahlen;

    protected BenchmarkBase()
    {
        zufallszahlen = new Random();
    }
    
    protected void init(Class c) throws Exception
    {
        theSet = (Set)c.newInstance();
    }

    /**
     * Wird benötigt, um in einem neuen Prozess den Speichertest durchzuführen.
     * Sollte nicht direkt aufgerufen werden.
     */
    public static void main(String[] args)
    {
        try
        {
            int anzahl = Integer.parseInt(args[0]);
            Class c = Class.forName(args[1]);
            MeinBenchmark b = new MeinBenchmark();
            b.init(c);
            Method m = b.getClass().getMethod(args[2], new Class[]{int.class});
            m.invoke(b, new Object[]{anzahl});
            System.out.println("@"+SizeOf.deepSizeOf((Object)b.theSet)+"@");
        }
        catch(Exception x)
        {
            System.out.println(x.getMessage());
        }
    }

    /**
     * Führt einen Laufzeittest aus. Wiederholt den Test 100 mal, um Ausreißer zu vermeiden.
     * Sollte nicht direkt aufgerufen werden.
     * @param typ       Die Unterklasse von Set, die getestet werden soll
     * @param methode   Der Name der Methode in der Klasse MeinBenchmark, die zum Testen ausgeführt werden soll
     * @param anzahl    Ein Parameter für die Methode namens "methode", der z.B. die Anzahl der Operationen steuert
     * @return          Die Laufzeit des Tests in Mikrosekunden (µs)
     */
    protected static int laufzeitTest(String typ, String methode, int anzahl)
    {
        try
        {
            Class c = Class.forName(typ);
            MeinBenchmark b = new MeinBenchmark();
            final int DURCHGAENGE = 100;
            long totalTime = 0;
            for (int i = 0; i < DURCHGAENGE; i++)
            {
                b.init(c);
                Method m = b.getClass().getMethod(methode, new Class[]{int.class});
                long vorher = System.nanoTime();
                m.invoke(b, new Object[]{anzahl});
                long nachher = System.nanoTime();
                totalTime += (nachher - vorher) / 1000;
            }
            return (int)(totalTime / DURCHGAENGE);
        }
        catch(Exception x)
        {
            System.out.println(x.getMessage());
        }
        return -1;
    }

    /**
     * Führt einen Speichertest aus. Sollte nicht direkt aufgerufen werden.
     * @param typ       Die Unterklasse von Set, die getestet werden soll
     * @param methode   Der Name der Methode in der Klasse MeinBenchmark, die zum Testen ausgeführt werden soll
     * @param anzahl    Ein Parameter für die Methode namens "methode", der z.B. die Anzahl der Operationen steuert
     * @return          Die Größe der Set in Bytes, nachdem die Testmethode ausgeführt wurde
     */
    protected static int speicherTest(String typ, String methode, int anzahl)
    {
        try
        {
            Process process = new ProcessBuilder("java","-javaagent:./+libs/SizeOf.jar", MeinBenchmark.class.getName(), ""+anzahl, typ, methode).start();
            InputStream is = process.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String line;

            StringBuilder sb = new StringBuilder();
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            String strOut = sb.toString();
            int idx = strOut.indexOf("@");
            if (idx < 0)
            {
                throw new Exception("Fehler: Ungültige Ausgabe:\n"+strOut);
            }
            int idx2 = strOut.indexOf("@", idx+1);
            if (idx2 < 0)
            {
                throw new Exception("Fehler: Ungültige Ausgabe:\n"+strOut);
            }
            int n = Integer.parseInt(strOut.substring(idx+1, idx2));
            return n;
        }
        catch(Exception x)
        {
            System.out.println(x.getMessage());
        }
        return -1;
    }
}
