/**
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See License.txt in the project root for
 * license information.
 *
 * Code generated by Microsoft (R) AutoRest Code Generator.
 */

package com.microsoft.azure.cognitiveservices.language.luis.authoring.implementation;

import retrofit2.Retrofit;
import com.microsoft.azure.cognitiveservices.language.luis.authoring.Trains;
import com.google.common.base.Joiner;
import com.google.common.reflect.TypeToken;
import com.microsoft.azure.cognitiveservices.language.luis.authoring.models.EnqueueTrainingResponse;
import com.microsoft.azure.cognitiveservices.language.luis.authoring.models.ErrorResponseException;
import com.microsoft.azure.cognitiveservices.language.luis.authoring.models.ModelTrainingInfo;
import com.microsoft.rest.ServiceCallback;
import com.microsoft.rest.ServiceFuture;
import com.microsoft.rest.ServiceResponse;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
import okhttp3.ResponseBody;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.Path;
import retrofit2.http.POST;
import retrofit2.Response;
import rx.functions.Func1;
import rx.Observable;

/**
 * An instance of this class provides access to all the operations defined
 * in Trains.
 */
public class TrainsImpl implements Trains {
    /** The Retrofit service to perform REST calls. */
    private TrainsService service;
    /** The service client containing this operation class. */
    private LUISAuthoringClientImpl client;

    /**
     * Initializes an instance of TrainsImpl.
     *
     * @param retrofit the Retrofit instance built from a Retrofit Builder.
     * @param client the instance of the service client containing this operation class.
     */
    public TrainsImpl(Retrofit retrofit, LUISAuthoringClientImpl client) {
        this.service = retrofit.create(TrainsService.class);
        this.client = client;
    }

    /**
     * The interface defining all the services for Trains to be
     * used by Retrofit to perform actually REST calls.
     */
    interface TrainsService {
        @Headers({ "Content-Type: application/json; charset=utf-8", "x-ms-logging-context: com.microsoft.azure.cognitiveservices.language.luis.authoring.Trains trainVersion" })
        @POST("apps/{appId}/versions/{versionId}/train")
        Observable<Response<ResponseBody>> trainVersion(@Path("appId") UUID appId, @Path("versionId") String versionId, @Header("accept-language") String acceptLanguage, @Header("x-ms-parameterized-host") String parameterizedHost, @Header("User-Agent") String userAgent);

        @Headers({ "Content-Type: application/json; charset=utf-8", "x-ms-logging-context: com.microsoft.azure.cognitiveservices.language.luis.authoring.Trains getStatus" })
        @GET("apps/{appId}/versions/{versionId}/train")
        Observable<Response<ResponseBody>> getStatus(@Path("appId") UUID appId, @Path("versionId") String versionId, @Header("accept-language") String acceptLanguage, @Header("x-ms-parameterized-host") String parameterizedHost, @Header("User-Agent") String userAgent);

    }

    /**
     * Sends a training request for a version of a specified LUIS app. This POST request initiates a request asynchronously. To determine whether the training request is successful, submit a GET request to get training status. Note: The application version is not fully trained unless all the models (intents and entities) are trained successfully or are up to date. To verify training success, get the training status at least once after training is complete.
     *
     * @param appId The application ID.
     * @param versionId The version ID.
     * @throws IllegalArgumentException thrown if parameters fail the validation
     * @throws ErrorResponseException thrown if the request is rejected by server
     * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent
     * @return the EnqueueTrainingResponse object if successful.
     */
    public EnqueueTrainingResponse trainVersion(UUID appId, String versionId) {
        return trainVersionWithServiceResponseAsync(appId, versionId).toBlocking().single().body();
    }

    /**
     * Sends a training request for a version of a specified LUIS app. This POST request initiates a request asynchronously. To determine whether the training request is successful, submit a GET request to get training status. Note: The application version is not fully trained unless all the models (intents and entities) are trained successfully or are up to date. To verify training success, get the training status at least once after training is complete.
     *
     * @param appId The application ID.
     * @param versionId The version ID.
     * @param serviceCallback the async ServiceCallback to handle successful and failed responses.
     * @throws IllegalArgumentException thrown if parameters fail the validation
     * @return the {@link ServiceFuture} object
     */
    public ServiceFuture<EnqueueTrainingResponse> trainVersionAsync(UUID appId, String versionId, final ServiceCallback<EnqueueTrainingResponse> serviceCallback) {
        return ServiceFuture.fromResponse(trainVersionWithServiceResponseAsync(appId, versionId), serviceCallback);
    }

    /**
     * Sends a training request for a version of a specified LUIS app. This POST request initiates a request asynchronously. To determine whether the training request is successful, submit a GET request to get training status. Note: The application version is not fully trained unless all the models (intents and entities) are trained successfully or are up to date. To verify training success, get the training status at least once after training is complete.
     *
     * @param appId The application ID.
     * @param versionId The version ID.
     * @throws IllegalArgumentException thrown if parameters fail the validation
     * @return the observable to the EnqueueTrainingResponse object
     */
    public Observable<EnqueueTrainingResponse> trainVersionAsync(UUID appId, String versionId) {
        return trainVersionWithServiceResponseAsync(appId, versionId).map(new Func1<ServiceResponse<EnqueueTrainingResponse>, EnqueueTrainingResponse>() {
            @Override
            public EnqueueTrainingResponse call(ServiceResponse<EnqueueTrainingResponse> response) {
                return response.body();
            }
        });
    }

    /**
     * Sends a training request for a version of a specified LUIS app. This POST request initiates a request asynchronously. To determine whether the training request is successful, submit a GET request to get training status. Note: The application version is not fully trained unless all the models (intents and entities) are trained successfully or are up to date. To verify training success, get the training status at least once after training is complete.
     *
     * @param appId The application ID.
     * @param versionId The version ID.
     * @throws IllegalArgumentException thrown if parameters fail the validation
     * @return the observable to the EnqueueTrainingResponse object
     */
    public Observable<ServiceResponse<EnqueueTrainingResponse>> trainVersionWithServiceResponseAsync(UUID appId, String versionId) {
        if (this.client.endpoint() == null) {
            throw new IllegalArgumentException("Parameter this.client.endpoint() is required and cannot be null.");
        }
        if (appId == null) {
            throw new IllegalArgumentException("Parameter appId is required and cannot be null.");
        }
        if (versionId == null) {
            throw new IllegalArgumentException("Parameter versionId is required and cannot be null.");
        }
        String parameterizedHost = Joiner.on(", ").join("{Endpoint}", this.client.endpoint());
        return service.trainVersion(appId, versionId, this.client.acceptLanguage(), parameterizedHost, this.client.userAgent())
            .flatMap(new Func1<Response<ResponseBody>, Observable<ServiceResponse<EnqueueTrainingResponse>>>() {
                @Override
                public Observable<ServiceResponse<EnqueueTrainingResponse>> call(Response<ResponseBody> response) {
                    try {
                        ServiceResponse<EnqueueTrainingResponse> clientResponse = trainVersionDelegate(response);
                        return Observable.just(clientResponse);
                    } catch (Throwable t) {
                        return Observable.error(t);
                    }
                }
            });
    }

    private ServiceResponse<EnqueueTrainingResponse> trainVersionDelegate(Response<ResponseBody> response) throws ErrorResponseException, IOException, IllegalArgumentException {
        return this.client.restClient().responseBuilderFactory().<EnqueueTrainingResponse, ErrorResponseException>newInstance(this.client.serializerAdapter())
                .register(202, new TypeToken<EnqueueTrainingResponse>() { }.getType())
                .registerError(ErrorResponseException.class)
                .build(response);
    }

    /**
     * Gets the training status of all models (intents and entities) for the specified LUIS app. You must call the train API to train the LUIS app before you call this API to get training status. "appID" specifies the LUIS app ID. "versionId" specifies the version number of the LUIS app. For example, "0.1".
     *
     * @param appId The application ID.
     * @param versionId The version ID.
     * @throws IllegalArgumentException thrown if parameters fail the validation
     * @throws ErrorResponseException thrown if the request is rejected by server
     * @throws RuntimeException all other wrapped checked exceptions if the request fails to be sent
     * @return the List&lt;ModelTrainingInfo&gt; object if successful.
     */
    public List<ModelTrainingInfo> getStatus(UUID appId, String versionId) {
        return getStatusWithServiceResponseAsync(appId, versionId).toBlocking().single().body();
    }

    /**
     * Gets the training status of all models (intents and entities) for the specified LUIS app. You must call the train API to train the LUIS app before you call this API to get training status. "appID" specifies the LUIS app ID. "versionId" specifies the version number of the LUIS app. For example, "0.1".
     *
     * @param appId The application ID.
     * @param versionId The version ID.
     * @param serviceCallback the async ServiceCallback to handle successful and failed responses.
     * @throws IllegalArgumentException thrown if parameters fail the validation
     * @return the {@link ServiceFuture} object
     */
    public ServiceFuture<List<ModelTrainingInfo>> getStatusAsync(UUID appId, String versionId, final ServiceCallback<List<ModelTrainingInfo>> serviceCallback) {
        return ServiceFuture.fromResponse(getStatusWithServiceResponseAsync(appId, versionId), serviceCallback);
    }

    /**
     * Gets the training status of all models (intents and entities) for the specified LUIS app. You must call the train API to train the LUIS app before you call this API to get training status. "appID" specifies the LUIS app ID. "versionId" specifies the version number of the LUIS app. For example, "0.1".
     *
     * @param appId The application ID.
     * @param versionId The version ID.
     * @throws IllegalArgumentException thrown if parameters fail the validation
     * @return the observable to the List&lt;ModelTrainingInfo&gt; object
     */
    public Observable<List<ModelTrainingInfo>> getStatusAsync(UUID appId, String versionId) {
        return getStatusWithServiceResponseAsync(appId, versionId).map(new Func1<ServiceResponse<List<ModelTrainingInfo>>, List<ModelTrainingInfo>>() {
            @Override
            public List<ModelTrainingInfo> call(ServiceResponse<List<ModelTrainingInfo>> response) {
                return response.body();
            }
        });
    }

    /**
     * Gets the training status of all models (intents and entities) for the specified LUIS app. You must call the train API to train the LUIS app before you call this API to get training status. "appID" specifies the LUIS app ID. "versionId" specifies the version number of the LUIS app. For example, "0.1".
     *
     * @param appId The application ID.
     * @param versionId The version ID.
     * @throws IllegalArgumentException thrown if parameters fail the validation
     * @return the observable to the List&lt;ModelTrainingInfo&gt; object
     */
    public Observable<ServiceResponse<List<ModelTrainingInfo>>> getStatusWithServiceResponseAsync(UUID appId, String versionId) {
        if (this.client.endpoint() == null) {
            throw new IllegalArgumentException("Parameter this.client.endpoint() is required and cannot be null.");
        }
        if (appId == null) {
            throw new IllegalArgumentException("Parameter appId is required and cannot be null.");
        }
        if (versionId == null) {
            throw new IllegalArgumentException("Parameter versionId is required and cannot be null.");
        }
        String parameterizedHost = Joiner.on(", ").join("{Endpoint}", this.client.endpoint());
        return service.getStatus(appId, versionId, this.client.acceptLanguage(), parameterizedHost, this.client.userAgent())
            .flatMap(new Func1<Response<ResponseBody>, Observable<ServiceResponse<List<ModelTrainingInfo>>>>() {
                @Override
                public Observable<ServiceResponse<List<ModelTrainingInfo>>> call(Response<ResponseBody> response) {
                    try {
                        ServiceResponse<List<ModelTrainingInfo>> clientResponse = getStatusDelegate(response);
                        return Observable.just(clientResponse);
                    } catch (Throwable t) {
                        return Observable.error(t);
                    }
                }
            });
    }

    private ServiceResponse<List<ModelTrainingInfo>> getStatusDelegate(Response<ResponseBody> response) throws ErrorResponseException, IOException, IllegalArgumentException {
        return this.client.restClient().responseBuilderFactory().<List<ModelTrainingInfo>, ErrorResponseException>newInstance(this.client.serializerAdapter())
                .register(200, new TypeToken<List<ModelTrainingInfo>>() { }.getType())
                .registerError(ErrorResponseException.class)
                .build(response);
    }

}
