/**
   * The main RESTful GET method; the other one ({@link #make_restful_get(String, String, int)})
   * calls this one with a pre-populated logging parameter.
   *
   * @param baseUrl A {@link String} URL to request from.
   * @param tag A {@link String} tag for logging/analytics purposes.
   * @param timeout An {@link Integer} value containing the number of milliseconds to wait before
   *     considering a server request to have timed out.
   * @param log A {@link Boolean} value that specifies whether debug logging should be enabled for
   *     this request or not.
   * @return A {@link ServerResponse} object containing the result of the RESTful request.
   */
  private ServerResponse make_restful_get(
      String baseUrl, String tag, int timeout, int retryNumber, boolean log) {
    String modifiedUrl = baseUrl;
    JSONObject getParameters = new JSONObject();
    HttpsURLConnection connection = null;
    if (timeout <= 0) {
      timeout = DEFAULT_TIMEOUT;
    }
    if (addCommonParams(getParameters, retryNumber)) {
      modifiedUrl += this.convertJSONtoString(getParameters);
    } else {
      return new ServerResponse(tag, NO_BRANCH_KEY_STATUS);
    }

    try {
      if (log) PrefHelper.Debug("BranchSDK", "getting " + modifiedUrl);
      URL urlObject = new URL(modifiedUrl);
      connection = (HttpsURLConnection) urlObject.openConnection();
      connection.setConnectTimeout(timeout);
      connection.setReadTimeout(timeout);

      if (connection.getResponseCode() >= 500 && retryNumber < prefHelper_.getRetryCount()) {
        try {
          Thread.sleep(prefHelper_.getRetryInterval());
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        retryNumber++;
        return make_restful_get(baseUrl, tag, timeout, retryNumber, log);
      } else {
        if (connection.getResponseCode() != HttpURLConnection.HTTP_OK
            && connection.getErrorStream() != null) {
          return processEntityForJSON(
              connection.getErrorStream(), connection.getResponseCode(), tag, log, null);
        } else {
          return processEntityForJSON(
              connection.getInputStream(), connection.getResponseCode(), tag, log, null);
        }
      }
    } catch (SocketException ex) {
      if (log)
        PrefHelper.Debug(getClass().getSimpleName(), "Http connect exception: " + ex.getMessage());
      return new ServerResponse(tag, NO_CONNECTIVITY_STATUS);
    } catch (UnknownHostException ex) {
      if (log)
        PrefHelper.Debug(getClass().getSimpleName(), "Http connect exception: " + ex.getMessage());
      return new ServerResponse(tag, NO_CONNECTIVITY_STATUS);
    } catch (IOException ex) {
      if (log) PrefHelper.Debug(getClass().getSimpleName(), "IO exception: " + ex.getMessage());
      return new ServerResponse(tag, 500);
    } finally {
      if (connection != null) {
        connection.disconnect();
      }
    }
  }
  /**
   * The main RESTful GET method; the other one ({@link #make_restful_get(String, String, int)})
   * calls this one with a pre-populated logging parameter.
   *
   * @param baseUrl A {@link String} URL to request from.
   * @param tag A {@link String} tag for logging/analytics purposes.
   * @param timeout An {@link Integer} value containing the number of milliseconds to wait before
   *     considering a server request to have timed out.
   * @param log A {@link Boolean} value that specifies whether debug logging should be enabled for
   *     this request or not.
   * @return A {@link ServerResponse} object containing the result of the RESTful request.
   */
  private ServerResponse make_restful_get(
      String baseUrl, String tag, int timeout, int retryNumber, boolean log) {
    String modifiedUrl = baseUrl;
    JSONObject getParameters = new JSONObject();
    if (addCommonParams(getParameters, retryNumber)) {
      modifiedUrl += this.convertJSONtoString(getParameters);
    } else {
      return new ServerResponse(tag, NO_BRANCH_KEY_STATUS);
    }

    try {
      if (log) PrefHelper.Debug("BranchSDK", "getting " + modifiedUrl);
      HttpGet request = new HttpGet(modifiedUrl);
      HttpResponse response = getGenericHttpClient(timeout).execute(request);
      if (response.getStatusLine().getStatusCode() >= 500
          && retryNumber < prefHelper_.getRetryCount()) {
        try {
          Thread.sleep(prefHelper_.getRetryInterval());
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        retryNumber++;
        return make_restful_get(baseUrl, tag, timeout, retryNumber, log);
      } else {
        return processEntityForJSON(
            response.getEntity(), response.getStatusLine().getStatusCode(), tag, log, null);
      }

    } catch (ClientProtocolException ex) {
      if (log)
        PrefHelper.Debug(
            getClass().getSimpleName(), "Client protocol exception: " + ex.getMessage());
    } catch (SocketException ex) {
      if (log)
        PrefHelper.Debug(getClass().getSimpleName(), "Http connect exception: " + ex.getMessage());
      return new ServerResponse(tag, NO_CONNECTIVITY_STATUS);
    } catch (UnknownHostException ex) {
      if (log)
        PrefHelper.Debug(getClass().getSimpleName(), "Http connect exception: " + ex.getMessage());
      return new ServerResponse(tag, NO_CONNECTIVITY_STATUS);
    } catch (IOException ex) {
      if (log) PrefHelper.Debug(getClass().getSimpleName(), "IO exception: " + ex.getMessage());
      return new ServerResponse(tag, 500);
    }
    return null;
  }
  /**
   * The main RESTful POST method. The others call this one with pre-populated parameters.
   *
   * @param body A {@link JSONObject} containing the main data body/payload of the request.
   * @param url A {@link String} URL to request from.
   * @param tag A {@link String} tag for logging/analytics purposes.
   * @param timeout An {@link Integer} value containing the number of milliseconds to wait before
   *     considering a server request to have timed out.
   * @param log A {@link Boolean} value that specifies whether debug logging should be enabled for
   *     this request or not.
   * @param linkData A {@link BranchLinkData} object containing the data dictionary associated with
   *     the link subject of the original server request.
   * @return A {@link ServerResponse} object representing the {@link HttpEntity} response in Branch
   *     SDK terms.
   */
  private ServerResponse make_restful_post(
      JSONObject body,
      String url,
      String tag,
      int timeout,
      int retryNumber,
      boolean log,
      BranchLinkData linkData) {
    try {
      JSONObject bodyCopy = new JSONObject();
      Iterator<?> keys = body.keys();
      while (keys.hasNext()) {
        String key = (String) keys.next();
        try {
          bodyCopy.put(key, body.get(key));
        } catch (JSONException e) {
          e.printStackTrace();
        }
      }

      if (!addCommonParams(bodyCopy, retryNumber)) {
        return new ServerResponse(tag, NO_BRANCH_KEY_STATUS);
      }
      if (log) {
        PrefHelper.Debug("BranchSDK", "posting to " + url);
        PrefHelper.Debug("BranchSDK", "Post value = " + bodyCopy.toString(4));
      }
      HttpPost request = new HttpPost(url);
      request.setEntity(new ByteArrayEntity(bodyCopy.toString().getBytes("UTF8")));
      request.setHeader("Content-type", "application/json");
      HttpResponse response = getGenericHttpClient(timeout).execute(request);
      if (response.getStatusLine().getStatusCode() >= 500
          && retryNumber < prefHelper_.getRetryCount()) {
        try {
          Thread.sleep(prefHelper_.getRetryInterval());
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        retryNumber++;
        return make_restful_post(bodyCopy, url, tag, timeout, retryNumber, log, linkData);
      } else {
        ServerResponse serverResponse =
            processEntityForJSON(
                response.getEntity(), response.getStatusLine().getStatusCode(), tag, log, linkData);
        serverResponse.setRequestObject(body);
        return serverResponse;
      }
    } catch (SocketException ex) {
      if (log)
        PrefHelper.Debug(getClass().getSimpleName(), "Http connect exception: " + ex.getMessage());
      return new ServerResponse(tag, NO_CONNECTIVITY_STATUS);
    } catch (UnknownHostException ex) {
      if (log)
        PrefHelper.Debug(getClass().getSimpleName(), "Http connect exception: " + ex.getMessage());
      return new ServerResponse(tag, NO_CONNECTIVITY_STATUS);
    } catch (Exception ex) {
      if (log) PrefHelper.Debug(getClass().getSimpleName(), "Exception: " + ex.getMessage());
      if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
        if (ex instanceof NetworkOnMainThreadException)
          Log.i(
              "BranchSDK",
              "Branch Error: Don't call our synchronous methods on the main thread!!!");
      }
      return new ServerResponse(tag, 500);
    }
  }
  /**
   * The main RESTful POST method. The others call this one with pre-populated parameters.
   *
   * @param body A {@link JSONObject} containing the main data body/payload of the request.
   * @param url A {@link String} URL to request from.
   * @param tag A {@link String} tag for logging/analytics purposes.
   * @param timeout An {@link Integer} value containing the number of milliseconds to wait before
   *     considering a server request to have timed out.
   * @param log A {@link Boolean} value that specifies whether debug logging should be enabled for
   *     this request or not.
   * @param linkData A {@link BranchLinkData} object containing the data dictionary associated with
   *     the link subject of the original server request.
   * @return A {@link ServerResponse} object representing the {@link HttpURLConnection} response in
   *     Branch SDK terms.
   */
  private ServerResponse make_restful_post(
      JSONObject body,
      String url,
      String tag,
      int timeout,
      int retryNumber,
      boolean log,
      BranchLinkData linkData) {
    HttpURLConnection connection = null;
    if (timeout <= 0) {
      timeout = DEFAULT_TIMEOUT;
    }
    try {
      JSONObject bodyCopy = new JSONObject();
      Iterator<?> keys = body.keys();
      while (keys.hasNext()) {
        String key = (String) keys.next();
        try {
          bodyCopy.put(key, body.get(key));
        } catch (JSONException e) {
          e.printStackTrace();
        }
      }

      if (!addCommonParams(bodyCopy, retryNumber)) {
        return new ServerResponse(tag, NO_BRANCH_KEY_STATUS);
      }
      if (log) {
        PrefHelper.Debug("BranchSDK", "posting to " + url);
        PrefHelper.Debug("BranchSDK", "Post value = " + bodyCopy.toString(4));
      }
      URL urlObject = new URL(url);
      connection = (HttpURLConnection) urlObject.openConnection();
      connection.setConnectTimeout(timeout);
      connection.setReadTimeout(timeout);
      connection.setDoInput(true);
      connection.setDoOutput(true);
      connection.setRequestProperty("Content-Type", "application/json");
      connection.setRequestProperty("Accept", "application/json");
      connection.setRequestMethod("POST");

      OutputStreamWriter outputStreamWriter = new OutputStreamWriter(connection.getOutputStream());
      outputStreamWriter.write(bodyCopy.toString());
      outputStreamWriter.flush();

      if (connection.getResponseCode() >= HttpURLConnection.HTTP_INTERNAL_ERROR
          && retryNumber < prefHelper_.getRetryCount()) {
        try {
          Thread.sleep(prefHelper_.getRetryInterval());
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        retryNumber++;
        return make_restful_post(bodyCopy, url, tag, timeout, retryNumber, log, linkData);
      } else {
        if (connection.getResponseCode() != HttpURLConnection.HTTP_OK
            && connection.getErrorStream() != null) {
          return processEntityForJSON(
              connection.getErrorStream(), connection.getResponseCode(), tag, log, linkData);
        } else {
          return processEntityForJSON(
              connection.getInputStream(), connection.getResponseCode(), tag, log, linkData);
        }
      }

    } catch (SocketException ex) {
      if (log)
        PrefHelper.Debug(getClass().getSimpleName(), "Http connect exception: " + ex.getMessage());
      return new ServerResponse(tag, NO_CONNECTIVITY_STATUS);
    } catch (UnknownHostException ex) {
      if (log)
        PrefHelper.Debug(getClass().getSimpleName(), "Http connect exception: " + ex.getMessage());
      return new ServerResponse(tag, NO_CONNECTIVITY_STATUS);
    } catch (Exception ex) {
      if (log) PrefHelper.Debug(getClass().getSimpleName(), "Exception: " + ex.getMessage());
      if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
        if (ex instanceof NetworkOnMainThreadException)
          Log.i(
              "BranchSDK",
              "Branch Error: Don't call our synchronous methods on the main thread!!!");
      }
      return new ServerResponse(tag, 500);
    } finally {
      if (connection != null) {
        connection.disconnect();
      }
    }
  }