/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira.junit
 */
package net.ranides.assira.junit;

import java.io.PrintStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public final class BenchmarkReport {
    
    private static final Pattern RE_BENCHMARK = Pattern.compile(
        "\\{vm=(.+?), trial=(.+?), benchmark=(.+?)\\} ([0-9,]+) ns; \\?=([0-9,]+) ns @ ([0-9]+) trials"
    );
    
    private BenchmarkReport() { /* utility class */ }
    
    public static void print(PrintStream out, String data) {
        
        InfoMap result = new InfoMap();
        for(String line : data.split("[\\n\\r]+")) {
            result.add(line);
        }
        
        String pattern = MessageFormat.format("%-{0}s    = %{1}.0f ? %.0f%n", 
            result.maxname,
            String.valueOf((int)result.maxtime).length()
        );
                
        for(List<Info> binfo : result.map.values()) {
            int n = 0; // binfo.size();
            double avg = 0;
            double dev = 0;
            for(Info info : binfo) {
                avg += info.trials*info.time;
                n += info.trials;
            }
            avg = avg / n;

            for(Info info : binfo) {
                dev += info.trials * ((avg - info.time) * (avg - info.time));
            }
            dev = Math.sqrt(dev) / n;
            out.printf(pattern, binfo.get(0).name, avg, dev);
        }
    }

    private static class InfoMap {
        
        private final Map<String, List<Info>> map = new TreeMap<>();
        
        public int maxname = 20;
        public double maxtime = 0;
        
        public void add(String line) {
            Matcher hit = RE_BENCHMARK.matcher(line);
            if(hit.find()) {
                add(new Info(hit));
            }
        }
        
        public void add(Info info) {
            maxname = Math.max(maxname, info.name.length());
            maxtime = Math.max(maxtime, info.time);
            
            if( !map.containsKey(info.name) ) {
                map.put(info.name, new ArrayList<>());
            }
            map.get(info.name).add(info);
        }
        
    }
    
    private static class Info {
        
        public final String vm;
        public final String name;
        public final String trial;
        public final double time;
        public final int trials;
        
        public Info(Matcher hit) {
            this.vm = hit.group(1);
            this.name = hit.group(3);
            this.trial = hit.group(2);
            this.time = Double.parseDouble(hit.group(4).replace(',', '.'));
            this.trials = Integer.parseInt(hit.group(6));
        }

    }

}
