/*
 * Project: e-Dec Example Code
 * (C) 2017 Bundesamt fuer Informatik und Telekommunikation, CH
 */
package ch.admin.bit.edec.examples.ws;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URLConnection;

/**
 * This class provides functionality to execute a HTTP request.<br />
 * Important: HTTP will not work with e-dec as HTTPS is required as a protocol.
 */
public class HttpRequest extends AbstractRequest {
  private static final Log log = LogFactory.getLog(HttpsRequest.class);

  /**
   * Executes the request. All information needed are provided in the connection parameters.
   *
   * @param connParams The parameters needed for making the request.
   * @return The response as text.
   * @throws IOException Thrown if anything out of the ordinary happened.
   */
  public static String doRequest(ConnectionParams connParams) throws IOException {
    StringBuilder responseStringBuilder; // Holds the response
    HttpURLConnection connection = null; // The actual connection

    try {
      log.info("Opening connection...");
      connection = (HttpURLConnection) connParams.getUrl().openConnection();
      log.info("Http connection established.");
    }
    catch (IOException e) { // Something went wrong
      log.error("Couldn't open a connection! " + e.getMessage());
      throw e;
    }

    // Set up HTTP AbstractRequest Headers
    setConnectionParameters(connection, connParams);
    connection.setRequestMethod(connParams.getRequestMethod());

    // send the request
    responseStringBuilder = sendRequest(connection, connParams);

    // close the connection if not done already
    connection = null; // close the connection if not done already

    // Return the response
    return responseStringBuilder.toString();
  }

  /**
   * Handles sending the actual request to the webservice and getting the response.
   *
   * @param connection A working connection.
   * @param connParams The connection parameters.
   * @return Response as text.
   */
  protected static StringBuilder sendRequest(URLConnection connection, ConnectionParams connParams) {
    StringBuilder response = new StringBuilder();
    Writer wr = null;
    BufferedReader reader = null;
    InputStream errorStream = null;

    try {
      // prepare output streams
      OutputStream output = connection.getOutputStream();
      OutputStreamWriter osw = new OutputStreamWriter(output, "UTF-8");
      wr = new BufferedWriter(osw);

      // prepare input streams
      InputStream input = connection.getInputStream();
      reader = new BufferedReader(new InputStreamReader(input));

      // write request
      log.info("  Making request...");
      wr.write(connParams.getRequest());
      wr.flush();
      log.info("  SOAP message successfully sent to " + connParams.getUrl());

      // get response
      log.info("  Forming response...");
      response = createResponse(reader);
    }
    catch (IOException ex) {
      log.error(
          "Received error response or something else went wrong:" + SEP + "Exception Message is: " + ex.getMessage());
      response.append(ex);

      try {
        if (connection instanceof HttpURLConnection) {
          errorStream = ((HttpURLConnection) connection).getErrorStream();
        }
        else if (connection instanceof HttpsURLConnection) {
          errorStream = ((HttpsURLConnection) connection).getErrorStream();
        }
        createError(response, errorStream);
      }
      catch (IOException eex) {
        log.error("Unexpected Error while reading error stream: " + eex.getMessage());
      }
    }
    finally {
      // Clean up
      try {
        if (wr != null) {
          log.debug("Closing writer.");
          wr.close();
        }
      }
      catch (IOException ex) {
      }
      finally {
        wr = null;
      }

      try {
        if (reader != null) {
          log.debug("Closing reader.");
          reader.close();
        }
      }
      catch (IOException ex) {
      }
      finally {
        reader = null;
      }

      try {
        if (errorStream != null) {
          log.debug("Closing error stream.");
          errorStream.close();
        }
      }
      catch (IOException ex) {
      }
      finally {
        errorStream = null;
      }
    }

    return response;
  }

  /**
   * Sets additional header parameters on the connection.
   *
   * @param connection A connection.
   * @param connParams The connection parameters.
   */
  private static void setConnectionParameters(URLConnection connection, ConnectionParams connParams) {
    connection.setDoOutput(true);
    connection.setDoInput(true);
    connection.setRequestProperty("Host", connParams.getHost());
    if (!"".equals(connParams.getSslHeaderAddition())) {
      connection.setRequestProperty("SSL_CLIENT_CERT_S_DN_CN", connParams.getSslHeaderAddition());
    }
    connection.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
    connection.setRequestProperty("Content-Length", "" + connParams.getRequest().length());
    connection.setRequestProperty("SOAPAction", "\"" + connParams.getSoapAction() + "\"");

    log.debug("Connection Host: " + connParams.getHost());
    log.debug("Connection SSL_CLIENT_CERT_S_DN_CN: " + connParams.getSslHeaderAddition());
    log.debug("Connection Content-Type: text/xml; charset=utf-8");
    log.debug("Connection Content-Length: " + connParams.getRequest().length());
    log.debug("Connection SOAPAction: " + connParams.getSoapAction());
    //log.debug("Connection CipherSuite: " + sslConnection.getCipherSuite());
  }
}
