/*
 * Copyright 2015-2016 the original author or authors.
 *
 * All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License v1.0 which
 * accompanies this distribution and is available at
 *
 * http://www.eclipse.org/legal/epl-v10.html
 */

package org.junit.gen5.console.tasks;

import static org.junit.gen5.engine.TestExecutionResult.Status.ABORTED;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.Clock;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.junit.gen5.engine.TestExecutionResult;
import org.junit.gen5.launcher.TestIdentifier;
import org.junit.gen5.launcher.TestPlan;

class XmlReportData {

	private static final int MILLIS_PER_SECOND = 1000;

	private final Map<TestIdentifier, TestExecutionResult> finishedTests = new ConcurrentHashMap<>();
	private final Map<TestIdentifier, String> skippedTests = new ConcurrentHashMap<>();
	private final Map<TestIdentifier, Long> startTimes = new ConcurrentHashMap<>();
	private final Map<TestIdentifier, Long> endTimes = new ConcurrentHashMap<>();

	private final TestPlan testPlan;
	private final Clock clock;

	XmlReportData(TestPlan testPlan, Clock clock) {
		this.testPlan = testPlan;
		this.clock = clock;
	}

	TestPlan getTestPlan() {
		return testPlan;
	}

	void markSkipped(TestIdentifier testIdentifier, String reason) {
		skippedTests.put(testIdentifier, reason == null ? "" : reason);
	}

	void markStarted(TestIdentifier testIdentifier) {
		startTimes.put(testIdentifier, clock.millis());
	}

	void markFinished(TestIdentifier testIdentifier, TestExecutionResult result) {
		endTimes.put(testIdentifier, clock.millis());
		if (result.getStatus() == ABORTED) {
			String reason = result.getThrowable().map(XmlReportData::readStackTrace).orElse("");
			skippedTests.put(testIdentifier, reason);
		}
		else {
			finishedTests.put(testIdentifier, result);
		}
	}

	boolean wasSkipped(TestIdentifier testIdentifier) {
		return skippedTests.containsKey(testIdentifier);
	}

	boolean wasFinished(TestIdentifier testIdentifier) {
		return finishedTests.containsKey(testIdentifier);
	}

	double getDurationInSeconds(TestIdentifier testIdentifier) {
		long startMillis = startTimes.getOrDefault(testIdentifier, 0L);
		long endMillis = endTimes.getOrDefault(testIdentifier, startMillis);
		return (endMillis - startMillis) / (double) MILLIS_PER_SECOND;
	}

	String getSkipReason(TestIdentifier test) {
		return skippedTests.get(test);
	}

	TestExecutionResult getResult(TestIdentifier testIdentifier) {
		return finishedTests.get(testIdentifier);
	}

	// TODO #86 Move to ExceptionUtils
	static String readStackTrace(Throwable throwable) {
		StringWriter stringWriter = new StringWriter();
		try (PrintWriter printWriter = new PrintWriter(stringWriter)) {
			throwable.printStackTrace(printWriter);
		}
		return stringWriter.toString();
	}

}

@@ -500,16 +500,69 @@
 .Clock;%0A
+import java.time.Duration;%0Aimport java.time.Instant;%0A
 import j
@@ -575,16 +575,16 @@
 il.Map;%0A
-
 import j
@@ -1061,23 +1061,29 @@
 er, 
-Long%3E startTime
+Instant%3E startInstant
 s = 
@@ -1148,21 +1148,27 @@
 er, 
-Long%3E endTime
+Instant%3E endInstant
 s = 
@@ -1604,20 +1604,23 @@
 %0A%09%09start
-Time
+Instant
 s.put(te
@@ -1635,30 +1635,31 @@
 fier, clock.
-millis
+instant
 ());%0A%09%7D%0A%0A%09vo
@@ -1740,20 +1740,23 @@
  %7B%0A%09%09end
-Time
+Instant
 s.put(te
@@ -1779,14 +1779,15 @@
 ock.
-millis
+instant
 ());
@@ -2312,32 +2312,36 @@
 %7B%0A%09%09
-long startMillis
+Instant startInstant
  = start
 Time
@@ -2336,20 +2336,23 @@
  = start
-Time
+Instant
 s.getOrD
@@ -2378,39 +2378,57 @@
 er, 
-0L);%0A%09%09long endMillis = endTime
+Instant.EPOCH);%0A%09%09Instant endInstant = endInstant
 s.ge
@@ -2459,22 +2459,23 @@
 r, start
-Millis
+Instant
 );%0A%09%09ret
@@ -2482,32 +2482,60 @@
 urn 
-(endMillis - start
+Duration.between(startInstant, endInstant).to
 Millis
+(
 ) / 

Use Instants instead of longs