/*
 ************************************************************************************
 * Copyright (C) 2016 Openbravo S.L.U.
 * Licensed under the Openbravo Commercial License version 1.0
 * You may obtain a copy of the License at http://www.openbravo.com/legal/obcl.html
 * or in the legal folder of this module distribution.
 ************************************************************************************
 */
package com.openbravo.but.integrationplatform.webservices;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import javax.servlet.ServletException;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.openbravo.base.exception.OBException;
import org.openbravo.erpCommon.utility.OBMessageUtils;
import org.openbravo.utils.FormatUtilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * Abstract class to implement calls to WebServices.
 *
 */
public abstract class WsClient {

  private static final Logger log = LoggerFactory.getLogger(WsClient.class);

  /**
   * Method to create a connection to a WebService. After using this method the connection created
   * remains opened, so must
   * 
   * @param urlStr
   *          URL of the WebService. Has to contain the protocol e.g. "http://"
   * @param method
   *          method of the WebService. Most frequent are GET or POST.
   * @param user
   *          user to authenticate. Can be null.
   * @param password
   *          password to authenticate. Can be null.
   * @param showStacktrace
   *          boolean indicating if Stacktrace must be shown in log. Can be null.
   */
  protected void connect(String urlStr, String method, String user, String password,
      boolean showStacktrace) {
    InputStream inputStream = null;
    HttpURLConnection connection = null;
    try {
      URL url = new URL(urlStr);
      connection = (HttpURLConnection) url.openConnection();
      connection.setRequestMethod(method);
      if (StringUtils.isNotEmpty(user) && StringUtils.isNotEmpty(password)) {
        String userPassword = user + ":" + passwordValidation(password);
        byte[] encoding = Base64.encodeBase64(userPassword.getBytes());
        connection.setRequestProperty("Authorization", "Basic " + (new String(encoding)));
      }
      connection.setRequestProperty("Content-Type", "application/json");
      connection.setDoOutput(true);
      connection.connect();

      if (StringUtils.isNotEmpty(method) && !method.equals("GET")) {
        // Write the data to send
        OutputStream ouput = connection.getOutputStream();
        writeRequestData(ouput);
        ouput.close();
      }

      // Web Service response
      if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
        inputStream = connection.getErrorStream();
      } else {
        inputStream = connection.getInputStream();
      }
      readRequestResponse(inputStream);
      if (inputStream != null) {
        inputStream.close();
      }
    } catch (IOException e) {
      if (showStacktrace) {
        throw new OBException(e.getLocalizedMessage(), e);
      } else {
        log.debug(OBMessageUtils.messageBD("BUTINTP_connection_error") + " " + e.getMessage());
        throw new OBException(e.getLocalizedMessage(), false);
      }
    } finally {
      if (connection != null) {
        connection.disconnect();
      }
    }
  }

  /**
   * Method that writes in the outputStream the data to send to the WebService.
   * 
   * @param ouput
   *          The output stream to use.
   */
  protected abstract void writeRequestData(OutputStream ouput);

  /**
   * Method that reads from the inputStream the data that the WebService responds.
   * 
   * @param inputStream
   *          The inputStream to read the response from.
   */
  protected abstract void readRequestResponse(InputStream inputStream);

  /**
   * Decrypts password
   * 
   * @param password
   *          A password decryptable
   * @return the password decrypted
   */
  protected String passwordValidation(String password) {
    try {
      return FormatUtilities.encryptDecrypt(password, false);
    } catch (ServletException e) {
      throw new OBException("Error decrypting password", e);
    }
  }

}
