package framework;

import data.Benchmark;
import data.BenchmarkQuery;
import data.BenchmarkSession;
import framework.context.BenchmarkContext;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import util.IO;

/**
 * Runs a benchmark, considering all the given ingredients (context, ADBMS system, 
 * query generator).
 * 
 * @author George Merticariu
 * @author Dimitar Misev
 */
public class BenchmarkExecutor {

    private static final Logger log = LoggerFactory.getLogger(BenchmarkExecutor.class);

    public static final int MAX_RETRY = 3;

    private final QueryGenerator queryGenerator;
    private final QueryExecutor queryExecutor;
    private final AdbmsSystem systemController;
    private final BenchmarkContext benchmarkContext;
    private final DataManager dataManager;

    public BenchmarkExecutor(BenchmarkContext benchmarkContext, QueryGenerator queryGenerator,
            QueryExecutor queryExecutor, DataManager dataManager, AdbmsSystem systemController) {
        this.queryGenerator = queryGenerator;
        this.queryExecutor = queryExecutor;
        this.systemController = systemController;
        this.benchmarkContext = benchmarkContext;
        this.dataManager = dataManager;
    }

    public void runBenchmark() throws Exception {
        log.info("Executing " + benchmarkContext.getBenchmarkType() + 
                " benchmark on " + systemController.getSystemName() + ", "
                + benchmarkContext.getArrayDimensionality() + "D data of size "
                + benchmarkContext.getArraySizeShort() + " (" + benchmarkContext.getArraySize() + "B)");

        File resultsDir = IO.getResultsDir();
        File resultsFile = new File(resultsDir.getAbsolutePath(), systemController.getSystemName() + "_benchmark_results.csv");

        try (PrintWriter pr = new PrintWriter(new FileWriter(resultsFile, true))) {
            // the query executor should check whether a collection is already created
            if (benchmarkContext.isCreateData()) {
                systemController.restartSystem();
                dataManager.loadData();
            }
            if (!benchmarkContext.isDisableBenchmark()) {
                long arraysSize = benchmarkContext.getArraySize();
                long maxSelectSize = benchmarkContext.getMaxSelectSize();

                Benchmark benchmark = queryGenerator.getBenchmark();
                for (BenchmarkSession session : benchmark.getBenchmarkSessions()) {
                    pr.println("# Benchmark session: " + session.getDescription());
                    long totalQueryExecutionTime = 0;
                    for (BenchmarkQuery query : session.getBenchmarkQueries()) {
                        List<Long> queryExecutionTimes = new ArrayList<>();
                        log.info("Executing query: " + query.getQueryString());
                        for (int repeatIndex = 0; repeatIndex < benchmarkContext.getRepeatNumber(); ++repeatIndex) {
                            boolean failed = true;
                            long time = -1;

                            for (int retryIndex = 0; retryIndex < MAX_RETRY && failed; ++retryIndex) {
                                try {
                                    systemController.restartSystem();
                                    time = queryExecutor.executeTimedQuery(query.getQueryString());
                                    log.debug(" -> " + time + "ms");
                                    failed = false;
                                } catch (Exception ex) {
                                    log.warn(" query \"" + query.getQueryString() + "\" failed on try " + (retryIndex + 1) + ". Retrying.");
                                }
                            }
                            queryExecutionTimes.add(time);
                        }

                        StringBuilder resultLine = new StringBuilder();
                        resultLine.append(String.format("\"%s\", \"%s\", \"%s\", \"%d\", \"%d\", \"%d\", ",
                                systemController.getSystemName(), query.getQueryType().toString(), query.getQueryString(),
                                query.getDimensionality(), arraysSize, maxSelectSize));

                        boolean isFirst = true;
                        for (Long queryExecutionTime : queryExecutionTimes) {
                            if (!isFirst) {
                                resultLine.append(", ");
                                isFirst = false;
                            } else {
                                totalQueryExecutionTime += queryExecutionTime;
                            }
                            resultLine.append(queryExecutionTime);
                        }

                        pr.println(resultLine.toString());
                        pr.flush();
                    }

                    pr.println("Benchmark session '" + session.getDescription() + "' execution time (ms): " + totalQueryExecutionTime);
                }
            }
        } finally {
            if (benchmarkContext.isDropData()) {
                systemController.restartSystem();
                dataManager.dropData();
            }
        }
    }

}

@@ -1727,16 +1727,65 @@
 + %22B)%22);
+%0A        %0A        boolean alreadyDropped = false;
 %0A%0A      
@@ -2053,82 +2053,8 @@
     
-// the query executor should check whether a collection is already created
 %0A   
@@ -2158,32 +2158,52 @@
 %0A               
+ long loadDataTime =
  dataManager.loa
@@ -2215,33 +2215,124 @@
 ();%0A            
-%7D
+    pr.println(%22Loaded benchmark data in (ms): %22 + loadDataTime);%0A            %7D%0A            
 %0A            if 
@@ -5243,19 +5243,12 @@
     
-%7D finally %7B
+    
 %0A   
@@ -5358,16 +5358,36 @@
         
+ long dropDataTime =
  dataMan
@@ -5395,32 +5395,439 @@
 ger.dropData();%0A
+                pr.println(%22Dropped benchmark data in (ms): %22 + dropDataTime);%0A                alreadyDropped = true;%0A            %7D%0A        %7D finally %7B%0A            if (benchmarkContext.isDropData() && !alreadyDropped) %7B%0A                systemController.restartSystem();%0A                long dropDataTime = dataManager.dropData();%0A                log.info(%22Dropped benchmark data in (ms): %22 + dropDataTime);%0A
             %7D%0A  

print load time