package org.jgroups.blocks;


import org.jgroups.Address;
import org.jgroups.Message;
import org.jgroups.View;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;


/**
 * Abstract class for a unicast or multicast request
 *
 * @author Bela Ban
 */
public abstract class Request<T> extends CompletableFuture<T> {
    protected long                    req_id;
    protected final RequestCorrelator corr;         // either use RequestCorrelator or ...
    protected final RequestOptions    options;
    protected long                    start_time;   // in ns, to compute RTT for blocking RPCs


    public Request(RequestCorrelator corr, RequestOptions options) {
        this.corr=corr;
        this.options=options;
    }

    public Request<T> requestId(long req_id) {
        this.req_id=req_id;
        return this;
    }

    public long requestId() {
        return req_id;
    }

    public Request<T> setResponseFilter(RspFilter filter) {
        options.rspFilter(filter);
        return this;
    }


    public T execute(Message msg, boolean block_for_results) throws Exception {
        if(corr == null)
            return null;

        sendRequest(msg);
        if(!block_for_results || options.mode() == ResponseMode.GET_NONE)
            return null;
        long timeout=options.timeout();
        return timeout > 0? waitForCompletion(timeout, TimeUnit.MILLISECONDS) : waitForCompletion();
    }

    public abstract void       sendRequest(Message req) throws Exception;

    public abstract void       receiveResponse(Object response_value, Address sender, boolean is_exception);

    public abstract void       viewChange(View new_view);

    public abstract void       siteUnreachable(String site);

    public abstract void       transportClosed();

    /** Blocks until all responses have been received and returns result or throws exception */
    public abstract T          waitForCompletion(long timeout, TimeUnit unit) throws Exception;
    public abstract T          waitForCompletion() throws Exception;



    public boolean cancel(boolean mayInterruptIfRunning) {
        try {
            return super.cancel(mayInterruptIfRunning);
        }
        finally {
            corrDone();
        }
    }


    public String toString() {
        return String.format("%s, mode=%s", getClass().getSimpleName(), options.mode());
    }


    protected void corrDone() {
        if(corr!=null && this.req_id > 0)
            corr.done(this.req_id);
    }
}
