package net.ranides.assira.functional;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;

import net.ranides.assira.functional.Functions.*;
import net.ranides.assira.functional.Consumers.*;
import net.ranides.assira.functional.special.AnyFunction;
import net.ranides.assira.generic.Ref;
import net.ranides.assira.reflection.IClass;

/**
 * Compisitor allows you to build composite functions in fluent way.
 *
 * You can declare every argument type, mapping and function body in separate step.
 *
 * You can use this compositor with Decorator and create your own DSL which allows you to build
 * composite functions of some specific type or chain many composites.
 *
 * @param <C> C
 * @param <T> T
 * @param <R> R
 */
public abstract class Compositor<C, T, R> {

    /**
     * Starts new composition without any custom decorator.
     * Returned type is convoluted, because it is intended to use only in call chains.
     *
     * @param <Ta> common type of input
     * @param <Ra> type of result
     * @return new chain intented to use in expressions
     */
    public static <Ta, Ra> CompositorScope<AnyFunction<Ra>, Ta, Ra>.Compositor0<Object, Object, Object, Object, Ra> compose() {
        return compose(SelfDecorator.getInstance());
    }

    /**
     * Starts new composition without any custom decorator.
     * Returned type is convoluted, because it is intended to use only in call chains.
     * All generic types are inferred from decorator passed as argument.
     *
     * @param decorator decorator applied to composed function
     * @param <A1> type bound for intermediate 1 argument
     * @param <A2> type bound for intermediate 2 argument
     * @param <A3> type bound for intermediate 3 argument
     * @param <A4> type bound for intermediate 4 argument
     * @param <Ca> chain type
     * @param <Ta> common input type
     * @param <Ra> result type
     * @return new chain intented to use in expressions
     */
    public static <A1, A2, A3, A4, Ca, Ta, Ra> CompositorScope<Ca, Ta, Ra>.Compositor0<A1, A2, A3, A4, Ra> compose(
        CompositorDecorator<Ca, Ta, Ra> decorator
    ) {
        CompositorScope<Ca, Ta, Ra> scope = new CompositorScope<>(decorator);
        return scope.new Compositor0<A1, A2, A3, A4, Ra>();
    }

    /**
     *
     * Returns composed function, with injected decorator hooks and applied all transformations to arguments.
     * Function is returned as Consumer interface, so the result is discarded.
     *
     * @return supplier
     */
    protected abstract Object consumer();

    /**
     * Returns composed function, with injected decorator hooks and applied all transformations to arguments.
     *
     * @return function
     */
    protected abstract Object function();

    /**
     * Returns composed function, with injected decorator hooks and applied all transformations to arguments.
     * Function is returned as generic AnyFunction interface.
     *
     * @return function
     */
    protected abstract AnyFunction<R> generic();

    /**
     * Given standard notation {@code H(x) = F( G1(x), G2(x), G3(x), G4(x), ..., Gn )}, this method returns {@code H}.
     * That means: composed function taking one argument, which is cloned "n" times, transformed, and then passed to "F".
     *
     * @return function
     */
    protected abstract Function1<T, R> compact();

    /**
     * Returns expression bound to compositor.
     * That means: returned function won't call any decorator hooks nor transform arguments.
     *
     * @return function
     */
    protected abstract AnyFunction<R> source();

    /**
     * This function returns object generated by Decorator. There are two common idiom for use of this functions:
     * <p>
     * In "chain" DSL, this method returns parent object used to create this composition.
     * It can be used to chain further calls. For example: RuleCollector can insert every generated rule internally
     * and return itself after each {@code #build}.
     * <p>
     * Alternatively, "custom builder" DSL uses this method to return user-defined objects instead of plain function.
     * For example: RuleBuilder can return Rule instead of Function(a,b,...) or AnyFunction.
     * <p>
     * Note for class designers:
     * Under the hood, composed function is passed to {@link CompositorDecorator}. Your "parent" object has to
     * define custom decorator, if you want to collect, wrap, or process composed functions in any way.
     *
     * @return custom object defined by Decorator
     */
    protected abstract C build();

    /**
     * Returns number of arguments required by generated composite function.
     *
     * @return number
     */
    protected abstract int arguments();

    /**
     * State shared by compositors in chained call is stored in this enclosing class.
     *
     * @param <Cs> chain type
     * @param <Bs> superclass for argument types
     * @param <Rs> superclass for return type
     */
    public static class CompositorScope<Cs, Bs, Rs> {
        private final CompositorDecorator<Cs, Bs, Rs> decorator;

        protected CompositorScope(CompositorDecorator<Cs, Bs, Rs> decorator) {
            this.decorator = decorator;
        }

        /**
         * Compositor producing: Functiom0 aka Supplier.
         * Type bound are used stored for later use in compositors for higher arity.
         *
         * @param <S1> type bound for intermediate 1 argument
         * @param <S2> type bound for intermediate 2 argument
         * @param <S3> type bound for intermediate 3 argument
         * @param <S4> type bound for intermediate 4 argument
         * @param <R> type of result
         */
        @AllArgsConstructor(access = AccessLevel.PRIVATE)
        public class Compositor0<S1, S2, S3, S4, R extends Rs> extends Compositor<Cs, Bs, R> {
            private final Function0<R> terminal;

            protected Compositor0() {
                this(() -> { throw errorNoTerm(); });
            }

            /**
             * Declares additional function parameter A, which is converted to P and then passed "terminal"
             *
             * @param map1 map
             * @param <A1> A
             * @param <P1> P
             * @return compositor
             */
            public <A1 extends Bs, P1 extends S1> Compositor1<A1, P1, S2, S3, S4, R> withParam(Function1<A1, P1> map1) {
                return new Compositor1<>(a -> terminal.get(), map1::apply);
            }

            /**
             * Declares additional function parameter of provided type
             *
             * @param type type
             * @param <P> P
             * @return compositor
             */
            public <P extends Bs> Compositor1<P, P, S2, S3, S4, R> withParam(Class<P> type) {
                return new Compositor1<>(a -> terminal.get(), type::cast);
            }

            /**
             * Declares additional function parameter of provided type
             *
             * @param type type
             * @param <P> P
             * @return compositor
             */
            public <P extends Bs> Compositor1<P, P, S2, S3, S4, R> withParam(IClass<P> type) {
                return new Compositor1<>(a -> terminal.get(), type::cast);
            }

            /**
             * Passes to "terminal" additional value generated by provided supplier.
             *
             * @param map1 map
             * @param <A1> A
             * @param <P1> P
             * @return compositor
             */
            public <A1 extends Bs, P1 extends S1> Compositor1<A1, P1, S2, S3, S4, R> withValue(Function0<P1> map1) {
                return withParam(input -> map1.get());
            }

            /**
             * Declares additional function parameter A, which is converted to P and then passed "terminal"
             * If input values is null, then "other" value is passed instead.
             *
             * @param map1 map
             * @param other other
             * @param <A1> A
             * @param <P1> P
             * @return compositor
             */
            public <A1 extends Bs, P1 extends S1> Compositor1<A1, P1, S2, S3, S4, R> withNullable(
                Function1<A1, P1> map1,
                P1 other
            ) {
                return withParam(FunctionUtils.withDefault(map1, other));
            }

            /**
             * Declares additional function parameter A, which is converted to P and then passed "terminal"
             * If input values is empty, then "other" value is passed instead.
             *
             * @param map1 map
             * @param other other
             * @param <A1> A
             * @param <P1> P
             * @return compositor
             */
            public <A1 extends Bs, P1 extends S1> Compositor1<A1, P1, S2, S3, S4, R> withOptional(
                Function1<A1, Ref<P1>> map1,
                P1 other
            ) {
                return withParam(FunctionUtils.withOptional(map1, other));
            }

            /**
             * Binds "terminal" action which will be used as core implementation of generated composite.
             *
             * @param function function
             * @param <Q> Q
             * @return compositor
             */
            public <Q extends R> Compositor0<S1, S2, S3, S4, Q> bind(Function0<Q> function) {
                return new Compositor0<>(function);
            }

            @Override
            public Consumer0 consumer() {
                return function()::apply;
            }

            @Override
            protected AnyFunction<R> generic() {
                return AnyFunction.from(function());
            }

            @Override
            public Function1<Bs, R> compact() {
                return FunctionUtils.raw(FunctionUtils.asFunction(function()));
            }

            @Override
            public Function0<R> function() {
                return decorator.before0(this, decorator.after0(this, terminal));
            }

            @Override
            public Cs build() {
                Function0<R> ready = function();
                decorator.accept0(this, ready);
                return decorator.chain0(this, ready);
            }

            @Override
            protected AnyFunction<R> source() {
                return AnyFunction.from(terminal);
            }

            @Override
            protected int arguments() {
                return 0;
            }
        }

        /**
         * Compositor producing: Function1.
         * Type bound are used stored for later use in compositors for higher arity.
         *
         * @param <B1> type bound for 1 argument
         * @param <S1> type bound for intermediate 1 argument
         * @param <S2> type bound for intermediate 2 argument
         * @param <S3> type bound for intermediate 3 argument
         * @param <S4> type bound for intermediate 4 argument
         * @param <R> type of result
         */
        @AllArgsConstructor(access = AccessLevel.PRIVATE)
        public class Compositor1<B1 extends Bs, S1, S2, S3, S4, R extends Rs> extends Compositor<Cs, Bs, R> {
            private final Function1<? super S1, R> terminal;
            private final Function1<B1, S1> map1;

            /**
             * Declares additional function parameter A, which is converted to P and then passed "terminal"
             *
             * @param map2 map
             * @param <A2> A
             * @param <P2> P
             * @return compositor
             */
            public <A2 extends Bs, P2 extends S2> Compositor2<B1, A2, S1, P2, S3, S4, R> withParam(Function1<A2, P2> map2) {
                return new Compositor2<>((arg1, arg2) -> terminal.apply(arg1), map1, map2::apply);
            }

            /**
             * Declares additional function parameter of provided type
             *
             * @param type type
             * @param <P> P
             * @return compositor
             */
            public <P extends Bs> Compositor2<B1, P, S1, P, S3, S4, R> withParam(Class<P> type) {
                return new Compositor2<>((arg1, arg2) -> terminal.apply(arg1), map1, type::cast);
            }

            /**
             * Declares additional function parameter of provided type
             *
             * @param type type
             * @param <P> P
             * @return compositor
             */
            public <P extends Bs> Compositor2<B1, P, S1, P, S3, S4, R> withParam(IClass<P> type) {
                return new Compositor2<>((arg1, arg2) -> terminal.apply(arg1), map1, type::cast);
            }

            /**
             * Passes to "terminal" additional value generated by provided supplier.
             *
             * @param map2 map
             * @param <A2> A
             * @param <P2> P
             * @return compositor
             */
            public <A2 extends Bs, P2 extends S2> Compositor2<B1, A2, S1, P2, S3, S4, R> withValue(
                Function0<P2> map2
            ) {
                return withParam(input -> map2.get());
            }

            /**
             * Declares additional function parameter A, which is converted to P and then passed "terminal"
             * If input value is null, then "other" value is passed instead.
             *
             * @param map2 map
             * @param other other
             * @param <A2> A
             * @param <P2> P
             * @return compositor
             */
            public <A2 extends Bs, P2 extends S2> Compositor2<B1, A2, S1, P2, S3, S4, R> withNullable(
                Function1<A2, P2> map2,
                P2 other
            ) {
                return withParam(FunctionUtils.withDefault(map2, other));
            }

            /**
             * Declares additional function parameter A, which is converted to P and then passed "terminal"
             * If input value is empty, then "other" value is passed instead.
             *
             * @param map2 map
             * @param other other
             * @param <A2> A
             * @param <P2> P
             * @return compositor
             */
            public <A2 extends Bs, P2 extends S2> Compositor2<B1, A2, S1, P2, S3, S4, R> withOptional(
                Function1<A2, Ref<P2>> map2,
                P2 other
            ) {
                return withParam(FunctionUtils.withOptional(map2, other));
            }

            /**
             * Binds "terminal" action which will be used as core implementation of generated composite.
             *
             * @param function function
             * @param <Q> Q
             * @return compositor
             */
            public <Q extends R> Compositor1<B1, S1, S2, S3, S4, Q> bind(Function1<S1, Q> function) {
                return new Compositor1<>(function::apply, map1);
            }

            @Override
            public Consumer1<B1> consumer() {
                return function()::apply;
            }

            @Override
            protected AnyFunction<R> generic() {
                return AnyFunction.from(function());
            }

            @Override
            public Function1<Bs, R> compact() {
                return FunctionUtils.raw(function());
            }

            @Override
            public Function1<B1, R> function() {
                return decorator.before1(this, transform(decorator.after1(this, terminal)));
            }

            private Function1<B1, R> transform(Function1<? super S1, R> func) {
                return arg1 -> func.apply(map1.apply(arg1));
            }

            @Override
            public Cs build() {
                Function1<B1, R> ready = function();
                decorator.accept1(this, ready);
                return decorator.chain1(this, ready);
            }

            @Override
            protected AnyFunction<R> source() {
                return AnyFunction.from(terminal);
            }

            @Override
            protected int arguments() {
                return 1;
            }
        }

        /**
         * Compositor producing: Function2.
         * Type bound are used stored for later use in compositors for higher arity.
         *
         * @param <B1> type bound for 1 argument
         * @param <B2> type bound for 2 argument
         * @param <S1> type bound for intermediate 1 argument
         * @param <S2> type bound for intermediate 2 argument
         * @param <S3> type bound for intermediate 3 argument
         * @param <S4> type bound for intermediate 4 argument
         * @param <R> type of result
         */
        @AllArgsConstructor(access = AccessLevel.PRIVATE)
        public class Compositor2<B1 extends Bs, B2 extends Bs, S1, S2, S3, S4, R extends Rs> extends Compositor<Cs, Bs, R> {
            private final Function2<? super S1, ? super S2, R> terminal;
            private final Function1<B1, S1> map1;
            private final Function1<B2, S2> map2;

            /**
             * Declares additional function parameter A, which is converted to P and then passed "terminal"
             *
             * @param map3 map
             * @param <A3> A
             * @param <P3> P
             * @return compositor
             */
            public <A3 extends Bs, P3 extends S3> Compositor3<B1, B2, A3, S1, S2, P3, S4, R> withParam(
                Function1<A3, P3> map3
            ) {
                return new Compositor3<>((arg1, arg2, arg3) -> terminal.apply(arg1, arg2), map1, map2, map3::apply);
            }

            /**
             * Declares additional function parameter of provided type
             *
             * @param type type
             * @param <P> P
             * @return compositor
             */
            public <P extends Bs> Compositor3<B1, B2, P, S1, S2, P, S4, R> withParam(Class<P> type) {
                return new Compositor3<>((arg1, arg2, arg3) -> terminal.apply(arg1, arg2), map1, map2, type::cast);
            }

            /**
             * Declares additional function parameter of provided type
             *
             * @param type type
             * @param <P> P
             * @return compositor
             */
            public <P extends Bs> Compositor3<B1, B2, P, S1, S2, P, S4, R> withParam(IClass<P> type) {
                return new Compositor3<>((arg1, arg2, arg3) -> terminal.apply(arg1, arg2), map1, map2, type::cast);
            }

            /**
             * Passes to "terminal" additional value generated by provided supplier.
             *
             * @param map3 map
             * @param <A3> A
             * @param <P3> P
             * @return compositor
             */
            public <A3 extends Bs, P3 extends S3> Compositor3<B1, B2, A3, S1, S2, P3, S4, R> withValue(
                Function0<P3> map3
            ) {
                return withParam(input -> map3.get());
            }

            /**
             * Declares additional function parameter A, which is converted to P and then passed "terminal"
             * If input value is null, then "other" value is passed instead.
             *
             * @param map3 map
             * @param other other
             * @param <A3> A
             * @param <P3> P
             * @return compositor
             */
            public <A3 extends Bs, P3 extends S3> Compositor3<B1, B2, A3, S1, S2, P3, S4, R> withNullable(
                Function1<A3, P3> map3,
                P3 other
            ) {
                return withParam(FunctionUtils.withDefault(map3, other));
            }

            /**
             * Declares additional function parameter A, which is converted to P and then passed "terminal"
             * If input value is empty, then "other" value is passed instead.
             *
             * @param map3 map
             * @param other other
             * @param <A3> A
             * @param <P3> P
             * @return compositor
             */
            public <A3 extends Bs, P3 extends S3> Compositor3<B1, B2, A3, S1, S2, P3, S4, R> withOptional(
                Function1<A3, Ref<P3>> map3,
                P3 other
            ) {
                return withParam(FunctionUtils.withOptional(map3, other));
            }

            /**
             * Binds "terminal" action which will be used as core implementation of generated composite.
             *
             * @param function function
             * @param <Q> Q
             * @return compositor
             */
            public <Q extends R> Compositor2<B1, B2, S1, S2, S3, S4, Q> bind(Function2<S1, S2, Q> function) {
                return new Compositor2<>(function, map1, map2);
            }

            public Consumer2<B1, B2> consumer() {
                return function()::apply;
            }

            @Override
            protected AnyFunction<R> generic() {
                return AnyFunction.from(function());
            }

            @Override
            public Function1<Bs, R> compact() {
                Function2<Bs, Bs, R> ready = FunctionUtils.raw(function());
                return arg -> ready.apply(arg, arg);
            }

            @Override
            public Function2<B1, B2, R> function() {
                return decorator.before2(this, transform(decorator.after2(this, terminal)));
            }

            private Function2<B1, B2, R> transform(Function2<? super S1, ? super S2, R> func) {
                return (p1, p2) -> func.apply(map1.apply(p1), map2.apply(p2));
            }

            @Override
            protected AnyFunction<R> source() {
                return AnyFunction.from(terminal);
            }

            @Override
            public Cs build() {
                Function2<B1, B2, R> ready = function();
                decorator.accept2(this, ready);
                return decorator.chain2(this, ready);
            }

            @Override
            protected int arguments() {
                return 2;
            }
        }

        /**
         * Compositor producing: Function1.
         * Type bound are used stored for later use in compositors for higher arity.
         *
         * @param <B1> type bound for 1 argument
         * @param <B2> type bound for 2 argument
         * @param <B3> type bound for 3 argument
         * @param <S1> type bound for intermediate 1 argument
         * @param <S2> type bound for intermediate 2 argument
         * @param <S3> type bound for intermediate 3 argument
         * @param <S4> type bound for intermediate 4 argument
         * @param <R> type of result
         */
        @AllArgsConstructor(access = AccessLevel.PRIVATE)
        public class Compositor3<B1 extends Bs, B2 extends Bs, B3 extends Bs, S1, S2, S3, S4, R extends Rs> extends Compositor<Cs, Bs, R> {
            private final Function3<? super S1, ? super S2, ? super S3, R> terminal;
            private final Function1<B1, S1> map1;
            private final Function1<B2, S2> map2;
            private final Function1<B3, S3> map3;

            /**
             * Declares additional function parameter A, which is converted to P and then passed "terminal"
             *
             * @param map4 map
             * @param <A4> A
             * @param <P4> P
             * @return compositor
             */
            public <A4 extends Bs, P4 extends S4> Compositor4<B1, B2, B3, A4, S1, S2, S3, P4, R> withParam(
                Function1<A4, P4> map4
            ) {
                return new Compositor4<>((a, b, c, d) -> terminal.apply(a, b, c), map1, map2, map3, map4::apply);
            }

            /**
             * Declares additional function parameter of provided type
             *
             * @param type type
             * @param <P> P
             * @return compositor
             */
            public <P extends Bs> Compositor4<B1, B2, B3, P, S1, S2, S3, P, R> withParam(Class<P> type) {
                return new Compositor4<>((a, b, c, d) -> terminal.apply(a, b, c), map1, map2, map3, type::cast);
            }

            /**
             * Declares additional function parameter of provided type
             *
             * @param type type
             * @param <P> P
             * @return compositor
             */
            public <P extends Bs> Compositor4<B1, B2, B3, P, S1, S2, S3, P, R> withParam(IClass<P> type) {
                return new Compositor4<>((a, b, c, d) -> terminal.apply(a, b, c), map1, map2, map3, type::cast);
            }

            /**
             * Passes to "terminal" additional value generated by provided supplier.
             *
             * @param map4 map
             * @param <A4> A
             * @param <P4> P
             * @return compositor
             */
            public <A4 extends Bs, P4 extends S4> Compositor4<B1, B2, B3, A4, S1, S2, S3, P4, R> withValue(
                Function0<P4> map4
            ) {
                return withParam(input -> map4.get());
            }

            /**
             * Declares additional function parameter A, which is converted to P and then passed "terminal"
             * If input value is null, then "other" value is passed instead.
             *
             * @param map4 map
             * @param other other
             * @param <A4> A
             * @param <P4> P
             * @return compositor
             */
            public <A4 extends Bs, P4 extends S4> Compositor4<B1, B2, B3, A4, S1, S2, S3, P4, R> withNullable(
                Function1<A4, P4> map4,
                P4 other
            ) {
                return withParam(FunctionUtils.withDefault(map4, other));
            }

            /**
             * Declares additional function parameter A, which is converted to P and then passed "terminal"
             * If input value is empty, then "other" value is passed instead.
             *
             * @param map4 map
             * @param other other
             * @param <A4> A
             * @param <P4> P
             * @return compositor
             */
            public <A4 extends Bs, P4 extends S4> Compositor4<B1, B2, B3, A4, S1, S2, S3, P4, R> withOptional(
                Function1<A4, Ref<P4>> map4,
                P4 other
            ) {
                return withParam(FunctionUtils.withOptional(map4, other));
            }

            /**
             * Binds "terminal" action which will be used as core implementation of generated composite.
             *
             * @param function function
             * @param <Q> Q
             * @return compositor
             */
            public <Q extends R> Compositor3<B1, B2, B3, S1, S2, S3, S4, Q> bind(Function3<S1, S2, S3, Q> function) {
                return new Compositor3<>(function, map1, map2, map3);
            }

            public Consumer3<B1, B2, B3> consumer() {
                return function()::apply;
            }

            @Override
            protected AnyFunction<R> generic() {
                return AnyFunction.from(function());
            }

            @Override
            public Function3<B1, B2, B3, R> function() {
                return decorator.before3(this, transform(decorator.after3(this, terminal)));
            }

            private Function3<B1, B2, B3, R> transform(Function3<? super S1, ? super S2, ? super S3, R> func) {
                return (arg1, arg2, arg3) -> func.apply(map1.apply(arg1), map2.apply(arg2), map3.apply(arg3));
            }

            @Override
            protected AnyFunction<R> source() {
                return AnyFunction.from(terminal);
            }

            @Override
            public Function1<Bs, R> compact() {
                Function3<Bs, Bs, Bs, R> ready = FunctionUtils.raw(function());
                return arg -> ready.apply(arg, arg, arg);
            }

            @Override
            public Cs build() {
                Function3<B1, B2, B3, R> ready = function();
                decorator.accept3(this, ready);
                return decorator.chain3(this, ready);
            }

            @Override
            protected int arguments() {
                return 3;
            }
        }

        /**
         * Compositor producing: Function4.
         * Type bound are used stored for later use in compositors for higher arity.
         *
         * @param <B1> type bound for 1 argument
         * @param <B2> type bound for 2 argument
         * @param <B3> type bound for 3 argument
         * @param <B4> type bound for 4 argument
         * @param <S1> type bound for intermediate 1 argument
         * @param <S2> type bound for intermediate 2 argument
         * @param <S3> type bound for intermediate 3 argument
         * @param <S4> type bound for intermediate 4 argument
         * @param <R> type of result
         */
        @AllArgsConstructor(access = AccessLevel.PRIVATE)
        public class Compositor4
            <B1 extends Bs, B2 extends Bs, B3 extends Bs, B4 extends Bs, S1, S2, S3, S4, R extends Rs> extends Compositor<Cs, Bs, R> {
            private final Function4<? super S1, ? super S2, ? super S3, ? super S4, R> terminal;
            private final Function1<B1, S1> map1;
            private final Function1<B2, S2> map2;
            private final Function1<B3, S3> map3;
            private final Function1<B4, S4> map4;

            /**
             * Binds "terminal" action which will be used as core implementation of generated composite.
             *
             * @param function function
             * @param <Q> Q
             * @return compositor
             */
            public <Q extends R> Compositor4<B1, B2, B3, B4, S1, S2, S3, S4, Q> bind(
                Function4<S1, S2, S3, S4, Q> function
            ) {
                return new Compositor4<>(function, map1, map2, map3, map4);
            }

            public Consumer4<B1, B2, B3, B4> consumer() {
                return function()::apply;
            }

            @Override
            protected AnyFunction<R> generic() {
                return AnyFunction.from(function());
            }

            @Override
            public Function1<Bs, R> compact() {
                Function4<Bs, Bs, Bs, Bs, R> ready = FunctionUtils.raw(function());
                return arg -> ready.apply(arg, arg, arg, arg);
            }

            @Override
            public Function4<B1, B2, B3, B4, R> function() {
                return decorator.before4(this, transform(decorator.after4(this, terminal)));
            }

            private Function4<B1, B2, B3, B4, R> transform(
                Function4<? super S1, ? super S2, ? super S3, ? super S4, R> func
            ) {
                return (a, b, c, d) -> func.apply(map1.apply(a), map2.apply(b), map3.apply(c), map4.apply(d));
            }

            @Override
            protected AnyFunction<R> source() {
                return AnyFunction.from(terminal);
            }

            @Override
            public Cs build() {
                Function4<B1, B2, B3, B4, R> ready = function();
                decorator.accept4(this, ready);
                return decorator.chain4(this, ready);
            }

            @Override
            protected int arguments() {
                return 4;
            }
        }
    }

    /**
     * This object is supposed to be used inside "decorator" and should not be instantiated directly.
     *
     * Please read carefully description of decorator methods:
     * You can't call every method on this object at every decoration stage.
     *
     * @param <Tc> Tc
     * @param <Rc> Rc
     */
    public static class CompositorBuilder<Tc, Rc> {
        private final Compositor<?, Tc, Rc> compositor;
        private final AnyFunction<Rc> body;

        @SuppressWarnings("unchecked")
        protected CompositorBuilder(Compositor<?, ?, ?> compositor, AnyFunction<Rc> body) {
            this.compositor = Compositor.class.cast(compositor);
            this.body = body;
        }

        /**
         * Returns compositor which is used to build function.
         *
         * @return compositor
         */
        public Compositor<?, Tc, Rc> compositor() {
            return compositor;
        }

        /**
         * Look at documentation for {@link Compositor#compact()}
         *
         * @return function
         */
        public Function1<Tc, Rc> compact() {
            return compositor.compact();
        }

        /**
         * Look at documentation for {@link Compositor#generic()}
         *
         * @return function
         */
        public AnyFunction<Rc> generic() {
            return compositor.generic();
        }

        /**
         * Returns function which is wrapped at current decoration stage.
         *
         * @return function
         */
        public AnyFunction<Rc> body() {
            return body;
        }

        /**
         * Look at documentation for {@link Compositor#source()}
         *
         * @return function
         */
        public AnyFunction<Rc> source() {
            return compositor.source();
        }

        /**
         * Look at documentation for {@link Compositor#arguments()}
         *
         * @return number of arguments in decorated function
         */
        public int arguments() {
            return compositor.arguments();
        }
    }

    /**
     * This is decorator with methods which can be overridden to change behaviour of generated function.
     * Additionally, decorator is used to configure result returned as call chain
     *
     * @param <Cc> type of chain
     * @param <Tc> common type of input arguments
     * @param <Rc> type of result
     *
     * @since 2021-04-01
     */
    @RequiredArgsConstructor(access = AccessLevel.PROTECTED)
    public abstract static class CompositorDecorator<Cc, Tc, Rc> {
        private final Cc chain;

        protected CompositorDecorator() {
            this(null);
        }

        /**
         * This method is supposed to take generated function and store it "in some way" inside a chain.
         * Must return true, if this decorator supports chaining and can handle this generated function.
         *
         * This method should be implemented if decorator can handle function with any arity.
         * Because method receives information about generated function in form of "CompositorBuilder",
         * it can take function in few forms (compact, generic, body, source).
         *
         * This method is called if version for specific arity already returned "false"
         *
         * @param function function
         * @return bool
         */
        public boolean accept(CompositorBuilder<Tc, Rc> function) {
            return false;
        }

        /**
         * This method is supposed to take generated function and store it "in some way" inside a chain.
         * Must return true, if this decorator supports chaining and can handle this generated function.
         *
         * This method should be implemented if decorator can handle function with arity = 0.
         * It receives fully generated action, returned by {@link Compositor#function()}
         *
         * @param action action
         * @return bool
         */
        public boolean accept(Function0<Rc> action) {
            return false;
        }

        /**
         * This method is supposed to take generated function and store it "in some way" inside a chain.
         * Must return true, if this decorator supports chaining and can handle this generated function.
         *
         * This method should be implemented if decorator can handle function with arity = 0.
         * It receives fully generated action, returned by {@link Compositor#function()}
         *
         * @param action action
         * @return bool
         */
        public boolean accept(Function1<Tc, Rc> action) {
            return false;
        }

        /**
         * This method is supposed to take generated function and store it "in some way" inside a chain.
         * Must return true, if this decorator supports chaining and can handle this generated function.
         *
         * This method should be implemented if decorator can handle function with arity = 0.
         * It receives fully generated action, returned by {@link Compositor#function()}
         *
         * @param action action
         * @return bool
         */
        public boolean accept(Function2<Tc, Tc, Rc> action) {
            return false;
        }

        /**
         * This method is supposed to take generated function and store it "in some way" inside a chain.
         * Must return true, if this decorator supports chaining and can handle this generated function.
         *
         * This method should be implemented if decorator can handle function with arity = 0.
         * It receives fully generated action, returned by {@link Compositor#function()}
         *
         * @param action action
         * @return bool
         */
        public boolean accept(Function3<Tc, Tc, Tc, Rc> action) {
            return false;
        }

        /**
         * This method is supposed to take generated function and store it "in some way" inside a chain.
         * Must return true, if this decorator supports chaining and can handle this generated function.
         *
         * This method should be implemented if decorator can handle function with arity = 0.
         * It receives fully generated action, returned by {@link Compositor#function()}
         *
         * @param action action
         * @return bool
         */
        public boolean accept(Function4<Tc, Tc, Tc, Tc, Rc> action) {
            return false;
        }

        /**
         * This method is expected to generate code executed before argument conversions.
         * Inside this code, you must call "terminal" on your.
         * This method can return null, if it does not introduce any changes.
         * This method should be implemented if you support any arity.
         *
         * Notes about execution:
         *  - You receive non-converted input
         *  - You should call "function.body.apply" to run conversions and then "terminal"
         *  - You can postprocess result
         *
         * Notes about CompositorBuilder:
         *  - You MUST NOT call "compact", "generic", "consumer"
         *  - You could call "source", but you have no idea what arguments pass to it
         *
         * This method is preffered, if you want to stop execution at all (including conversions)
         * because of some internal state, for example logger level, or broken connection.
         *
         * @param function function
         * @return function
         */
        public AnyFunction<Rc> before(CompositorBuilder<Tc, Rc> function) {
            return null;
        }

        /**
         * This method is expected to generate code executed before argument conversions.
         * Inside this code, you must call "terminal" on your.
         * This method can return null, if it does not introduce any changes.
         * This method should be implemented if you support arity = 0.
         *
         * Notes about execution:
         *  - You receive non-converted input
         *  - You should call "function.body.apply" to run conversions and then "terminal"
         *  - You can postprocess result
         *
         * This method is preffered, if you want to stop execution at all (including conversions)
         * because of some internal state, for example logger level, or broken connection.
         *
         * @param action action
         * @return action
         */
        public Function0<Rc> before(Function0<Rc> action) {
            return null;
        }

        /**
         * This method is expected to generate code executed before argument conversions.
         * Inside this code, you must call "terminal" on your.
         * This method can return null, if it does not introduce any changes.
         * This method should be implemented if you support arity = 1.
         *
         * Notes about execution:
         *  - You receive non-converted input
         *  - You should call "function.body.apply" to run conversions and then "terminal"
         *  - You can postprocess result
         *
         * This method is preffered, if you want to stop execution at all (including conversions)
         * because of some internal state, for example logger level, or broken connection.
         *
         * @param action action
         * @return action
         */
        public Function1<Object, Rc> before(Function1<Object, Rc> action) {
            return null;
        }

        /**
         * This method is expected to generate code executed before argument conversions.
         * Inside this code, you must call "terminal" on your.
         * This method can return null, if it does not introduce any changes.
         * This method should be implemented if you support arity = 2.
         *
         * Notes about execution:
         *  - You receive non-converted input
         *  - You should call "function.body.apply" to run conversions and then "terminal"
         *  - You can postprocess result
         *
         * This method is preffered, if you want to stop execution at all (including conversions)
         * because of some internal state, for example logger level, or broken connection.
         *
         * @param action action
         * @return action
         */
        public Function2<Object, Object, Rc> before(Function2<Object, Object, Rc> action) {
            return null;
        }

        /**
         * This method is expected to generate code executed before argument conversions.
         * Inside this code, you must call "terminal" on your.
         * This method can return null, if it does not introduce any changes.
         * This method should be implemented if you support arity = 3.
         *
         * Notes about execution:
         *  - You receive non-converted input
         *  - You should call "function.body.apply" to run conversions and then "terminal"
         *  - You can postprocess result
         *
         * This method is preffered, if you want to stop execution at all (including conversions)
         * because of some internal state, for example logger level, or broken connection.
         *
         * @param action action
         * @return action
         */
        public Function3<Object, Object, Object, Rc> before(Function3<Object, Object, Object, Rc> action) {
            return null;
        }

        /**
         * This method is expected to generate code executed before argument conversions.
         * Inside this code, you must call "terminal" on your.
         * This method can return null, if it does not introduce any changes.
         * This method should be implemented if you support arity = 4.
         *
         * Notes about execution:
         *  - You receive non-converted input
         *  - You should call "function.body.apply" to run conversions and then "terminal"
         *  - You can postprocess result
         *
         * This method is preffered, if you want to stop execution at all (including conversions)
         * because of some internal state, for example logger level, or broken connection.
         *
         * @param action action
         * @return action
         */
        public Function4<Object, Object, Object, Object, Rc> before(Function4<Object, Object, Object, Object, Rc> action) {
            return null;
        }

        /**
         * This method is expected to generate code executed after argument conversions.
         * Inside this code, you must call "terminal" on your.
         * This method can return null, if it does not introduce any changes.
         * This method should be implemented if you support any arity.
         *
         * Notes about execution:
         *  - You receive converted input
         *  - You should call "function.body.apply" to run conversions and then "terminal"
         *  - You can postprocess result
         *
         * Notes about CompositorBuilder:
         *  - You MUST NOT call "compact", "generic", "consumer"
         *  - You could call "source", but you have no idea what arguments pass to it
         *
         * This method is preffered, if you always execute "terminal" and process result
         *
         * @param function function
         * @return function
         */
        public AnyFunction<Rc> after(CompositorBuilder<Tc, Rc> function) {
            return null;
        }

        /**
         * This method is expected to generate code executed after argument conversions.
         * Inside this code, you must call "terminal" on your.
         * This method can return null, if it does not introduce any changes.
         * This method should be implemented if you support any arity.
         *
         * Notes about execution:
         *  - You receive converted input
         *  - You should call "function.body.apply" to run conversions and then "terminal"
         *  - You can postprocess result
         *
         * This method is preffered, if you always execute "terminal" and process result
         *
         * @param action action
         * @return action
         */
        public Function0<Rc> after(Function0<Rc> action) {
            return null;
        }

        /**
         * This method is expected to generate code executed after argument conversions.
         * Inside this code, you must call "terminal" on your.
         * This method can return null, if it does not introduce any changes.
         * This method should be implemented if you support any arity.
         *
         * Notes about execution:
         *  - You receive converted input
         *  - You should call "function.body.apply" to run conversions and then "terminal"
         *  - You can postprocess result
         *
         * This method is preffered, if you always execute "terminal" and process result
         *
         * @param action action
         * @return action
         */
        public Function1<Object, Rc> after(Function1<Object, Rc> action) {
            return null;
        }

        /**
         * This method is expected to generate code executed after argument conversions.
         * Inside this code, you must call "terminal" on your.
         * This method can return null, if it does not introduce any changes.
         * This method should be implemented if you support any arity.
         *
         * Notes about execution:
         *  - You receive converted input
         *  - You should call "function.body.apply" to run conversions and then "terminal"
         *  - You can postprocess result
         *
         * This method is preffered, if you always execute "terminal" and process result
         *
         * @param action action
         * @return action
         */
        public Function2<Object, Object, Rc> after(Function2<Object, Object, Rc> action) {
            return null;
        }

        /**
         * This method is expected to generate code executed after argument conversions.
         * Inside this code, you must call "terminal" on your.
         * This method can return null, if it does not introduce any changes.
         * This method should be implemented if you support any arity.
         *
         * Notes about execution:
         *  - You receive converted input
         *  - You should call "function.body.apply" to run conversions and then "terminal"
         *  - You can postprocess result
         *
         * This method is preffered, if you always execute "terminal" and process result
         *
         * @param action action
         * @return action
         */
        public Function3<Object, Object, Object, Rc> after(Function3<Object, Object, Object, Rc> action) {
            return null;
        }

        /**
         * This method is expected to generate code executed after argument conversions.
         * Inside this code, you must call "terminal" on your.
         * This method can return null, if it does not introduce any changes.
         * This method should be implemented if you support any arity.
         *
         * Notes about execution:
         *  - You receive converted input
         *  - You should call "function.body.apply" to run conversions and then "terminal"
         *  - You can postprocess result
         *
         * This method is preffered, if you always execute "terminal" and process result
         *
         * @param action action
         * @return action
         */
        public Function4<Object, Object, Object, Object, Rc> after(Function4<Object, Object, Object, Object, Rc> action) {
            return null;
        }

        /**
         * If you want to support {@link Compositor#build()} then you must implement this method.
         *
         * This method is designed for custom chaining:
         * Your decorator should implement "accept" and store every composite inside "Cc" chain,
         * "accept" is called on every bind. This method should simply return "Cc" chain.
         *
         * Your method openinng chain should create new Decorator with new "Cc" on every open.
         *
         * @return chain
         */
        public Cc chain() {
            return Ref.ofNullable(chain).orElseThrow(Compositor::errorNoChain);
        }

        /**
         * If you want to support {@link Compositor#build()} then you must implement this method.
         *
         * If you want to implement custom result, then:
         *  - your decorator should implement "accept" and just return "true"
         *  - this method should wrap provided function into new "Cc" object
         *
         * This method is supposed to support every arity.
         *  - it is preffered to use fully decorated "function.generic"
         *  - you can use "function.compact", "function.body" or others
         *
         * @param function function
         * @return custom result
         */
        public Cc chain(CompositorBuilder<Tc, Rc> function) {
            return null;
        }

        /**
         * If you want to support {@link Compositor#build()} then you must implement this method.
         *
         * If you want to implement custom result, then:
         *  - your decorator should implement "accept" and just return "true"
         *  - this method should wrap provided function into new "Cc" object
         *
         * This method is supposed to support arity = 0.
         * It receives fully decorated function.
         *
         * @param action action
         * @return custom result
         */
        public Cc chain(Function0<Rc> action) {
            return null;
        }

        /**
         * If you want to support {@link Compositor#build()} then you must implement this method.
         *
         * If you want to implement custom result, then:
         *  - your decorator should implement "accept" and just return "true"
         *  - this method should wrap provided function into new "Cc" object
         *
         * This method is supposed to support arity = 1.
         * It receives fully decorated function.
         *
         * @param action action
         * @return custom result
         */
        public Cc chain(Function1<Tc, Rc> action) {
            return null;
        }

        /**
         * If you want to support {@link Compositor#build()} then you must implement this method.
         *
         * If you want to implement custom result, then:
         *  - your decorator should implement "accept" and just return "true"
         *  - this method should wrap provided function into new "Cc" object
         *
         * This method is supposed to support arity = 2.
         * It receives fully decorated function.
         *
         * @param action action
         * @return custom result
         */
        public Cc chain(Function2<Tc, Tc, Rc> action) {
            return null;
        }

        /**
         * If you want to support {@link Compositor#build()} then you must implement this method.
         *
         * If you want to implement custom result, then:
         *  - your decorator should implement "accept" and just return "true"
         *  - this method should wrap provided function into new "Cc" object
         *
         * This method is supposed to support arity = 3.
         * It receives fully decorated function.
         *
         * @param action action
         * @return custom result
         */
        public Cc chain(Function3<Tc, Tc, Tc, Rc> action) {
            return null;
        }

        /**
         * If you want to support {@link Compositor#build()} then you must implement this method.
         *
         * If you want to implement custom result, then:
         *  - your decorator should implement "accept" and just return "true"
         *  - this method should wrap provided function into new "Cc" object
         *
         * This method is supposed to support arity = 4.
         * It receives fully decorated function.
         *
         * @param action action
         * @return custom result
         */
        public Cc chain(Function4<Tc, Tc, Tc, Tc, Rc> action) {
            return null;
        }

        private void accept0(Compositor<?, ?, ?> compositor, Function0<?> action) {
            Function0<Rc> input = FunctionUtils.raw(action);
            if (!accept(input) && !accept(new CompositorBuilder<>(compositor, AnyFunction.from(input)))) {
                throw errorNoAccept();
            }
        }

        private void accept1(Compositor<?, ?, ?> compositor, Function1<?, ?> action) {
            Function1<Tc, Rc> input = FunctionUtils.raw(action);
            if (!accept(input) && !accept(new CompositorBuilder<>(compositor, AnyFunction.from(input)))) {
                throw errorNoAccept();
            }
        }

        private void accept2(Compositor<?, ?, ?> compositor, Function2<?, ?, ?> action) {
            Function2<Tc, Tc, Rc> input = FunctionUtils.raw(action);
            if (!accept(input) && !accept(new CompositorBuilder<>(compositor, AnyFunction.from(input)))) {
                throw errorNoAccept();
            }
        }

        private void accept3(Compositor<?, ?, ?> compositor, Function3<?, ?, ?, ?> action) {
            Function3<Tc, Tc, Tc, Rc> input = FunctionUtils.raw(action);
            if (!accept(input) && !accept(new CompositorBuilder<>(compositor, AnyFunction.from(input)))) {
                throw errorNoAccept();
            }
        }

        private void accept4(Compositor<?, ?, ?> compositor, Function4<?, ?, ?, ?, ?> action) {
            Function4<Tc, Tc, Tc, Tc, Rc> input = FunctionUtils.raw(action);
            if (!accept(input) && !accept(new CompositorBuilder<>(compositor, AnyFunction.from(input)))) {
                throw errorNoAccept();
            }
        }

        private <R> Function0<R> before0(Compositor<?, ?, ?> compositor, Function0<?> action) {
            Function0<Rc> input = FunctionUtils.raw(action);
            Function0<Rc> output = firstNonNull(
                () -> before(input),
                () -> AnyFunction.toFunction0(before(new CompositorBuilder<>(compositor, AnyFunction
                    .from(input)))),
                () -> input
            );
            return FunctionUtils.raw(output);
        }

        private <P1, R> Function1<P1, R> before1(Compositor<?, ?, ?> compositor, Function1<?, ?> action) {
            Function1<Object, Rc> input = FunctionUtils.raw(action);
            Function1<Object, Rc> output = firstNonNull(
                () -> before(input),
                () -> AnyFunction.toFunction1(before(new CompositorBuilder<>(compositor, AnyFunction
                    .from(input)))),
                () -> input
            );
            return FunctionUtils.raw(output);
        }

        private <P1, P2, R> Function2<P1, P2, R> before2(Compositor<?, ?, ?> compositor, Function2<?, ?, ?> action) {
            Function2<Object, Object, Rc> input = FunctionUtils.raw(action);
            Function2<Object, Object, Rc> output = firstNonNull(
                () -> before(input),
                () -> AnyFunction.toFunction2(before(new CompositorBuilder<>(compositor, AnyFunction
                    .from(input)))),
                () -> input
            );
            return FunctionUtils.raw(output);
        }

        private <P1, P2, P3, R> Function3<P1, P2, P3, R> before3(Compositor<?, ?, ?> compositor,
            Function3<?, ?, ?, ?> action) {
            Function3<Object, Object, Object, Rc> input = FunctionUtils.raw(action);
            Function3<Object, Object, Object, Rc> output = firstNonNull(
                () -> before(input),
                () -> AnyFunction.toFunction3(before(new CompositorBuilder<>(compositor, AnyFunction
                    .from(input)))),
                () -> input
            );
            return FunctionUtils.raw(output);
        }

        private <P1, P2, P3, P4, R> Function4<P1, P2, P3, P4, R> before4(Compositor<?, ?, ?> compositor, Function4<?, ?, ?, ?, ?> action) {
            Function4<Object, Object, Object, Object, Rc> input = FunctionUtils.raw(action);
            Function4<Object, Object, Object, Object, Rc> output = firstNonNull(
                () -> before(input),
                () -> AnyFunction.toFunction4(before(new CompositorBuilder<>(compositor, AnyFunction
                    .from(input)))),
                () -> input
            );
            return FunctionUtils.raw(output);
        }

        private <R> Function0<R> after0(Compositor<?, ?, ?> compositor, Function0<?> action) {
            Function0<Rc> input = FunctionUtils.raw(action);
            Function0<Rc> output = firstNonNull(
                () -> after(input),
                () -> AnyFunction.toFunction0(after(new CompositorBuilder<>(compositor, AnyFunction.from(input)))),
                () -> input
            );
            return FunctionUtils.raw(output);
        }

        private <P1, R> Function1<P1, R> after1(Compositor<?, ?, ?> compositor, Function1<?, ?> action) {
            Function1<Object, Rc> input = FunctionUtils.raw(action);
            Function1<Object, Rc> output = firstNonNull(
                () -> after(input),
                () -> AnyFunction.toFunction1(after(new CompositorBuilder<>(compositor, AnyFunction.from(input)))),
                () -> input
            );
            return FunctionUtils.raw(output);
        }

        private <P1, P2, R> Function2<P1, P2, R> after2(Compositor<?, ?, ?> compositor, Function2<?, ?, ?> action) {
            Function2<Object, Object, Rc> input = FunctionUtils.raw(action);
            Function2<Object, Object, Rc> output = firstNonNull(
                () -> after(input),
                () -> AnyFunction.toFunction2(after(new CompositorBuilder<>(compositor, AnyFunction.from(input)))),
                () -> input
            );
            return FunctionUtils.raw(output);
        }

        private <P1, P2, P3, R> Function3<P1, P2, P3, R> after3(Compositor<?, ?, ?> compositor, Function3<?, ?, ?, ?> action) {
            Function3<Object, Object, Object, Rc> input = FunctionUtils.raw(action);
            Function3<Object, Object, Object, Rc> output = firstNonNull(
                () -> after(input),
                () -> AnyFunction.toFunction3(after(new CompositorBuilder<>(compositor, AnyFunction.from(input)))),
                () -> input
            );
            return FunctionUtils.raw(output);
        }

        private <P1, P2, P3, P4, R> Function4<P1, P2, P3, P4, R> after4(Compositor<?, ?, ?> compositor, Function4<?, ?, ?, ?, ?> action) {
            Function4<Object, Object, Object, Object, Rc> input = FunctionUtils.raw(action);
            Function4<Object, Object, Object, Object, Rc> output = firstNonNull(
                () -> after(input),
                () -> AnyFunction.toFunction4(after(new CompositorBuilder<>(compositor, AnyFunction.from(input)))),
                () -> input
            );
            return FunctionUtils.raw(output);
        }

        private Cc chain0(Compositor<?, ?, ?> compositor, Function0<?> action) {
            Function0<Rc> input = FunctionUtils.raw(action);
            return firstNonNull(
                () -> chain(input),
                () -> chain(new CompositorBuilder<>(compositor, AnyFunction.from(input))),
                this::chain
            );
        }

        private Cc chain1(Compositor<?, ?, ?> compositor, Function1<?, ?> action) {
            Function1<Tc, Rc> input = FunctionUtils.raw(action);
            return firstNonNull(
                () -> chain(input),
                () -> chain(new CompositorBuilder<>(compositor, AnyFunction.from(input))),
                this::chain
            );
        }

        private Cc chain2(Compositor<?, ?, ?> compositor, Function2<?, ?, ?> action) {
            Function2<Tc, Tc, Rc> input = FunctionUtils.raw(action);
            return firstNonNull(
                () -> chain(input),
                () -> chain(new CompositorBuilder<>(compositor, AnyFunction.from(input))),
                this::chain
            );
        }

        private Cc chain3(Compositor<?, ?, ?> compositor, Function3<?, ?, ?, ?> action) {
            Function3<Tc, Tc, Tc, Rc> input = FunctionUtils.raw(action);
            return firstNonNull(
                () -> chain(input),
                () -> chain(new CompositorBuilder<>(compositor, AnyFunction.from(input))),
                this::chain
            );
        }

        private Cc chain4(Compositor<?, ?, ?> compositor, Function4<?, ?, ?, ?, ?> action) {
            Function4<Tc, Tc, Tc, Tc, Rc> input = FunctionUtils.raw(action);
            return firstNonNull(
                () -> chain(input),
                () -> chain(new CompositorBuilder<>(compositor, AnyFunction.from(input))),
                this::chain
            );
        }
    }

    /**
     * Default decorator implementation, which returns generic function as a chain.
     *
     * @param <Tc> type of arguments
     * @param <Rc> type of results
     *
     * @since 2021-04-01
     */
    public static class SelfDecorator<Tc, Rc> extends CompositorDecorator<AnyFunction<Rc>, Tc, Rc> {
        @SuppressWarnings("rawtypes")
        private static final SelfDecorator INSTANCE = new SelfDecorator();

        @SuppressWarnings("unchecked")
        private static <C, T, R> CompositorDecorator<C, T, R> getInstance() {
            return INSTANCE;
        }

        @Override
        public boolean accept(CompositorBuilder<Tc, Rc> function) {
            return true;
        }

        @Override
        public AnyFunction<Rc> chain(CompositorBuilder<Tc, Rc> function) {
            return function.generic();
        }
    }

    private static <V> V firstNonNull(Function0<V> expr1, Function0<V> expr2, Function0<V> expr3) {
        V arg1 = expr1.get();
        if (arg1 != null) {
            return arg1;
        }
        V arg2 = expr2.get();
        if (arg2 != null) {
            return arg2;
        }
        V arg3 = expr3.get();
        if (arg3 != null) {
            return arg3;
        }
        throw errorNoTerm();
    }

    private static final String NO_CHAIN = "Method #end is called, but #chain is undefined.";

    private static final String NO_ACCEPT = "Method #end is called, but #accept is undefined.";

    private static final String NO_TERMINAL = "No binding to terminate function.";

    private static RuntimeException errorNoChain() {
        return new UnsupportedOperationException(NO_CHAIN);
    }

    private static RuntimeException errorNoAccept() {
        return new UnsupportedOperationException(NO_ACCEPT);
    }

    private static RuntimeException errorNoTerm() {
        return new IllegalStateException(NO_TERMINAL);
    }
}