package org.codefx.demo.junit5.parameterized;

import org.codefx.demo.junit5.parameterized.CustomArgumentConverterTest.Point;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.aggregator.AggregateWith;
import org.junit.jupiter.params.aggregator.ArgumentsAccessor;
import org.junit.jupiter.params.aggregator.ArgumentsAggregationException;
import org.junit.jupiter.params.aggregator.ArgumentsAggregator;
import org.junit.jupiter.params.converter.ArgumentConversionException;
import org.junit.jupiter.params.converter.ArgumentConverter;
import org.junit.jupiter.params.provider.CsvSource;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

class ArgumentAggregatorTest {

	@ParameterizedTest
	@CsvSource({ "0, 0, 0", "1, 0, 1", "1, 1, 0", "1.414, 1, 1", "2.236, 2, 1" })
	void testPointNorm(double norm, ArgumentsAccessor arguments) {
		Point point = Point.from(arguments.getDouble(1), arguments.getDouble(2));
		assertEquals(norm, point.norm(), 0.01);
	}

	@ParameterizedTest
	@CsvSource({ "0, 0, 0", "1, 0, 1", "1, 1, 0", "1.414, 1, 1", "2.236, 2, 1" })
	void testPointNorm(double norm, @AggregatePoint Point point) {
		assertEquals(norm, point.norm(), 0.01);
	}

	@Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER })
	@Retention(RetentionPolicy.RUNTIME)
	@AggregateWith(PointAggregator.class)
	@interface AggregatePoint { }

	static class PointAggregator implements ArgumentsAggregator {

		@Override
		public Object aggregateArguments(
				ArgumentsAccessor arguments, ParameterContext context) throws ArgumentsAggregationException {
			return Point.from(arguments.getDouble(1), arguments.getDouble(2));
		}

	}

	@ParameterizedTest
	@CsvSource({ "0" })
	void testNoFactoryAccess_errors(ArgumentsAccessor arguments) {
		// would be nice to pass NoFactoryConverter...
		NoFactory nope = arguments.get(0, NoFactory.class);
		assertNotNull(nope);
	}

	static class NoFactoryConverter implements ArgumentConverter {

		@Override
		public Object convert(Object source, ParameterContext context) throws ArgumentConversionException {
			if (source instanceof String)
				return new NoFactory(Integer.parseInt((String) source));
			throw new ArgumentConversionException(source + " could not be converted.");
		}
	}

	static class NoFactory {

		NoFactory(int __) { }

	}

}

@@ -119,16 +119,59 @@
 .Point;%0A
+import org.junit.jupiter.api.TestReporter;%0A
 import o
@@ -219,16 +219,16 @@
 ontext;%0A
-
 import o
@@ -875,16 +875,74 @@
 arget;%0A%0A
+import static org.assertj.core.api.Assertions.assertThat;%0A
 import s
@@ -1328,16 +1328,16 @@
 le(2));%0A
-
 %09%09assert
@@ -1370,24 +1370,385 @@
  0.01);%0A%09%7D%0A%0A
+%09@ParameterizedTest%0A%09@CsvSource(%7B %220, 0, 0%22, %221, 0, 1%22, %221, 1, 0%22, %221.414, 1, 1%22, %222.236, 2, 1%22 %7D)%0A%09// without ArgumentsAccessor in there, this leads to a ParameterResolutionException%0A%09void testEatingArguments(double norm, ArgumentsAccessor arguments, TestReporter reporter) %7B%0A%09%09reporter.publishEntry(%22norm%22, norm + %22%22);%0A%09%09assertThat(norm).isNotNegative();%0A%09%7D%0A%0A
 %09@Parameteri

Add demo for eating arguments with accessor