package net.ranides.assira.functional;

import lombok.experimental.UtilityClass;

import java.io.Serializable;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * Function interfaces with arity other than 1
 *
 * Please note, that those interfaces extend Serializable.
 * You can use them if you want to store your lambda expressions in Serializable objects.
 *
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
@UtilityClass
public class Functions {

    /**
     * Function interface with arity = 0.
     * It is equivalent to Supplier, but uses Function naming convention.
     *
     * @param <R>
     */
    public interface Function0<R> extends Supplier<R>, Serializable {
        /**
         * Operation
         *
         * @return result
         */
        R apply();

        default R get() {
            return apply();
        }
    }

    /**
     * Function interface with arity = 1
     * It is equivalent to Function.
     *
     * @param <A> A
     * @param <R> R
     */
    public interface Function1<A, R> extends Function<A, R>, Serializable {
        @Override
        R apply(A a);
    }

    /**
     * Function interface with arity = 2
     * It is equivalent to BiFunction.
     *
     * @param <A> A
     * @param <B> B
     * @param <R> R
     */
    public interface Function2<A, B, R> extends BiFunction<A, B, R>, Serializable {
        @Override
        R apply(A a, B b);
    }

    /**
     * Function interface with arity = 3
     *
     * @param <A> A
     * @param <B> B
     * @param <C> C
     * @param <R> R
     */
    public interface Function3<A, B, C, R> extends Serializable {
        /**
         * Operation
         *
         * @param a a
         * @param b b
         * @param c c
         * @return result
         */
        R apply(A a, B b, C c);
    }

    /**
     * Function interface with arity = 4
     *
     * @param <A> A
     * @param <B> B
     * @param <C> C
     * @param <D> D
     * @param <R> R
     */
    public interface Function4<A, B, C, D, R> extends Serializable {
        /**
         * Operation
         *
         * @param a a
         * @param b b
         * @param c c
         * @param d d
         * @return result
         */
        R apply(A a, B b, C c, D d);
    }

    /**
     * It is Function which takes value of element and index of element inside collection.
     * It is used by functional looping methods.
     *
     * @author Ranides Atterwim {@literal <ranides@gmail.com>}
     */
    public interface EachFunction<U,R> extends Serializable {

        /**
         * Operation: takes value and it's position.
         *
         * @param index index
         * @param value value
         * @return result
         */
        R apply(int index, U value);

    }
}
