/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/everything
 */

package net.ranides.assira.generic;


import lombok.experimental.UtilityClass;

import java.util.function.Supplier;

@UtilityClass
public class LazySuppliers {

    /**
     * Initialization is unsynchronized. There is no guarantee about thread-safety.
     * @param <T> T
     * @param supplier supplier
     * @return supplier
     */
    public static <T> LazySupplier<T> unique(Supplier<T> supplier) {
        LazyCaller<T> cmp = LazyCallers.unique();
        return () -> cmp.computeIfAbsent(supplier);
    }

    /**
     * Initialization is done once per thread. Every thread will have different thread-local object.
     * @param <T> T
     * @param supplier supplier
     * @return  supplier
     */
    public static <T> LazySupplier<T> local(Supplier<T> supplier) {
        return ThreadLocal.withInitial(supplier)::get;
    }

    /**
     * Initialization is synchronized using correctly implemented "double checked locking".
     * Requires JDK 1.5+ because uses new "volatile semantics".
     * @param <T> T
     * @param supplier supplier
     * @return supplier
     */
    public static <T> LazySupplier<T> sharedVolatile(Supplier<T> supplier) {
        LazyCaller<T> cmp = LazyCallers.sharedVolatile();
        return () -> cmp.computeIfAbsent(supplier);
    }

    /**
     * Initialization is synchronized using correctly implemented "double checked locking".
     * It uses wrapper object with "final" attribute instead of "volatile". I don't
     * know anything about compatibility of that with JDK &lt; 1.5.
     * @param <T> T
     * @param supplier supplier
     * @return supplier
     */
    public static <T> LazySupplier<T> shared(Supplier<T> supplier) {
        LazyCaller<T> cmp = LazyCallers.shared();
        return () -> cmp.computeIfAbsent(supplier);
    }

    /**
     * Initialization is lock-free. There is a chance of spurious "double initialization".
     * @param <T> T
     * @param supplier supplier
     * @return  supplier
     */
    public static <T> LazySupplier<T> concurrent(Supplier<T> supplier) {
        LazyCaller<T> cmp = LazyCallers.concurrent();
        return () -> cmp.computeIfAbsent(supplier);
    }
	
}
