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

import net.ranides.assira.reflection.IClass;

import java.io.Serializable;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * Simple interface of object which allows to modify stored external value.
 *
 * It could be simple mutable wrapper with behaviour similar to singleton collection,
 * but it could be always binding to some field, property or map entry.
 *
 * In other words: it can represent anything ;-)
 *
 * @param <T> T
 */
public interface Wrapper<T> extends Set<T>, Serializable {

    /**
     * Returns declared type of value.
     * It must be superclass of stored value, at best it should be identical.
     *
     * @return type
     */
    IClass<?> type();

    /**
     * Returns binded value.
     * For example: simple wrapper will return stored value, EntryWrapper will read from Map.
     *
     * @return value value
     */
    T get();

    /**
     * Changes binded value.
     * For example: simple wrapper will update stored value, EntryWrapper will change value inside Map.
     *
     * @param value value
     */
    void set(T value);

    /**
     * Changes binded value.
     * For example: simple wrapper will update stored value, EntryWrapper will change value inside Map.
     *
     * Please note: because "replace" returns previous value, it executes "get" internally.
     *
     * @param value value
     * @return previously stored value
     */
    T replace(T value);

    /**
     * Casts this Wrapper to new type: it is safe, checked cast.
     * It does not create new wrapper, it is only cast operation.
     *
     * @param type type
     * @param <R> R
     * @return this
     */
    <R> Wrapper<R> cast(Class<R> type);

    /**
     * Casts this Wrapper to new type: it is safe, checked cast.
     * It does not create new wrapper, it is only cast operation.
     *
     * @param type type
     * @param <R> R
     * @return this
     */
    <R> Wrapper<R> cast(TypeToken<R> type);

    /**
     * Checks if value is not "null"
     *
     * @return boolean
     */
    boolean isPresent();

    @Override
    boolean isEmpty();

    /**
     * Returns true if optional does not contain value
     *
     * @return boolean
     */
    boolean isAbsent();

    /**
     * Executes consumer with value stored inside optional if it is non-empty.
     * Does nothing if optional is empty.
     *
     * @param consumer consumer
     */
    void ifPresent(Consumer<? super T> consumer);

    void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction);

    /**
     * Executes consumer if there is no value inside optional.
     * Does nothing if optional is not empty.
     *
     * @param consumer consumer
     */
    void ifAbsent(Runnable consumer);

    /**
     * Returns value stored in optional or "other" if optional is empty.
     *
     * @param other other
     * @return value
     */
    T orElse(T other);

    /**
     * Returns value stored in optional or computes "other" if optional is empty.
     *
     * @param other other
     * @return value
     */
    T orElseGet(Supplier<? extends T> other);

    /**
     * Returns value stored in optional or throws computed exception if optional is empty.
     *
     * @param onEmpty onEmpty
     * @param <X> X
     * @return value
     * @throws X if empty
     */
    <X extends Throwable> T orElseThrow(Supplier<? extends X> onEmpty) throws X;
}
