/*
 * Copyright 2011 NEHTA
 *
 * Licensed under the NEHTA Open Source (Apache) License; you may not use this
 * file except in compliance with the License. A copy of the License is in the
 * 'license.txt' file, which should be provided with this work.
 *
 * 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 au.gov.nehta.vendorlibrary.hi.hpio;

import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLSocketFactory;
import javax.xml.ws.Binding;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Holder;
import javax.xml.ws.handler.Handler;

import au.gov.nehta.common.utils.ArgumentUtils;
import au.gov.nehta.vendorlibrary.hi.handler.message.HIHeaderHandler;
import au.gov.nehta.vendorlibrary.hi.handler.security.HISecurityHandler;
import au.gov.nehta.vendorlibrary.ws.TimeUtility;
import au.gov.nehta.vendorlibrary.ws.WebServiceClientUtil;
import au.gov.nehta.vendorlibrary.ws.handler.LoggingHandler;
import au.net.electronichealth.ns.hi.providercore.address._3_2_0.AustralianAddressCriteriaType;
import au.net.electronichealth.ns.hi.providercore.address._3_2_0.InternationalAddressCriteriaType;
import au.net.electronichealth.ns.hi.svc.hiproviderdirectoryfororganisation._3_2_0.SearchHIProviderDirectoryForOrganisation;
import au.net.electronichealth.ns.hi.svc.hiprovidersearchorg._5_0_0.ProviderSearchForProviderOrganisationPortType;
import au.net.electronichealth.ns.hi.svc.hiprovidersearchorg._5_0_0.ProviderSearchForProviderOrganisationService;
import au.net.electronichealth.ns.hi.svc.hiprovidersearchorg._5_0_0.SearchForProviderOrganisation;
import au.net.electronichealth.ns.hi.svc.hiprovidersearchorg._5_0_0.SearchForProviderOrganisationResponse;
import au.net.electronichealth.ns.hi.svc.hiprovidersearchorg._5_0_0.StandardErrorMsg;
import au.net.electronichealth.ns.hi.xsd.common.commoncoreelements._3.ProductType;
import au.net.electronichealth.ns.hi.xsd.common.commoncoreelements._3.SignatureContainerType;
import au.net.electronichealth.ns.hi.xsd.common.commoncoreelements._3.TimestampType;
import au.net.electronichealth.ns.hi.xsd.common.qualifiedidentifier._3.QualifiedId;

/**
 * An implementation of a  Provider Search For Provider Organisation (HPI-O) search client.
 * This class may be used to connect to the Medicare HI Service to do Provider Search For
 * Organisation searches.
 */
public class ProviderSearchForProviderOrganisationClient {

      /**
       * Empty String variable.
       */
      public static final String EMPTY = "";
    
      /**
       * SSL socket factory class name
       */
      private static final String SSL_SOCKET_FACTORY = "com.sun.xml.ws.transport.https.client.SSLSocketFactory";
    
      

      /**
       * Validates the arguments passed for the various operations
       */
      //private ArgumentValidator argumentValidator = new ArgumentValidator();
    
      /**
       * The SSL Socket Factory to be used for connecting to the Web Service provider
       */
      private SSLSocketFactory sslSocketFactory;
    
      /**
       * CSP HPI-O header name.
       */
      private static final String HPIO_CSP_HEADER_ELEMENT_NAME = "hpio";
    
      /**
       * The Private Key to be used for Signing
       */
      private PrivateKey signingPrivateKey;
    
      /**
       * The Certificate to be used for Signing
       */
      private X509Certificate signingCertificate;
    
      /**
       * The user Qualified ID associated with this use of the ProviderSearchHIProviderDirectoryForOrganisation service
       */
      private final QualifiedId individualQualifiedId;
    
      /**
       * The organisation Qualified ID associated with this use of the ProviderSearchHIProviderDirectoryForOrganisation service
       */
      private final QualifiedId organisationQualifiedId;
    
      /**
       * The Product details associated with this use of the ProviderSearchHIProviderDirectoryForIndividual service
       */
      private final Holder<ProductType> productHeader;
    
      /**
       * The Web Services port for the ProviderSearchHIProviderDirectoryForIndividual service
       */
      private ProviderSearchForProviderOrganisationPortType port;
    
  
    
      /**
       * The Logging handler instance for logging SOAP request and Response messages.
       */
      private LoggingHandler loggingHandler;
    

      /**
       * Constructor which creates a new Client with an endpoint and an SSL Socket Factory.
       *
       * @param serviceEndpoint       the Web Service endpoint for the Medicare HI Service interface (Mandatory)
       * @param individualQualifiedId The qualified user ID for connecting to the ProviderSearchHIProviderDirectoryForOrganisation service (Mandatory)
       * @param productHeader         The product header data for connecting to the ProviderSearchHIProviderDirectoryForOrganisation service (Mandatory)
       * @param signingPrivateKey     The private key to be used for signing (Mandatory)
       * @param signingCertificate    The certificate to be used for signing (Mandatory)
       * @param sslSocketFactory      the SSL Socket Factory to be used when connecting to the Web Service provider
       */
      public ProviderSearchForProviderOrganisationClient(final String serviceEndpoint,
                        final QualifiedId individualQualifiedId,
                        final Holder<ProductType> productHeader,
                        final PrivateKey signingPrivateKey,
                        final X509Certificate signingCertificate,
                        final SSLSocketFactory sslSocketFactory) {
        this(
          serviceEndpoint,
          individualQualifiedId,
          null,
          productHeader,
          signingPrivateKey,
          signingCertificate,
          sslSocketFactory
        );
      }

      /**
       * Constructor which creates a new ProviderSearchForProviderOrganisation Client with an endpoint and an SSL Socket Factory, with
       * the optional contracted service providers HPI-O organisation qualified ID set.
       *
       * @param serviceEndpoint         the Web Service endpoint for the Medicare HI Service interface (Mandatory)
       * @param individualQualifiedId   The qualified user ID for connecting to the ProviderSearchHIProviderDirectoryForOrganisation service (Mandatory)
       * @param organisationQualifiedId The qualified organisation ID for connecting to the ProviderSearchHIProviderDirectoryForOrganisation
       *                                service (Optional)
       * @param productHeader           The product header data for connecting to the ProviderSearchHIProviderDirectoryForOrganisation service (Mandatory)
       * @param signingPrivateKey       The private key to be used for signing (Mandatory)
       * @param signingCertificate      The certificate to be used for signing (Mandatory)
       * @param sslSocketFactory        the SSL Socket Factory to be used when connecting to the Web Service provider
       */
      public ProviderSearchForProviderOrganisationClient(final String serviceEndpoint,
                            final QualifiedId individualQualifiedId,
                            final QualifiedId organisationQualifiedId,
                            final Holder<ProductType> productHeader,
                            final PrivateKey signingPrivateKey,
                            final X509Certificate signingCertificate,
                            final SSLSocketFactory sslSocketFactory) {

        ArgumentUtils.checkNotNullNorBlank( serviceEndpoint, "serviceEndpoint" );
        ArgumentUtils.checkNotNull( individualQualifiedId, "individualQualifiedId" );
        ArgumentUtils.checkNotNull( productHeader, "productHeader" );
        ArgumentUtils.checkNotNull( signingPrivateKey, "signingPrivateKey" );
        ArgumentUtils.checkNotNull( signingCertificate, "signingPrivateKey" );
        ArgumentUtils.checkNotNull( sslSocketFactory, "sslSocketFactory" );

        this.sslSocketFactory = sslSocketFactory;
        this.individualQualifiedId = individualQualifiedId;
        this.organisationQualifiedId = organisationQualifiedId;
        this.productHeader = productHeader;

        this.signingPrivateKey = signingPrivateKey;
        this.signingCertificate = signingCertificate;
        this.loggingHandler = new LoggingHandler( true ); // Set true to dump
                                                          // the SOAP message to
                                                          // the default logger.

        @SuppressWarnings("rawtypes")
        List<Handler> handlerChain = new ArrayList<Handler>();
        handlerChain.add( loggingHandler );
        this.port = WebServiceClientUtil.getPort(
          ProviderSearchForProviderOrganisationPortType.class,
          ProviderSearchForProviderOrganisationService.class,
          sslSocketFactory,
          handlerChain);
        configureEndpoint(this.port, serviceEndpoint);
    }

    /**
     * Executes an Identifier HPI-O search.
     * 
     * @param request
     *            the SearchHIProviderDirectoryForIndividual request object
     *            containing the following mandatory fields: HPI-O and the
     *            following optional fields: Link Search Type
     * @return
     * @return the response from the SearchHIProviderDirectoryForIndividual
     *         service
     * @throws StandardErrorMsg
     *             if the Web Service call fails.
     * @throws
     */
    public final SearchForProviderOrganisationResponse identifierSearch( SearchForProviderOrganisation request ) throws StandardErrorMsg {
        ArgumentUtils.checkNotNull(request, "request");
        ArgumentUtils.checkNotNullNorBlank(request.getHpioNumber(), "HPI-O Number");

        Holder<SignatureContainerType> signatureHeader = null;

        return this.port.searchForProviderOrganisation( request, this.productHeader, getTimestampHeader(), signatureHeader, this.individualQualifiedId, this.organisationQualifiedId );
    }

    

    // Helper Methods
    
    /**
     * Returns the current
     * {@link au.net.electronichealth.ns.hi.common.commoncoreelements._3_0.TimestampType}
     * 
     * @return {@link au.net.electronichealth.ns.hi.common.commoncoreelements._3_0.TimestampType}
     *         instance with current as created time.
     */
    private TimestampType getTimestampHeader() {
        TimestampType timestampHeader = new TimestampType();
        timestampHeader.setCreated( TimeUtility.nowXMLGregorianCalendar() );
        return timestampHeader;
    }
    

    /**
     * Configure the endpoint using the provided configuration information.
     * 
     * @param servicePort
     *            the service definition. (Mandatory)
     * @param endpoint
     *            the URL for the Web service endpoint. (Mandatory)
     */
    private void configureEndpoint( final Object servicePort, final String endpoint ) {
        final BindingProvider bindingProvider = (BindingProvider) servicePort;

        // Set details on request context
        final Map<String, Object> requestContext = bindingProvider.getRequestContext();
        requestContext.put( BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpoint.trim() );

        requestContext.put( SSL_SOCKET_FACTORY, this.sslSocketFactory );

        if (bindingProvider != null && bindingProvider.getBinding() != null) {
            Binding binding = bindingProvider.getBinding();

            // Get handler chain
            @SuppressWarnings("rawtypes")
            List<Handler> handlerChain = binding.getHandlerChain();

            // Remove hpioHeader if value is not specified.
            if (this.organisationQualifiedId == null) {
                List<String> headerNames = new ArrayList<String>();
                headerNames.add( HPIO_CSP_HEADER_ELEMENT_NAME );
                handlerChain.add( new HIHeaderHandler( headerNames ) );
            }

            // Add HISecurityHandler to sign the Outoging SOAP message and
            // verify the incoming SOAP message.
            handlerChain.add( new HISecurityHandler( this.signingCertificate, this.signingPrivateKey ) );

            // Add handler to capture inbound and outbound SOAP messages
            handlerChain.add( this.loggingHandler );
            binding.setHandlerChain( handlerChain );
        }
    }

    /**
     * Getter for lastSoapResponse.
     * 
     * @return lastSoapResponse the lastSoapResponse instance variable
     */
    public final String getLastSoapResponse() {
        if (loggingHandler != null) {
            return loggingHandler.getLastSoapResponse();
        } else {
            return EMPTY;
        }
    }

    /**
     * Getter for lastSoapRequest.
     * 
     * @return lastSoapRequest the lastSoapRequest instance variable (Mandatory)
     */
    public final String getLastSoapRequest() {
        if (loggingHandler != null) {
            return loggingHandler.getLastSoapRequest();
        } else {
            return EMPTY;
        }
    }

    /**
     * returns the current JAX-WS port object
     * @return the port
     */
    public ProviderSearchForProviderOrganisationPortType getPort() {
        return port;
    }
}
