/*******************************************************************************
 * Copyright (c) 2019 Eclipse RDF4J contributors.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Distribution License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *******************************************************************************/
package org.eclipse.rdf4j.federated.performance;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.eclipse.rdf4j.federated.SPARQLBaseTest;
import org.eclipse.rdf4j.federated.generator.DataGenerator;
import org.eclipse.rdf4j.federated.generator.ResultGenerator;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * Simple manual performance test for FedX.
 * <p>
 * Can be applied against a scenario created with {@link DataGenerator} and {@link ResultGenerator}. An example scenario
 * is on the classpath in "build/test/fedxPerformanceScenario.jar"
 * </p>
 *
 * <p>
 * Note that the performance scenario does include the correctness check, i.e. it does not only evaluate the pure query
 * duration, but also checks the query results against a prepared result.
 * </p>
 *
 * <p>
 * Example performance run in local environment:
 * </p>
 *
 * <pre>
 * query01: avg=916, min=858, max=984
 * query02: avg=5, min=5, max=6
 * query03: avg=17, min=14, max=24
 * query04: avg=902, min=847, max=972
 * query05: avg=145, min=121, max=239
 * query06: avg=57, min=43, max=79
 * query07: avg=276, min=246, max=346
 * query08: avg=2640, min=2083, max=3747
 * query09: avg=72, min=66, max=94
 * query10: avg=785, min=664, max=1006
 * query11: avg=2465, min=2249, max=3707
 * </pre>
 *
 *
 * @author Andreas Schwarte
 *
 */
public class FedXPerformanceTest extends SPARQLBaseTest {

	static final String[] queries = new String[] {
			"query01", "query02", "query03", "query04", "query05", "query06", "query07", "query08", "query09",
			"query10", "query11" /* , "query12" */
	};

	@Test
	@Disabled
	public void testPerformance() throws Throwable {
		String basePackage = "/tests/performance/";

		/* prepare endpoints */
		prepareTest(Arrays.asList(basePackage + "data1.ttl", basePackage + "data2.ttl", basePackage + "data3.ttl",
				basePackage + "data4.ttl"));

		// warm-up
		for (String query : queries) {
			long start = System.currentTimeMillis();
			execute(basePackage + query + ".rq", basePackage + query + ".srx", false);
			long duration = System.currentTimeMillis() - start;
			System.out.println("Warmup " + query + " (Duration: " + duration + ")");
		}

		final int maxRuns = 10;
		List<Run> runs = Lists.newArrayList();
		for (int i = 1; i <= maxRuns; i++) {
			System.out.println("Run " + i);
			long runStart = System.currentTimeMillis();
			Run run = new Run(i);
			runs.add(run);
			for (String query : queries) {
				SingleQueryRun queryRun = new SingleQueryRun(query);
				run.addRun(queryRun);
				long start = System.currentTimeMillis();
				execute(basePackage + query + ".rq", basePackage + query + ".srx", false);
				long duration = System.currentTimeMillis() - start;
				queryRun.duration = duration;
			}
			System.out.println("Run " + i + " duration: " + (System.currentTimeMillis() - runStart));
		}

		for (String query : queries) {
			long totalDuration = 0;
			long minDuration = Long.MAX_VALUE;
			long maxDuration = Long.MIN_VALUE;
			for (Run run : runs) {
				SingleQueryRun qRun = run.queryRuns.get(query);
				totalDuration += qRun.duration;
				minDuration = qRun.duration < minDuration ? qRun.duration : minDuration;
				maxDuration = qRun.duration > maxDuration ? qRun.duration : maxDuration;
			}
			long avg = totalDuration / runs.size();

			System.out.println(query + ": avg=" + avg + ", min=" + minDuration + ", max=" + maxDuration);

		}

		log.info("Done");
	}

	@Test
	@Disabled // FIXME currently stack overflow error in result comparison due to implementation issue in RDF4J
	public void testRun() throws Exception {
		String basePackage = "/tests/performance/";

		/* prepare endpoints */
		prepareTest(Arrays.asList(basePackage + "data1.ttl", basePackage + "data2.ttl", basePackage + "data3.ttl",
				basePackage + "data4.ttl"));

		String query = "query12";
		execute(basePackage + query + ".rq", basePackage + query + ".srx", false);
	}

	static class Run {

		final int runId;

		protected Map<String, SingleQueryRun> queryRuns = Maps.newLinkedHashMap();

		public Run(int runId) {
			super();
			this.runId = runId;
		}

		public void addRun(SingleQueryRun queryRun) {
			queryRuns.put(queryRun.query, queryRun);
		}
	}

	static class SingleQueryRun {

		private final String query;

		private long duration;

		public SingleQueryRun(String query) {
			super();
			this.query = query;
		}
	}

}
