/**
 * Interface to help create {@link java.util.function.BiFunction PostProcessor}
 * for the DSL starter with same value.
 * 
 */
@FunctionalInterface
public interface PostProcessor
		extends java.util.function.BiFunction<org.hamcrest.Matcher<?>, java.lang.Object, org.hamcrest.Matcher<?>> {
	/**
	 * Chaining interface to support the filter once the target matcher class has
	 * been accepted.
	 *
	 * @param <O> Target type of the matcher
	 * @param <T> Matcher
	 */
	@FunctionalInterface
	interface PostProcessor1<O, T extends org.hamcrest.Matcher<O>> {

		/**
		 * Defines the accepting criteria.
		 * 
		 * @param accept the BiPredicate to be used to verify the current matcher and
		 *               current other.
		 * @return the next stop of the PostProcessor.
		 */
		PostProcessor2<O, T> when(java.util.function.BiPredicate<T, O> accept);
	}

	/**
	 * Chaining interface to support the action to be done for this acceptance
	 * criteria.
	 *
	 * @param <O> Target type of the matcher
	 * @param <T> Matcher
	 */
	@FunctionalInterface
	interface PostProcessor2<O, T extends org.hamcrest.Matcher<O>> {

		/**
		 * Defines the transformation to be applied.
		 * 
		 * @param postProcessor the BiFunction to be applied.
		 * @return A chainable PostProcessor.
		 */
		PostProcessor3<O, T> then(java.util.function.BiFunction<T, O, T> postProcessor);

		static <O, T extends org.hamcrest.Matcher<O>> PostProcessor2<O, T> _newPostProcessor2Simple(
				Class<T> targetMatcher, java.util.function.BiPredicate<T, O> accept) {
			return p -> new PostProcessor3<O, T>() {

				@Override
				public org.hamcrest.Matcher<?> apply(org.hamcrest.Matcher<?> m, Object o) {
					return java.util.Optional.of(m).filter(targetMatcher::isInstance).map(targetMatcher::cast)
							.filter(x -> accept.test(x, (O) o)).<org.hamcrest.Matcher<?>>map(x -> p.apply(x, (O) o))
							.orElse(m);
				}

				@Override
				public PostProcessor2<O, T> when(java.util.function.BiPredicate<T, O> accept) {
					return PostProcessor2._newPostProcessor2Chained(targetMatcher, accept, this);
				}
			};
		}

		static <O, T extends org.hamcrest.Matcher<O>> PostProcessor2<O, T> _newPostProcessor2Chained(
				Class<T> targetMatcher, java.util.function.BiPredicate<T, O> accept,
				java.util.function.BiFunction<org.hamcrest.Matcher<?>, java.lang.Object, org.hamcrest.Matcher<?>> before) {
			return p -> new PostProcessor3<O, T>() {

				@Override
				public org.hamcrest.Matcher<?> apply(org.hamcrest.Matcher<?> m, Object o) {
					return PostProcessor.of(targetMatcher).when(accept).then(p).apply(before.apply(m, o), o);
				}

				@Override
				public PostProcessor2<O, T> when(java.util.function.BiPredicate<T, O> accept) {
					return PostProcessor2._newPostProcessor2Chained(targetMatcher, accept, this);
				}
			};
		}

	}

	/**
	 * Chainable PostProcessor.
	 *
	 * @param <O> Target type of the matcher of the last post processor
	 * @param <T> Matcher of the last post processor
	 */
	interface PostProcessor3<O, T extends org.hamcrest.Matcher<O>> extends PostProcessor, PostProcessor1<O, T> {

		/**
		 * add a new postprocessor, for another matcher class.
		 * 
		 * @param <O2>                   Target type of the matcher
		 * @param <T2>                   Matcher
		 * @param targetMatcherInterface class of the matcher
		 * @return next step of the builder
		 */
		default <O2, T2 extends org.hamcrest.Matcher<O2>> PostProcessor1<O2, T2> of(Class<T2> targetMatcherInterface) {
			return a -> PostProcessor2._newPostProcessor2Chained(targetMatcherInterface, a, PostProcessor3.this);
		}

		/**
		 * Coerce this chainable PostProcess to a not chainable PostProcessor.
		 * 
		 * @return the post Processor.
		 */
		default java.util.function.BiFunction<org.hamcrest.Matcher<?>, java.lang.Object, org.hamcrest.Matcher<?>> build() {
			return this;
		}

	}

	/**
	 * Creation of a chainable post processor.
	 * 
	 * @param <O>                    Target type of the matcher
	 * @param <T>                    Matcher
	 * @param targetMatcherInterface class of the matcher
	 * @return next step of the builder
	 */
	static <O, T extends org.hamcrest.Matcher<O>> PostProcessor1<O, T> of(Class<T> targetMatcherInterface) {
		return a -> PostProcessor2._newPostProcessor2Simple(targetMatcherInterface, a);
	}

}