/*****************************************************************************
© 2017 Avaya Inc. All rights reserved. 
****************************************************************************/

package com.avaya.zephyr.services.sample_services.OutboundHttpsSample;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;

import javax.net.ssl.SSLContext;

import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;

import com.avaya.asm.datamgr.DMFactory;
import com.avaya.collaboration.ssl.util.SSLUtilityException;
import com.avaya.collaboration.ssl.util.SSLUtilityFactory;
import com.avaya.collaboration.util.logger.Logger;
import com.avaya.zephyr.platform.dm.ZephyrDM;

public enum HttpClientSingleton implements HttpClientProvider
{
    INSTANCE;
    private HttpClient client;
    private String myFqdnOrIpAddress = null;
    private AttributeReader attributeReader;
    private HttpClientBuilder httpClientBuilder;
    private SSLContext sslContext;
    private Builder requestConfigBuilder;
    private static final Logger LOGGER = Logger.getLogger(HttpClientSingleton.class);

    private HttpClientSingleton()
    {
    }

    @Override
    public synchronized HttpClient reset() throws IOException, SSLUtilityException
    {
        ((CloseableHttpClient) client).close();
        client = null;
        myFqdnOrIpAddress = null;
        myFqdnOrIpAddress = null;
        attributeReader = null;
        sslContext = null;
        requestConfigBuilder = null;
        client = getHttpClientBasedOnSSLContext();
        return client;
    }

    @Override
    public synchronized HttpClient getHttpClientBasedOnSSLContext() throws SSLUtilityException
    {
        if (client != null)
        {
            try
            {
                if (sslContext != null)
                {
                    final String existingProtocol = sslContext.getProtocol();
                    final String newProtocol = SSLUtilityFactory.createSSLContext().getProtocol();
                    if (existingProtocol != null && !existingProtocol.equals(newProtocol))
                    {
                        if (LOGGER.isFinestEnabled())
                        {
                            LOGGER.finest("getHttpClientBasedOnSSLContext: The protocol version changed from " + existingProtocol + " to " +
                                    newProtocol);
                        }
                        ((CloseableHttpClient) client).close();
                        sslContext = null;
                        client = null;
                    }
                }
                else
                {
                    ((CloseableHttpClient) client).close();
                    client = null;
                }
            }
            catch (final IOException e)
            {
                LOGGER.error("Caught IOException");
                sslContext = null;
                client = null;
            }
        }
        if (client == null)
        {
            final AttributeReader attReader = getAttributeReader();
            RequestConfig requestConfig;
            try
            {
                requestConfig = getRequestConfigBuilder()
                        .setLocalAddress(InetAddress.getByName(getMyFqdnOrIpAddress()))
                        .setConnectionRequestTimeout(attReader.getHttpConnectionRequestTimeout())
                        .setConnectTimeout(attReader.getHttpConnectTimeout())
                        .setSocketTimeout(attReader.getHttpSocketTimeout()).build();
                final SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
                        getSSLContext(),
                        NoopHostnameVerifier.INSTANCE);
                final Registry<ConnectionSocketFactory> registry = RegistryBuilder
                        .<ConnectionSocketFactory> create()
                        .register("http", PlainConnectionSocketFactory.getSocketFactory())
                        .register("https", sslConnectionSocketFactory)
                        .build();
                final HttpClientConnectionManager cm = new BasicHttpClientConnectionManager(registry);
                client = getHttpClientBuilder().setConnectionManager(cm).setDefaultRequestConfig(requestConfig)
                        .setMaxConnTotal(attReader.getHttpMaxTotalConnections())
                        .setMaxConnPerRoute(attReader.getHttpMaxConnectionsPerHost())
                        .build();
            }
            catch (final UnknownHostException e)
            {
                LOGGER.error("Caught UnknownHostException");
            }
        }
        return client;
    }

    final String getMyFqdnOrIpAddress()
    {
        if (myFqdnOrIpAddress == null)
        {
            final ZephyrDM dm = (ZephyrDM) DMFactory.getInstance().getDataMgr(
                    ZephyrDM.class);
            if (null != dm.getMySIPEntity())
            {
                myFqdnOrIpAddress = dm.getMySIPEntity().getFqdnoripaddr();
            }
            else
            {
                // For Junits
                myFqdnOrIpAddress = "zang.io.com";
            }

        }
        return myFqdnOrIpAddress;
    }

    AttributeReader getAttributeReader()
    {
        if (attributeReader == null)
        {
            attributeReader = new AttributeReaderImpl();
        }
        return attributeReader;
    }

    HttpClientBuilder getHttpClientBuilder()
    {
        if (httpClientBuilder == null)
        {
            httpClientBuilder = HttpClients.custom().useSystemProperties();
        }
        return httpClientBuilder;
    }

    SSLContext getSSLContext() throws SSLUtilityException
    {
        if (sslContext == null)
        {
            sslContext = SSLUtilityFactory.createSSLContext();
        }
        return sslContext;
    }

    Builder getRequestConfigBuilder()
    {
        if (requestConfigBuilder == null)
        {
            requestConfigBuilder = RequestConfig.custom();
        }
        return requestConfigBuilder;
    }

}
