/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.teradata.tempto.internal.listeners;

import com.google.common.base.Joiner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;

import java.math.BigDecimal;
import java.math.RoundingMode;

import static com.google.common.base.Preconditions.checkState;
import static com.teradata.tempto.internal.initialization.RequirementsExpanderInterceptor.getMethodsCountFromContext;
import static java.lang.System.currentTimeMillis;

public class ProgressLoggingListener
        implements ITestListener
{
    private final static Logger LOGGER = LoggerFactory.getLogger(ProgressLoggingListener.class);

    private int started;
    private int succeeded;
    private int skipped;
    private int failed;
    private long startTime;
    private long testStartTime;

    private final TestMetadataReader testMetadataReader;

    public ProgressLoggingListener()
    {
        this.testMetadataReader = new TestMetadataReader();
    }

    @Override
    public void onStart(ITestContext context)
    {
        startTime = currentTimeMillis();
        LOGGER.info("Starting tests running");
    }

    @Override
    public void onTestStart(ITestResult testCase)
    {
        TestMetadata testMetadata = testMetadataReader.readTestMetadata(testCase);
        testStartTime = currentTimeMillis();
        started++;
        int total = getMethodsCountFromContext(testCase.getTestContext());
        String testGroups = Joiner.on(", ").join(testMetadata.testGroups);
        LOGGER.info("[{} of {}] {} (Groups: {})", started, total, testMetadata.testName, testGroups);
    }

    @Override
    public void onTestSuccess(ITestResult testCase)
    {
        succeeded++;
        logTestEnd("SUCCESS");
    }

    @Override
    public void onTestFailure(ITestResult testCase)
    {
        failed++;
        logTestEnd("FAILURE");
        if (testCase.getThrowable() != null) {
            LOGGER.error("Failure cause:", testCase.getThrowable());
        }
    }

    @Override
    public void onTestSkipped(ITestResult testCase)
    {
        skipped++;
        LOGGER.info("SKIPPED");
    }

    private void logTestEnd(String outcome)
    {
        long executionTime = currentTimeMillis() - testStartTime;
        if (executionTime < 1000) {
            LOGGER.info(outcome);
        }
        else {
            LOGGER.info("{}     /    took {}", outcome, formatDuration(executionTime));
        }
    }

    @Override
    public void onTestFailedButWithinSuccessPercentage(ITestResult testCase)
    {
    }

    @Override
    public void onFinish(ITestContext context)
    {
        checkState(succeeded + failed + skipped > 0, "No tests executed");
        LOGGER.info("");
        LOGGER.info("Completed {} tests", started);
        LOGGER.info("{} SUCCEEDED      /      {} FAILED      /      {} SKIPPED", succeeded, failed, skipped);
        LOGGER.info("Tests execution took {}", formatDuration(currentTimeMillis() - startTime));
    }

    private static String formatDuration(long durationInMillis)
    {
        BigDecimal durationSeconds = durationInSeconds(durationInMillis);
        if (durationSeconds.longValue() > 60) {
            long minutes = durationSeconds.longValue() / 60;
            long restSeconds = durationSeconds.longValue() % 60;
            return String.format("%d minutes and %d seconds", minutes, restSeconds);
        }
        else {
            return String.format("%s seconds", durationSeconds);
        }
    }

    private static BigDecimal durationInSeconds(long millis)
    {
        return new BigDecimal(millis).divide(new BigDecimal(1000), 1, RoundingMode.HALF_UP);
    }
}

@@ -1035,24 +1035,63 @@
 romContext;%0A
+import static java.lang.String.format;%0A
 import stati
@@ -1128,16 +1128,16 @@
 Millis;%0A
-
 %0Apublic 
@@ -1864,91 +1864,8 @@
   %7B%0A
-        TestMetadata testMetadata = testMetadataReader.readTestMetadata(testCase);%0A
     
@@ -2003,83 +2003,8 @@
 ));%0A
-        String testGroups = Joiner.on(%22, %22).join(testMetadata.testGroups);%0A
     
@@ -2037,21 +2037,8 @@
 %5D %7B%7D
- (Groups: %7B%7D)
 %22, s
@@ -2056,41 +2056,32 @@
 al, 
-testMetadata.t
+formatT
 estName
-, 
+(
 test
-Groups
+Case)
 );%0A 
@@ -2198,16 +2198,26 @@
 TestEnd(
+testCase, 
 %22SUCCESS
@@ -2336,16 +2336,26 @@
 TestEnd(
+testCase, 
 %22FAILURE
@@ -2649,16 +2649,38 @@
 TestEnd(
+ITestResult testCase, 
 String o
@@ -2890,16 +2890,19 @@
     /   
+ %7B%7D
  took %7B%7D
@@ -2912,16 +2912,42 @@
 outcome,
+ formatTestName(testCase),
  formatD
@@ -3417,24 +3417,24 @@
 , skipped);%0A
-
         LOGG
@@ -3517,32 +3517,336 @@
 tTime));%0A    %7D%0A%0A
+    private String formatTestName(ITestResult testCase)%0A    %7B%0A        TestMetadata testMetadata = testMetadataReader.readTestMetadata(testCase);%0A        String testGroups = Joiner.on(%22, %22).join(testMetadata.testGroups);%0A        return format(%22%25s (Groups: %25s)%22, testMetadata.testName, testGroups);%0A    %7D%0A%0A
     private stat

Print test case name when it ends too