package org.radargun.stages;

import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Base64;
import java.util.List;

import org.radargun.DistStageAck;
import org.radargun.StageResult;
import org.radargun.config.Property;
import org.radargun.config.Stage;
import org.radargun.state.WorkerState;

@Stage(doc = "Allows to invoke Http methods.")
public class HttpInvocationStage extends AbstractDistStage {

   public enum AuthenticationMechanism {
      BASIC
   }

   @Property(optional = false, doc = "Url")
   private String url;

   @Property(optional = true, doc = "Authentication Mechanism")
   private AuthenticationMechanism authMechanism;

   @Property(optional = true, doc = "Expected Http Response body")
   private String expectedResult;

   @Override
   public DistStageAck executeOnWorker() {
      String result;
      try {
         URL javaUrl = new URL(url);
         URLConnection urlConnection = javaUrl.openConnection();
         if (javaUrl.getUserInfo() != null) {
            if (AuthenticationMechanism.BASIC.equals(authMechanism)) {
               String userEncoded = Base64.getEncoder().encodeToString(javaUrl.getUserInfo().getBytes());
               urlConnection.setRequestProperty("Authorization", "Basic " + userEncoded);
            }
         }
         try (InputStreamReader isr = new InputStreamReader(urlConnection.getInputStream())) {
            int numCharsRead;
            char[] charArray = new char[1024];
            StringBuffer sb = new StringBuffer();
            while ((numCharsRead = isr.read(charArray)) > 0) {
               sb.append(charArray, 0, numCharsRead);
            }
            result = sb.toString();
         }
      } catch (MalformedURLException e) {
         return errorResponse("Malformed URL", e);
      } catch (IOException e) {
         return errorResponse(e.getMessage(), e);
      }
      return new HttpInvocationAck(workerState, result);
   }

   @Override
   public StageResult processAckOnMain(List<DistStageAck> acks) {
      StageResult stageResult = super.processAckOnMain(acks);
      if (stageResult.isError()) {
         return stageResult;
      }
      String resultFromWorker;
      if (expectedResult != null && !expectedResult.equals((resultFromWorker = getResultFromWorker(acks)))) {
         log.error(String.format("The value '%s' returned by operation is not the expected value '%s'.", resultFromWorker, expectedResult));
         return StageResult.FAIL;
      }
      return StageResult.SUCCESS;
   }

   private String getResultFromWorker(List<DistStageAck> acks) {
      for (DistStageAck dack : acks) {
         if (dack instanceof HttpInvocationAck) {
            HttpInvocationAck ack = (HttpInvocationAck) dack;
            return ack.result;
         }
      }
      return null;
   }

   private static class HttpInvocationAck extends DistStageAck {

      private final String result;

      public HttpInvocationAck(WorkerState workerState, String result) {
         super(workerState);
         this.result = result;
      }
   }
}

@@ -856,16 +856,103 @@
 esult;%0A%0A
+   @Property(doc = %22Print response. Default false%22)%0A   private boolean printResponse;%0A%0A
    @Over
@@ -1857,24 +1857,104 @@
 toString();%0A
+            if (printResponse) %7B%0A               log.info(result);%0A            %7D%0A
          %7D%0A 

Print Response for Http Invocation Stage