/*
 * Copyright 2015-2018 Micro Focus or one of its affiliates.
 *
 * 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 com.hpe.caf.autoscale.scaler.marathon;

import com.google.gson.JsonObject;
import com.google.gson.JsonArray;
import com.hpe.caf.api.autoscale.ScalerException;
import java.io.IOException;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class AppInstancePatcher {

    private final static Logger LOG = LoggerFactory.getLogger(AppInstancePatcher.class);
    private final URI marathonUri;

    public AppInstancePatcher(final URI marathonUri){
        this.marathonUri = marathonUri;
    }

    public void patchInstances(final String appId, final int instances) throws ScalerException {
        patchInstances(appId, instances, false);
    }

    private void patchInstances(final String appId, final int instances, final boolean force) throws ScalerException {
        final JsonObject details = new JsonObject();
        details.addProperty("id", appId);
        details.addProperty("instances", instances);

        final JsonArray appArray = new JsonArray();
        appArray.add(details);
        patchInstance(appArray, appId, instances, force);
    }
    
    private void patchInstance(final JsonArray appArray, final String appId, final int instances, final boolean force)
        throws ScalerException
    {
        int count = 0;
        boolean patched = false;
        while (!patched) {
            try (final CloseableHttpClient client = HttpClientBuilder.create().build()) {
                final URIBuilder uriBuilder = new URIBuilder(marathonUri).setPath("/v2/apps");
                uriBuilder.setParameters(Arrays.asList(new BasicNameValuePair("force", Boolean.toString(force))));
                final HttpPatch patch = new HttpPatch(uriBuilder.build());
                patch.setEntity(new StringEntity(appArray.toString(), ContentType.APPLICATION_JSON));
                final HttpResponse response = client.execute(patch);
                if (!force && response.getStatusLine().getStatusCode() == 409) {
                    patchInstances(appId, instances, true);
                    return;
                }
                if (response.getStatusLine().getStatusCode() != 200) {
                    if (response.getStatusLine().getStatusCode() == 503) {
                        LOG.error(String.format("A temporary error occured while attempting to patch service %s. "
                            + "Patch request returned a 503 status, patching will be reattempted.", appId));
                        count++;
                        final int sleepTime = count * 1000;
                        final int timeToSleep = sleepTime < 10000 ? sleepTime : 10000;
                        Thread.sleep(timeToSleep);
                    } else {
                        LOG.error("Response code: " + response.getStatusLine().getStatusCode());
                        LOG.error("Response Phrase: " + response.getStatusLine().getReasonPhrase());
                        throw new ScalerException(response.getStatusLine().getReasonPhrase());
                    }
                }
            } catch (final URISyntaxException | IOException ex) {
                throw new ScalerException(String.format("Exception patching %s to %s instances.", appId, instances), ex);
            } catch (final InterruptedException ex) {
                // Set interupted flag
                Thread.currentThread().interrupt();
                // Throw exception to suppress further calls from current scaler thread until after scaler thread refresh
                throw new ScalerException("An error occured during an attempt to have the main thread sleep beofre retrying patch command",
                                          ex);
            }
            LOG.debug(String.format("Service %s patched successfully.", appId));
            patched = true;
        }
    }
}

@@ -859,16 +859,40 @@
 he.http.
+client.methods.Closeable
 HttpResp
@@ -3000,30 +3000,43 @@
             
+try(
 final 
+Closeable
 HttpResponse
@@ -3068,19 +3068,25 @@
 e(patch)
-;%0D%0A
+) %7B%0D%0A    
         
@@ -3171,32 +3171,36 @@
                 
+    
 patchInstances(a
@@ -3244,16 +3244,20 @@
         
+   
+ 
 return;%0D
@@ -3265,32 +3265,36 @@
                 
+    
 %7D%0D%0A             
@@ -3288,32 +3288,36 @@
                 
+  
+  
 if (response.get
@@ -3352,24 +3352,28 @@
  != 200) %7B%0D%0A
+    
             
@@ -3452,32 +3452,36 @@
                 
+ 
+   
 LOG.error(String
@@ -3584,16 +3584,24 @@
         
+        
 + %22Patch
@@ -3698,16 +3698,20 @@
         
+   
+ 
 count++;
@@ -3712,16 +3712,20 @@
 unt++;%0D%0A
+    
         
@@ -3793,32 +3793,36 @@
                 
+  
+  
 final int timeTo
@@ -3889,24 +3889,28 @@
             
+    
 Thread.sleep
@@ -3945,16 +3945,20 @@
         
+ 
+   
 %7D else %7B
@@ -3955,24 +3955,28 @@
   %7D else %7B%0D%0A
+    
             
@@ -4077,32 +4077,36 @@
                 
+    
 LOG.error(%22Respo
@@ -4183,32 +4183,36 @@
                 
+   
+ 
 throw new Scaler
@@ -4259,32 +4259,59 @@
 asonPhrase());%0D%0A
+                        %7D%0D%0A
                 

fix for unreleased resources (#44)