package model;

import okhttp3.HttpUrl;
import util.FilestackService;
import util.Networking;

import java.util.ArrayList;
import java.util.Arrays;

/**
 * Base class for file transformations and conversions.
 */
public class Transform {
    String apiKey;
    String source;

    ArrayList<Task> tasks;

    FilestackService.Process processService;

    Transform(Client client, String source) {
        this(client, source, null);
    }

    Transform(FileLink fileLink) {
        this(null, null, fileLink);
    }

    Transform(Client client, String source, FileLink fileLink) {
        if (client != null) {
            this.apiKey = client.getApiKey();
            this.source = source;
        } else {
            this.source = fileLink.getHandle();
        }

        this.tasks = new ArrayList<>();
        this.processService = Networking.getProcessService();

        Security security = client != null ? client.getSecurity() : fileLink.getSecurity();
        if (security != null) {
            Task securityTask = new Task("security");
            securityTask.addOption("policy", security.getPolicy());
            securityTask.addOption("signature", security.getSignature());
            this.tasks.add(securityTask);
        }
    }

    /**
     * Generic task object.
     * A "task" in this case is a transformation, for example resize, crop, convert, etc.
     */
    protected static class Task {
        String name;
        ArrayList<Option> options;

        Task(String name) {
            this.name = name;
            this.options = new ArrayList<>();
        }

        void addOption(String key, Object value) {
            addOption(key, value.toString());
        }

        void addOption(String key, Object value[]) {
            String valueString = Arrays.toString(value);
            // Remove spaces between array items
            valueString = valueString.replace(" ", "");
            addOption(key, valueString);
        }

        void addOption(String key, String value) {
            options.add(new Option(key, value));
        }

        @Override
        public String toString() {
            StringBuilder stringBuilder = new StringBuilder(name);
            stringBuilder.append("=");
            for (Option option : options)
                if (option.value != null)
                    stringBuilder.append(option.key).append(":").append(option.value).append(",");
            stringBuilder.deleteCharAt(stringBuilder.length()-1);
            return stringBuilder.toString();
        }
    }

    /**
     * Each {@link Task Task} object has options.
     * These are the settings for that task.
     * For example the resize task would have options for width and height.
     */
    protected static class Option {
        String key;
        String value;

        Option(String key, String value) {
            this.key = key;
            this.value = value;
        }
    }

    /**
     * Build tasks into single string to insert into request.
     */
    protected String getTasksString() {
        if (tasks.size() == 0)
            return "";

        StringBuilder stringBuilder = new StringBuilder();
        for (Task task : tasks)
            stringBuilder.append(task.toString()).append('/');
        stringBuilder.deleteCharAt(stringBuilder.length()-1);
        return stringBuilder.toString();
    }

    public String url() {
        String tasksString = getTasksString();
        HttpUrl httpUrl;

        if (apiKey != null)
            httpUrl = processService.getExternal(apiKey, tasksString, source).request().url();
        else
            httpUrl = processService.get(tasksString, source).request().url();

        // When forming the request we add a / between tasks, then add that entire string as a path variable
        // Because it's added as a single path variable, the / is URL encoded
        // That's a little confusing so we're replacing "%2F" with "/" for a more expected URL
        return httpUrl.toString().replace("%2F", "/");
    }
}

@@ -88,16 +88,64 @@
 rking;%0A%0A
+import java.security.InvalidParameterException;%0A
 import j
@@ -1699,106 +1699,491 @@
     
-addOption(key, value.toString());%0A        %7D%0A%0A        void addOption(String key, Object value%5B%5D) %7B%0A
+// Passing an empty key is a mistake, shouldn't happen%0A            if (key == null %7C%7C key.length() == 0)%0A                throw new InvalidParameterException(%22Task option key cannot be empty%22);%0A            // Allowing the passing of a null value however, is for convenience%0A            // If we're leaving out an option for a transform task, we only need to check for that here%0A            if (value == null)%0A                return;%0A%0A            if (value.getClass().isArray()) %7B%0A    
     
@@ -2231,16 +2231,26 @@
 ing(
+(Object%5B%5D)
 value);%0A
     
@@ -2241,24 +2241,28 @@
 t%5B%5D)value);%0A
+    
             
@@ -2310,16 +2310,20 @@
         
+   
+ 
 valueStr
@@ -2370,19 +2370,36 @@
         
-add
+    options.add(new 
 Option(k
@@ -2414,16 +2414,17 @@
 eString)
+)
 ;%0A      
@@ -2429,62 +2429,25 @@
     
-%7D%0A%0A
     
-    void addOption(String key, String value) %7B%0A
+%7D else %7B%0A    
     
@@ -2487,19 +2487,44 @@
 y, value
-));
+.toString()));%0A            %7D
 %0A       

Fix exception when adding a null option to transform task