// Path: cachedfield/src/main/java/com/byoutline/cachedfield/ErrorListenerWithArg.java
// public interface ErrorListenerWithArg<ARG_TYPE> {
// 
//     void valueLoadingFailed(Exception ex, ARG_TYPE arg);
// }
// 
// Path: cachedfield/src/main/java/com/byoutline/cachedfield/ProviderWithArg.java
// public interface ProviderWithArg<RETURN_TYPE, ARG_TYPE> {
// 
//     RETURN_TYPE get(ARG_TYPE arg);
// }
// 
// Path: cachedfield/src/main/java/com/byoutline/cachedfield/SuccessListenerWithArg.java
// public interface SuccessListenerWithArg<VALUE_TYPE, ARG_TYPE> {
// 
//     void valueLoaded(VALUE_TYPE value, ARG_TYPE arg);
// }


import com.byoutline.cachedfield.ErrorListenerWithArg;
import com.byoutline.cachedfield.ProviderWithArg;
import com.byoutline.cachedfield.SuccessListenerWithArg;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.concurrent.atomic.AtomicBoolean;
package com.byoutline.cachedfield.internal;



/**
 * Thread that loads {@link #valueGetter} into {@link #valueGetter} and informs listener.
 * <p>
 * After execution either {@link #successListener} or {@link #errorListener} will be informed
 * exactly once.
 * </p>
 *
 * @author Sebastian Kacprzak <sebastian.kacprzak at byoutline.com>
 */
class LoadThread<RETURN_TYPE, ARG_TYPE> extends Thread {
    private final ProviderWithArg<RETURN_TYPE, ARG_TYPE> valueGetter;
    private final SuccessListenerWithArg<RETURN_TYPE, ARG_TYPE> successListener;
    private final ErrorListenerWithArg<ARG_TYPE> errorListener;
    private final CachedValue<RETURN_TYPE, ARG_TYPE> value;
    private final ARG_TYPE arg;
    private final boolean dropValueOnFailure;

    /**
     * Guards against posting two values from thread (for example calling success listener,
     * then getting interrupted and informing error listener).
     */
    private final AtomicBoolean resultPosted = new AtomicBoolean(false);

    public LoadThread(@Nonnull ProviderWithArg<RETURN_TYPE, ARG_TYPE> valueGetter,
                      @Nonnull SuccessListenerWithArg<RETURN_TYPE, ARG_TYPE> successListener,
                      @Nonnull ErrorListenerWithArg<ARG_TYPE> errorListener,
                      @Nonnull CachedValue<RETURN_TYPE, ARG_TYPE> value,
                      @Nullable ARG_TYPE arg,
                      boolean dropValueOnFailure) {
        this.valueGetter = valueGetter;
        this.successListener = successListener;
        this.errorListener = errorListener;
        this.value = value;
        this.arg = arg;
        this.dropValueOnFailure = dropValueOnFailure;
    }

    @Override
    public void run() {
        try {
            value.loadingStarted(arg);
            RETURN_TYPE fetchedValue = valueGetter.get(arg);
            // We want to successfully inform either success or error listener, so
            // if loading got interrupted we no longer call success listener.
            if (resultPosted.compareAndSet(false, true)) {
// Path: cachedfield/src/main/java/com/byoutline/cachedfield/ErrorListenerWithArg.java
// public interface ErrorListenerWithArg<ARG_TYPE> {
// 
//     void valueLoadingFailed(Exception ex, ARG_TYPE arg);
// }
// 
// Path: cachedfield/src/main/java/com/byoutline/cachedfield/ProviderWithArg.java
// public interface ProviderWithArg<RETURN_TYPE, ARG_TYPE> {
// 
//     RETURN_TYPE get(ARG_TYPE arg);
// }
// 
// Path: cachedfield/src/main/java/com/byoutline/cachedfield/SuccessListenerWithArg.java
// public interface SuccessListenerWithArg<VALUE_TYPE, ARG_TYPE> {
// 
//     void valueLoaded(VALUE_TYPE value, ARG_TYPE arg);
// }


// Path: cachedfield/src/main/java/com/byoutline/cachedfield/internal/LoadThread.java
import com.byoutline.cachedfield.ErrorListenerWithArg;
import com.byoutline.cachedfield.ProviderWithArg;
import com.byoutline.cachedfield.SuccessListenerWithArg;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.concurrent.atomic.AtomicBoolean;

package com.byoutline.cachedfield.internal;



/**
 * Thread that loads {@link #valueGetter} into {@link #valueGetter} and informs listener.
 * <p>
 * After execution either {@link #successListener} or {@link #errorListener} will be informed
 * exactly once.
 * </p>
 *
 * @author Sebastian Kacprzak <sebastian.kacprzak at byoutline.com>
 */
class LoadThread<RETURN_TYPE, ARG_TYPE> extends Thread {
    private final ProviderWithArg<RETURN_TYPE, ARG_TYPE> valueGetter;
    private final SuccessListenerWithArg<RETURN_TYPE, ARG_TYPE> successListener;
    private final ErrorListenerWithArg<ARG_TYPE> errorListener;
    private final CachedValue<RETURN_TYPE, ARG_TYPE> value;
    private final ARG_TYPE arg;
    private final boolean dropValueOnFailure;

    /**
     * Guards against posting two values from thread (for example calling success listener,
     * then getting interrupted and informing error listener).
     */
    private final AtomicBoolean resultPosted = new AtomicBoolean(false);

    public LoadThread(@Nonnull ProviderWithArg<RETURN_TYPE, ARG_TYPE> valueGetter,
                      @Nonnull SuccessListenerWithArg<RETURN_TYPE, ARG_TYPE> successListener,
                      @Nonnull ErrorListenerWithArg<ARG_TYPE> errorListener,
                      @Nonnull CachedValue<RETURN_TYPE, ARG_TYPE> value,
                      @Nullable ARG_TYPE arg,
                      boolean dropValueOnFailure) {
        this.valueGetter = valueGetter;
        this.successListener = successListener;
        this.errorListener = errorListener;
        this.value = value;
        this.arg = arg;
        this.dropValueOnFailure = dropValueOnFailure;
    }

    @Override
    public void run() {
        try {
            value.loadingStarted(arg);
            RETURN_TYPE fetchedValue = valueGetter.get(arg);
            // We want to successfully inform either success or error listener, so
            // if loading got interrupted we no longer call success listener.
            if (resultPosted.compareAndSet(false, true)) {
                value.setSuccess(fetchedValue, arg);