package com.googlecode.yatspec.junit;

import com.googlecode.totallylazy.Predicate;
import com.googlecode.yatspec.state.Result;
import com.googlecode.yatspec.state.Scenario;
import com.googlecode.yatspec.state.TestResult;
import com.googlecode.yatspec.state.givenwhenthen.TestState;
import com.googlecode.yatspec.state.givenwhenthen.WithTestState;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;

import java.io.File;
import java.util.List;

import static com.googlecode.totallylazy.Sequences.sequence;
import static java.lang.System.getProperty;

public class SpecRunner extends TableRunner {
    public static final String OUTPUT_DIR = "yatspec.output.dir";
    private final Result testResult;
    private Scenario currentScenario;

    public SpecRunner(Class<?> klass) throws org.junit.runners.model.InitializationError {
        super(klass);
        testResult = new TestResult(klass);
    }

    @Override
    protected List<FrameworkMethod> computeTestMethods() {
        return sequence(super.computeTestMethods()).filter(isNotEvaluateMethod()).toList();
    }

    private static Predicate<FrameworkMethod> isNotEvaluateMethod() {
        return new Predicate<FrameworkMethod>() {
            public boolean matches(FrameworkMethod method) {
                return !method.getName().equals("evaluate");
            }
        };
    }

    private WithCustomResultListeners listeners = new DefaultResultListeners();

    @Override
    protected Object createTest() throws Exception {
        Object instance = super.createTest();
        if(instance instanceof WithCustomResultListeners){
            listeners = (WithCustomResultListeners) instance;
        } else {
            listeners = new DefaultResultListeners();
        }
        return instance;
    }

    @Override
    public void run(RunNotifier notifier) {
        final SpecListener listener = new SpecListener();
        notifier.addListener(listener);
        super.run(notifier);
        notifier.removeListener(listener);
        try {
            for (SpecResultListener resultListener : listeners.getResultListeners()) {
                resultListener.complete(outputDirectory(), testResult);
            }
        } catch (Exception e) {
            System.out.println("Error while writing yatspec output");
            e.printStackTrace(System.out);
        }
    }

    public static File outputDirectory() {
        return new File(getProperty(OUTPUT_DIR, getProperty("java.io.tmpdir")));
    }

    @Override
    protected Statement methodInvoker(final FrameworkMethod method, final Object test) {
        final Statement statement = super.methodInvoker(method, test);
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                currentScenario = testResult.getScenario(method.getName());

                if (test instanceof WithTestState) {
                    TestState testState = ((WithTestState) test).testState();
                    currentScenario.setTestState(testState);
                }
                statement.evaluate();
            }
        };
    }

    private boolean isInTest() {
        return currentScenario != null;
    }

    private final class SpecListener extends RunListener {

        @Override
        public void testFailure(Failure failure) throws Exception {
            if (isInTest()) {
                currentScenario.setException(failure.getException());
            }
        }
    }
}

@@ -616,20 +616,68 @@
 va.util.
-List
+HashMap;%0Aimport java.util.List;%0Aimport java.util.Map
 ;%0A%0Aimpor
@@ -937,16 +937,28 @@
  private
+ Map%3CString,
  Scenari
@@ -958,16 +958,17 @@
 Scenario
+%3E
  current
@@ -975,16 +975,50 @@
 Scenario
+ = new HashMap%3CString, Scenario%3E()
 ;%0A%0A    p
@@ -1826,16 +1826,17 @@
       if
+ 
 (instanc
@@ -1874,16 +1874,17 @@
 steners)
+ 
 %7B%0A      
@@ -3063,65 +3063,263 @@
     
-currentScenario = testResult.getScenario(method.getName()
+final String fullyQualifiedTestMethod = test.getClass().getCanonicalName() + %22.%22 + method.getName();%0A                final Scenario scenario = testResult.getScenario(method.getName());%0A                currentScenario.put(fullyQualifiedTestMethod, scenario
 );%0A%0A
@@ -3485,16 +3485,46 @@
 cenario.
+get(fullyQualifiedTestMethod).
 setTestS
@@ -3632,88 +3632,8 @@
  %7D%0A%0A
-    private boolean isInTest() %7B%0A        return currentScenario != null;%0A    %7D%0A%0A
     
@@ -3687,17 +3687,16 @@
 tener %7B%0A
-%0A
         
@@ -3789,57 +3789,248 @@
     
-if (isInTest()) %7B%0A                currentScenario
+String fullyQualifiedTestMethod = failure.getDescription().getClassName() + %22.%22 + failure.getDescription().getMethodName();%0A            if (currentScenario.get(fullyQualifiedTestMethod) != null) currentScenario.get(fullyQualifiedTestMethod)
 .set
@@ -4064,30 +4064,16 @@
 ion());%0A
-            %7D%0A
         

fix for issue #21 - Thread safety issue in SpecRunner