package eu.dnetlib.data.download;

import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import javax.net.ssl.*;
import javax.xml.ws.wsaddressing.W3CEndpointReference;

import eu.dnetlib.data.download.rmi.DownloadItem;
import eu.dnetlib.data.download.rmi.DownloadService;
import eu.dnetlib.data.download.rmi.DownloadServiceException;
import eu.dnetlib.data.download.rmi.DownloadServiceFeeder;
import eu.dnetlib.data.objectstore.rmi.ObjectStoreServiceException;
import eu.dnetlib.enabling.tools.AbstractBaseService;
import eu.dnetlib.enabling.tools.blackboard.NotificationHandler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.beans.factory.annotation.Value;

/**
 * The Class DownloadServiceImpl.
 */
public class DownloadServiceImpl extends AbstractBaseService implements DownloadService {

	/**
	 * The Constant END_QUEUE.
	 */
	public static final DownloadItem END_QUEUE = new DownloadItem();

	public static final String END_QUEUE_STRING = "END_DOWNLOAD";

	/**
	 * The Constant log.
	 */
	private static final Log log = LogFactory.getLog(DownloadServiceImpl.class);
	private static int DEFAULT_CONNECT_TIMEOUT_MS = 5000;
	private static int DEFAULT_READ_TIMEOUT_MS = 5000;
	/**
	 * The download plugin enumerator.
	 */
	@Resource
	private DownloadPluginEnumeratorImpl downloadPluginEnumerator;
	@Resource
	private DownloadServiceFeeder downloadfeeder;
	/**
	 * The notification handler.
	 */
	private NotificationHandler notificationHandler;
	@Value("${services.download.http.sslcheck}")
	private boolean checkCerts;
	@Value("${services.download.https.protocols}")
	private String httpsProtocols;

	/*
	 * (non-Javadoc)
	 *
	 * @see eu.dnetlib.data.download.rmi.DownloadService#downloadFromResultSet(javax.xml.ws.wsaddressing.W3CEndpointReference,
	 * java.lang.String, java.lang.String, java.lang.String, java.lang.String)
	 */
	@Override
	public void downloadFromResultSet(final W3CEndpointReference resultSet,
			final String plugin,
			final String objectStoreID,
			final String protocol,
			final String mimeType) throws DownloadServiceException {

		log.info(String.format("download using plugin '%s' , protocol '%s' into ObjectStore '%s'", plugin, protocol, objectStoreID));
		try {
			downloadfeeder.download(resultSet.toString(), plugin, objectStoreID, protocol, mimeType, 5, null, null, DEFAULT_CONNECT_TIMEOUT_MS,
					DEFAULT_READ_TIMEOUT_MS, 0);
		} catch (ObjectStoreServiceException e) {
			log.error(e);
		}
		log.info(String.format("download completed using plugin '%s' , protocol '%s' into ObjectStore '%s'", plugin, protocol, objectStoreID));
	}

	/**
	 * {@inheritDoc}
	 *
	 * @see eu.dnetlib.enabling.tools.AbstractBaseService#notify(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
	 */
	@Override
	public void notify(final String subscriptionId, final String topic, final String isId, final String message) {
		getNotificationHandler().notified(subscriptionId, topic, isId, message);
	}

	@Override
	public void start() {
		if (!checkCerts) {
			log.info("disabling SSL check ...");
			new Thread(new Runnable() {

				@Override
				public void run() {
					disableSslVerification();
				}
			}).start();
		}
		//TODO: we should remove references to BouncyCastle. We are not expecting issues like #2520 with Java8
		//Security.insertProviderAt(new BouncyCastleProvider(),1);
		//System.setProperty("https.protocols", httpsProtocols);
	}

	private void disableSslVerification() {
		try {
			// Create a trust manager that does not validate certificate chains
			TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {

				@Override
				public void checkClientTrusted(final java.security.cert.X509Certificate[] x509Certificates, final String s) throws CertificateException {

				}

				@Override
				public void checkServerTrusted(final java.security.cert.X509Certificate[] x509Certificates, final String s) throws CertificateException {

				}

				@Override
				public java.security.cert.X509Certificate[] getAcceptedIssuers() {
					return null;
				}
			} };

			// Install the all-trusting trust manager
			SSLContext sc = SSLContext.getInstance("SSL");
			sc.init(null, trustAllCerts, new java.security.SecureRandom());
			HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

			// Create all-trusting host name verifier
			HostnameVerifier allHostsValid = new HostnameVerifier() {

				@Override
				public boolean verify(final String hostname, final SSLSession session) {
					return true;
				}
			};

			// Install the all-trusting host verifier
			HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
			log.info("disabled SSL check");
		} catch (NoSuchAlgorithmException e) {
			log.error(e.getMessage(), e);
		} catch (KeyManagementException e) {
			log.error(e.getMessage(), e);
		}
	}

	/**
	 * @return the downloadfeeder
	 */
	public DownloadServiceFeeder getDownloadfeeder() {
		return downloadfeeder;
	}

	/**
	 * @param downloadfeeder the downloadfeeder to set
	 */
	public void setDownloadfeeder(final DownloadServiceFeeder downloadfeeder) {
		this.downloadfeeder = downloadfeeder;
	}

	/**
	 * @return the notificationHandler
	 */
	public NotificationHandler getNotificationHandler() {
		return notificationHandler;
	}

	/**
	 * @param notificationHandler the notificationHandler to set
	 */
	@Required
	public void setNotificationHandler(final NotificationHandler notificationHandler) {
		this.notificationHandler = notificationHandler;
	}

	@Override
	public List<String> listPlugins() throws DownloadServiceException {

		List<String> result = new ArrayList<String>();
		result.addAll(downloadPluginEnumerator.getAll().keySet());
		return result;
	}

}
