package jbehave;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import junit.framework.Assert;

import org.jbehave.core.model.Scenario;
import org.jbehave.core.model.Story;
import org.jbehave.core.parsers.RegexStoryParser;
import org.jbehave.core.parsers.StoryParser;
import org.junit.Test;

public class StoryReporter2AdapterTest {
	public static final String NEWLINE = System.getProperty("line.separator");
	public static final String STORY_TEXT = 
		"Scenario: some title" + NEWLINE +
		"Given some given" + NEWLINE +
		"When some when <value1>" + NEWLINE +
		"Then some then <value2>" + NEWLINE +
		NEWLINE +
		"Examples:" + NEWLINE +
		"value1|value2|" + NEWLINE +
		"a1|a2" + NEWLINE +
		"b1|b2" + NEWLINE;
	
	@Test
	public void testAdapter() {
		StoryParser storyParser = new RegexStoryParser();
		Story story = storyParser.parseStory(STORY_TEXT, "/storyPath");
		Scenario scenario = story.getScenarios().get(0);
		
		MethodSavingInvocationHandler handler = new MethodSavingInvocationHandler();
		StoryReporter2 mockStoryReporter2 = 
			(StoryReporter2) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { StoryReporter2.class}, handler);
		
		StoryReporter2Adapter adapter = new StoryReporter2Adapter(mockStoryReporter2);
		
		adapter.beforeStory(story, true);
		adapter.beforeScenario(scenario.getTitle());
		adapter.beforeExamples(scenario.getSteps(), scenario.getExamplesTable());
		adapter.example(scenario.getExamplesTable().getRow(0));
		adapter.example(scenario.getExamplesTable().getRow(1));
		adapter.afterExamples();
		adapter.afterScenario();
		adapter.afterStory(true);
		
		List<MethodInvocation> expecteds = Arrays.asList(
			new MethodInvocation("beforeStory", story, true),
			new MethodInvocation("beforeScenario", story, scenario),
			new MethodInvocation("beforeExamples", story, scenario, scenario.getExamplesTable()),
			new MethodInvocation("beforeExample", story, scenario, scenario.getExamplesTable().getRow(0)),
			new MethodInvocation("afterExample", story, scenario, scenario.getExamplesTable().getRow(0)),
			new MethodInvocation("beforeExample", story, scenario, scenario.getExamplesTable().getRow(1)),
			new MethodInvocation("afterExample", story, scenario, scenario.getExamplesTable().getRow(1)),
			new MethodInvocation("afterExamples", story, scenario, scenario.getExamplesTable()),
			new MethodInvocation("afterScenario", story, scenario),
			new MethodInvocation("afterStory", story, true)
		);
		
		Assert.assertEquals(expecteds, handler.getInvocations());
	}
	
	public static class MethodSavingInvocationHandler implements InvocationHandler {
		private List<MethodInvocation> invocations = new ArrayList<MethodInvocation>();

		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			invocations.add(new MethodInvocation(method.getName(), args));
			return null;
		}
		
		public List<MethodInvocation> getInvocations() {
			return invocations;
		}
	}
	
	public static class MethodInvocation {
		private String methodName;
		private Object[] args;
		public MethodInvocation(String methodName, Object... args) {
			super();
			this.methodName = methodName;
			this.args = args;
		}
		public String getMethodName() {
			return methodName;
		}
		public Object[] getArgs() {
			return args;
		}
		
		@Override
		public boolean equals(Object obj) {
			MethodInvocation other = (MethodInvocation) obj;
			return other.getMethodName().equals(methodName) && Arrays.equals(other.getArgs(), args);
		}
	}
}
