
package de.ivu.elect.business.system.control;

import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.inject.Inject;
import javax.jws.WebService;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;
import javax.xml.ws.handler.Handler;

public class SoapWebServiceHelper implements Serializable {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient SoapWebServiceLoggingHandler soapWebServiceLoggingHandler;

  public SoapWebServiceHelper() {
    super();
  }

  public Service createService(Class< ? > webServiceClass) {
    WebService webServiceAnnotation = webServiceClass.getAnnotation(WebService.class);

    String namespaceURI = webServiceAnnotation.targetNamespace();
    String localPart = webServiceAnnotation.name();

    return Service.create(new QName(namespaceURI, localPart));
  }

  public void setupServiceEndpoint(BindingProvider serviceEndpoint, URL serviceUrl, List< ? extends AbstractSoapWebServiceHandler> soapWebServiceHandlers, boolean isLoggingEnabled) {
    setupServiceEndpointAddressAndConnectionTimeout(serviceEndpoint, serviceUrl.toString());
    setupServiceEndpointHandlers(serviceEndpoint, soapWebServiceHandlers, isLoggingEnabled);
  }

  private void setupServiceEndpointHandlers(BindingProvider serviceEndpoint, List< ? extends AbstractSoapWebServiceHandler> soapWebServiceHandlers, boolean isLoggingEnabled) {
    serviceEndpoint.getBinding().setHandlerChain(collectWebServiceHandlers(soapWebServiceHandlers, isLoggingEnabled));
  }

  private List<Handler> collectWebServiceHandlers(List< ? extends AbstractSoapWebServiceHandler> soapWebServiceHandlers, boolean isLoggingEnabled) {
    List<Handler> result = new ArrayList<>(soapWebServiceHandlers);

    // IMMER als letzten Handler hinzufügen, um finalen (XML-) Request zu loggen.
    if (isLoggingEnabled) {
      result.add(soapWebServiceLoggingHandler);
    }

    return result;
  }

  private void setupServiceEndpointAddressAndConnectionTimeout(BindingProvider serviceEndpoint, String endpointAddress) {
    Map<String, Object> requestContext = serviceEndpoint.getRequestContext();
    requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointAddress);

    // ELECTMUC-740 Set timeout params - keine Ahnung, ob die alle notwendig sind, es funktioniert aber...
    long connectionTimeoutInMs = getDefaultConnectionTimeoutInMs();

    requestContext.put("com.sun.xml.ws.request.timeout", connectionTimeoutInMs);
    requestContext.put("com.sun.xml.ws.connect.timeout", connectionTimeoutInMs);
    requestContext.put("com.sun.xml.internal.ws.request.timeout", connectionTimeoutInMs);
    requestContext.put("com.sun.xml.internal.ws.connect.timeout", connectionTimeoutInMs);
    requestContext.put("sun.net.client.defaultConnectTimeout", connectionTimeoutInMs);
    requestContext.put("sun.net.client.defaultReadTimeout", connectionTimeoutInMs);
    requestContext.put("org.jboss.ws.timeout", connectionTimeoutInMs);
    requestContext.put("javax.xml.ws.client.connectionTimeout", connectionTimeoutInMs);
    requestContext.put("javax.xml.ws.client.receiveTimeout", connectionTimeoutInMs);
  }

  private long getDefaultConnectionTimeoutInMs() {
    return TimeUnit.DAYS.toMillis(1);
  }
}
