package com.filestack.transforms;

import com.filestack.FileLink;
import com.filestack.FilestackClient;
import com.filestack.errors.InternalException;
import com.filestack.errors.InvalidParameterException;
import com.filestack.errors.PolicySignatureException;
import com.filestack.errors.ResourceNotFoundException;
import com.filestack.responses.StoreResponse;
import com.filestack.transforms.tasks.StoreOptions;
import com.filestack.util.Util;
import com.google.gson.JsonObject;
import io.reactivex.Single;
import io.reactivex.schedulers.Schedulers;
import java.io.IOException;
import java.util.concurrent.Callable;
import retrofit2.Response;

/**
 * {@link Transform Transform} subclass for image transformations.
 */
public class ImageTransform extends Transform {

  public ImageTransform(FilestackClient fsClient, String source) {
    super(fsClient, source);
  }

  public ImageTransform(FileLink fileLink) {
    super(fileLink);
  }

  /**
   * Debugs the transformation as built so far, returning explanations of any issues.
   * @see <a href="https://www.filestack.com/docs/image-transformations/debug"></a>
   *
   * @return {@link JsonObject JSON} report for transformation
   * @throws IOException               if request fails because of network or other IO issue
   * @throws PolicySignatureException  if security is missing or invalid
   * @throws ResourceNotFoundException if API key, handle, or external URL are not found
   * @throws InvalidParameterException if a request parameter is missing or invalid
   * @throws InternalException         if unexpected error occurs
   */
  public JsonObject debug()
      throws IOException, PolicySignatureException, ResourceNotFoundException,
             InvalidParameterException, InternalException {

    String tasksString = getTasksString();

    Response<JsonObject> response;
    if (apiKey != null) {
      response = fsService.transformDebugExt(apiKey, tasksString, source).execute();
    } else {
      response = fsService.transformDebug(tasksString, source).execute();
    }

    Util.checkResponseAndThrow(response);

    JsonObject body = response.body();
    if (body == null) {
      throw new IOException();
    }

    return body;
  }

  /**
   * Stores the result of a transformation into a new file. Uses default storage options.
   * @see <a href="https://www.filestack.com/docs/image-transformations/store"></a>
   *
   * @return new {@link FileLink FileLink} pointing to the file
   * @throws IOException               if request fails because of network or other IO issue
   * @throws PolicySignatureException  if security is missing or invalid
   * @throws ResourceNotFoundException if API key, handle, or external URL are not found
   * @throws InvalidParameterException if a request parameter is missing or invalid
   * @throws InternalException         if unexpected error occurs
   */
  public FileLink store()
      throws IOException, PolicySignatureException, ResourceNotFoundException,
             InvalidParameterException, InternalException {

    return store(null);
  }

  /**
   * Stores the result of a transformation into a new file.
   * @see <a href="https://www.filestack.com/docs/image-transformations/store"></a>
   *
   * @param storeOptions configure where and how your file is stored
   * @return new {@link FileLink FileLink} pointing to the file
   * @throws IOException               if request fails because of network or other IO issue
   * @throws PolicySignatureException  if security is missing or invalid
   * @throws ResourceNotFoundException if API key, handle, or external URL are not found
   * @throws InvalidParameterException if a request parameter is missing or invalid
   * @throws InternalException         if unexpected error occurs
   */
  public FileLink store(StoreOptions storeOptions)
      throws IOException, PolicySignatureException, ResourceNotFoundException,
             InvalidParameterException, InternalException {

    if (storeOptions == null) {
      storeOptions = new StoreOptions();
    }
    addTask(storeOptions);

    Response<StoreResponse> response;
    String tasksString = getTasksString();
    if (apiKey != null) {
      response = fsService.transformStoreExt(apiKey, tasksString, source).execute();
    } else {
      response = fsService.transformStore(tasksString, source).execute();
    }

    Util.checkResponseAndThrow(response);

    StoreResponse body = response.body();
    if (body == null) {
      throw new IOException();
    }

    String handle = body.getUrl().split("/")[3];
    return new FileLink(apiKey, handle, security);
  }

  /**
   * Add a new transformation to the chain. Tasks are executed in the order they are added.
   *
   * @param task any of the available {@link ImageTransformTask} subclasses
   */
  public ImageTransform addTask(ImageTransformTask task) {
    if (task == null) {
      throw new NullPointerException("Cannot add null task to image transform");
    }
    tasks.add(task);
    return this;
  }

  // Async method wrappers

  /**
   * Async, observable version of {@link #debug()}.
   * Same exceptions are passed through observable.
   */
  public Single<JsonObject> debugAsync() {
    return Single.fromCallable(new Callable<JsonObject>() {
      @Override
      public JsonObject call() throws Exception {
        return debug();
      }
    })
        .subscribeOn(Schedulers.io())
        .observeOn(Schedulers.single());
  }

  /**
   * Async, observable version of {@link #store()}.
   * Same exceptions are passed through observable.
   */
  public Single<FileLink> storeAsync() {
    return storeAsync(null);
  }

  /**
   * Async, observable version of {@link #store(StoreOptions)}.
   * Same exceptions are passed through observable.
   */
  public Single<FileLink> storeAsync(final StoreOptions storeOptions) {
    return Single.fromCallable(new Callable<FileLink>() {
      @Override
      public FileLink call() throws Exception {
        return store(storeOptions);
      }
    })
        .subscribeOn(Schedulers.io())
        .observeOn(Schedulers.single());
  }
}

@@ -97,16 +97,53 @@
 Client;%0A
+import com.filestack.StorageOptions;%0A
 import c
@@ -3266,24 +3266,26 @@
  @param stor
+ag
 eOptions con
@@ -3820,32 +3820,34 @@
 eLink store(Stor
+ag
 eOptions storeOp
@@ -3835,32 +3835,34 @@
 rageOptions stor
+ag
 eOptions)%0A      
@@ -4003,24 +4003,26 @@
     if (stor
+ag
 eOptions == 
@@ -4035,24 +4035,26 @@
 %7B%0A      stor
+ag
 eOptions = n
@@ -4056,24 +4056,26 @@
 s = new Stor
+ag
 eOptions();%0A
@@ -4084,32 +4084,37 @@
   %7D%0A
+%0A
     
-addTask
+tasks.add
 (stor
+ag
 eOptions
 );%0A%0A
@@ -4109,16 +4109,28 @@
 eOptions
+.getAsTask()
 );%0A%0A    
@@ -5742,24 +5742,26 @@
  #store(Stor
+ag
 eOptions)%7D.%0A
@@ -5861,24 +5861,26 @@
 c(final Stor
+ag
 eOptions sto
@@ -5876,24 +5876,26 @@
 Options stor
+ag
 eOptions) %7B%0A
@@ -6041,16 +6041,18 @@
 ore(stor
+ag
 eOptions

Update ImageTransform to use new StorageOptions