/*
 * Copyright 2012-2013 eBay Software Foundation and ios-driver committers
 *
 * 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 org.uiautomation.ios.server.instruments.communication;

import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.remote.BeanToJsonConverter;
import org.openqa.selenium.remote.Response;
import org.uiautomation.ios.server.command.UIAScriptRequest;
import org.uiautomation.ios.server.command.UIAScriptResponse;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;

import static org.uiautomation.ios.IOSCapabilities.COMMAND_TIMEOUT_MILLIS;

/**
 * base class for communication with an Instruments process. Assumes asyncronous communication.
 */
public abstract class BaseCommunicationChannel implements CommunicationChannel {

  private static final Logger log = Logger.getLogger(BaseCommunicationChannel.class.getName());

  private final BlockingQueue<UIAScriptResponse> responseQueue = new ArrayBlockingQueue<>(1);

  private final Lock lock = new ReentrantLock();
  private final Condition condition = lock.newCondition();
  private volatile boolean ready = false;
  private final String sessionId;

  public BaseCommunicationChannel(String sessionId) {
    this.sessionId = sessionId;
  }

  protected String getSessionId() {
    return sessionId;
  }

  @Override
  public boolean waitForUIScriptToBeStarted() throws InterruptedException {
    try {
      lock.lock();
      if (ready) {
        return true;
      }
      return condition.await(15, TimeUnit.SECONDS);
    } finally {
      lock.unlock();
    }
  }

  @Override
  public void registerUIAScript() {
    try {
      lock.lock();
      ready = true;
      condition.signal();
    } finally {
      lock.unlock();
    }
  }

  protected void handleLastCommand(UIAScriptRequest request) {
    // Stop is a fire and forget response. It will kill the instruments script,
    // so the script cannot
    // send a response.
    if ("stop".equals(request.getScript())) {
      Response response = new Response();
      response.setSessionId(getSessionId());
      response.setStatus(0);
      response.setValue("ok");
      BeanToJsonConverter converter = new BeanToJsonConverter();
      String json = converter.convert(response);
      UIAScriptResponse r = new UIAScriptResponse(json);
      setNextResponse(r);
    }

  }

  public void setNextResponse(UIAScriptResponse r) {
    try {
      responseQueue.add(r);

    } catch (IllegalStateException e) {
      try {
        System.out.println("ALREADY PRESENT:" + responseQueue.take().getRaw());
        System.out.println("TRY TO ADD:" + r.getRaw());
      } catch (InterruptedException e1) {
        e1.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
      }
      e.printStackTrace();
    }
  }


  protected UIAScriptResponse waitForResponse() {
    try {
      UIAScriptResponse res = responseQueue.poll(COMMAND_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
      if (log.isLoggable(Level.FINE))
        log.fine(String.format("Raw response: %s", res.getRaw()));
      return res;
    } catch (InterruptedException e) {
      throw new WebDriverException("timeout getting the response", e);
    }
  }

}

@@ -2028,16 +2028,22 @@
 otected 
+final 
 String g
@@ -2054,24 +2054,24 @@
 ssionId() %7B%0A
-
     return s
@@ -2106,16 +2106,22 @@
  public 
+final 
 boolean 
@@ -2289,10 +2289,10 @@
 ait(
-15
+30
 , Ti
@@ -2373,24 +2373,30 @@
 de%0A  public 
+final 
 void registe
@@ -2545,16 +2545,22 @@
 otected 
+final 
 void han
@@ -3130,17 +3130,16 @@
 ;%0A    %7D%0A
-%0A
   %7D%0A%0A  p
@@ -3144,16 +3144,22 @@
  public 
+final 
 void set
@@ -3232,17 +3232,16 @@
 add(r);%0A
-%0A
     %7D ca
@@ -3613,17 +3613,16 @@
  %7D%0A  %7D%0A%0A
-%0A
   protec
@@ -3625,16 +3625,22 @@
 otected 
+final 
 UIAScrip
@@ -4015,15 +4015,14 @@
 ;%0A    %7D%0A
+
   %7D%0A
-%0A
 %7D%0A

Increase registerUIAScript timeout to 30 secs for slow VMs.