package meerkat.bulletinboard;

import com.google.common.util.concurrent.FutureCallback;

import com.google.common.util.concurrent.FutureCallback;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by Arbel Deutsch Peled on 09-Dec-15.
 *
 * This is a general class for handling multi-server work
 * It utilizes Single Server Clients to perform the actual per-server work
 */
public abstract class MultiServerWorker<IN, OUT> extends BulletinClientWorker<IN> implements Runnable, FutureCallback<OUT>{

    protected final List<SingleServerBulletinBoardClient> clients;

    protected AtomicInteger minServers; // The minimal number of servers the job must be successful on for the job to be completed

    protected AtomicInteger maxFailedServers; // The maximal number of allowed server failures

    private AtomicBoolean returnedResult;

    private final FutureCallback<OUT> futureCallback;

    /**
     * Constructor
     * @param clients contains a list of Single Server clients to handle requests
     * @param shuffleClients is a boolean stating whether or not it is needed to shuffle the clients
     * @param minServers is the minimal amount of servers needed in order to successfully complete the job
     * @param payload is the payload for the job
     * @param maxRetry is the maximal per-server retry count
     * @param futureCallback contains the callback methods used to report the result back to the client
     */
    public MultiServerWorker(List<SingleServerBulletinBoardClient> clients, boolean shuffleClients,
                             int minServers, IN payload, int maxRetry,
                             FutureCallback<OUT> futureCallback) {

        super(payload,maxRetry);

        this.clients = clients;
        if (shuffleClients){
            Collections.shuffle(clients);
        }

        this.minServers = new AtomicInteger(minServers);
        maxFailedServers = new AtomicInteger(clients.size() - minServers);
        this.futureCallback = futureCallback;

        returnedResult = new AtomicBoolean(false);

    }

    /**
     * Constructor overload without client shuffling
     */
    public MultiServerWorker(List<SingleServerBulletinBoardClient> clients,
                             int minServers, IN payload, int maxRetry,
                             FutureCallback<OUT> futureCallback) {

        this(clients, false, minServers, payload, maxRetry, futureCallback);

    }

    /**
     * Used to report a successful operation to the client
     * Only reports once to the client
     * @param result is the result
     */
    protected void succeed(OUT result){
        if (returnedResult.compareAndSet(false, true)) {
            if (futureCallback != null)
                futureCallback.onSuccess(result);
        }
    }

    /**
     * Used to report a failed operation to the client
     * Only reports once to the client
     * @param t contains the error/exception that occurred
     */
    protected void fail(Throwable t){
        if (returnedResult.compareAndSet(false, true)) {
            if (futureCallback != null)
                futureCallback.onFailure(t);
        }
    }

    protected int getClientNumber() {
        return clients.size();
    }

}