/*
 * Copyright 2021 Danilo Reinert
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.reinert.requestor.core;

import io.reinert.requestor.core.header.Header;
import io.reinert.requestor.core.payload.Payload;
import io.reinert.requestor.core.payload.SerializedPayload;
import io.reinert.requestor.core.payload.type.PayloadType;
import io.reinert.requestor.core.uri.Uri;

/**
 * PreparedRequest implementation that ensures the request is dispatched only once.
 *
 * @author Danilo Reinert
 */
class PreparedRequestImpl<R> implements PreparedRequest {

    private final RequestDispatcher dispatcher;
    private final MutableSerializedRequest request;
    private final Deferred<R> deferred;
    private final PayloadType responsePayloadType;
    private final UriWithQueryBuilder uri;

    private boolean withCredentials;
    private boolean sent;

    public PreparedRequestImpl(RequestDispatcher dispatcher, MutableSerializedRequest request, Deferred<R> deferred,
                               PayloadType responsePayloadType) {
        this(dispatcher, request, deferred, responsePayloadType, false, false);
    }

    private PreparedRequestImpl(RequestDispatcher dispatcher, MutableSerializedRequest request, Deferred<R> deferred,
                                PayloadType responsePayloadType, boolean withCredentials, boolean sent) {
        this.dispatcher = dispatcher;
        this.request = request;
        this.deferred = deferred;
        this.responsePayloadType = responsePayloadType;
        this.withCredentials = withCredentials;
        this.sent = sent;
        this.uri = new UriWithQueryBuilder(request.getUri());
    }

    @Override
    public void abort(RawResponse response) {
        if (sent)
            throw new IllegalStateException("PreparedRequest couldn't be aborted: Request has already been sent.");

        dispatcher.evalResponse(response);

        sent = true;
    }

    @Override
    public void abort(RequestAbortException error) {
        if (sent)
            throw new IllegalStateException("PreparedRequest couldn't be aborted: Request has already been sent.");

        deferred.notifyError(error);

        sent = true;
    }

    @Override
    public void send() {
        if (sent)
            throw new IllegalStateException("PreparedRequest has already been sent.");

        try {
            dispatcher.send(this, deferred, responsePayloadType);
        } catch (Exception e) {
            deferred.notifyError(new RequestDispatchException(request,
                    "Some non-caught exception occurred while dispatching the request", e));
        }

        sent = true;
    }

    @Override
    public String getAccept() {
        return request.getAccept();
    }

    @Override
    public String getContentType() {
        return request.getContentType();
    }

    @Override
    public Headers getHeaders() {
        return request.getHeaders();
    }

    @Override
    public String getHeader(String headerName) {
        return request.getHeader(headerName);
    }

    @Override
    public HttpMethod getMethod() {
        return request.getMethod();
    }

    @Override
    public Payload getPayload() {
        return request.getPayload();
    }

    @Override
    public SerializedPayload getSerializedPayload() {
        return request.getSerializedPayload();
    }

    @Override
    public int getTimeout() {
        return request.getTimeout();
    }

    @Override
    public int getDelay() {
        return request.getDelay();
    }

    @Override
    public boolean isPolling() {
        return request.isPolling();
    }

    @Override
    public int getPollingInterval() {
        return request.getPollingInterval();
    }

    @Override
    public int getPollingLimit() {
        return request.getPollingLimit();
    }

    @Override
    public int getPollingCounter() {
        return request.getPollingCounter();
    }

    @Override
    public PollingStrategy getPollingStrategy() {
        return request.getPollingStrategy();
    }

    @Override
    public void stopPolling() {
        request.stopPolling();
    }

    @Override
    public Uri getUri() {
        return uri.getUri();
    }

    @Override
    public PayloadType getResponsePayloadType() {
        return responsePayloadType;
    }

    @Override
    public RequestDispatcher getDispatcher() {
        return dispatcher;
    }

    @Override
    public Deferred<?> getDeferred() {
        return deferred;
    }

    @Override
    public Auth getAuth() {
        return request.getAuth();
    }

    @Override
    public Store getStore() {
        return request.getStore();
    }

    @Override
    public boolean isWithCredentials() {
        return withCredentials;
    }

    @Override
    public void setHeader(Header header) {
        request.setHeader(header);
    }

    @Override
    public void setHeader(String name, String value) {
        request.setHeader(name, value);
    }

    @Override
    public Header delHeader(String headerName) {
        return request.delHeader(headerName);
    }

    @Override
    public void setQueryParam(String name, String... values) {
        uri.setQueryParam(name, values);
    }

    @Override
    public MutableSerializedRequest getMutableCopy() {
        return request.copy();
    }

    @Override
    public void setWithCredentials(boolean withCredentials) {
        this.withCredentials = withCredentials;
    }
}

@@ -929,56 +929,34 @@
 ion 
-that ensures the request is dispatched only once
+supporting request retries
 .%0A *
@@ -1320,34 +1320,8 @@
 als;
-%0A    private boolean sent;
 %0A%0A  
@@ -1569,15 +1569,8 @@
 ype,
- false,
  fal
@@ -1791,22 +1791,8 @@
 ials
-, boolean sent
 ) %7B%0A
@@ -2003,34 +2003,8 @@
 ls;%0A
-        this.sent = sent;%0A
     
@@ -2132,143 +2132,8 @@
 ) %7B%0A
-        if (sent)%0A            throw new IllegalStateException(%22PreparedRequest couldn't be aborted: Request has already been sent.%22);%0A%0A
     
@@ -2170,38 +2170,16 @@
 sponse);
-%0A%0A        sent = true;
 %0A    %7D%0A%0A
@@ -2249,143 +2249,8 @@
 ) %7B%0A
-        if (sent)%0A            throw new IllegalStateException(%22PreparedRequest couldn't be aborted: Request has already been sent.%22);%0A%0A
     
@@ -2281,38 +2281,16 @@
 (error);
-%0A%0A        sent = true;
 %0A    %7D%0A%0A
@@ -2332,114 +2332,8 @@
 ) %7B%0A
-        if (sent)%0A            throw new IllegalStateException(%22PreparedRequest has already been sent.%22);%0A%0A
     
@@ -2425,16 +2425,23 @@
  catch (
+Runtime
 Exceptio
@@ -2607,24 +2607,24 @@
 uest%22, e));%0A
+
         %7D%0A%0A 
@@ -2624,30 +2624,8 @@
    %7D
-%0A%0A        sent = true;
 %0A   

Remove sent flag from PreparedRequestImpl