/**
 * Copyright 2008-2009 DRIVER PROJECT (Bielefeld University)
 * Original author: Marek Imialek <marek.imialek at uni-bielefeld.de>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 eu.dnetlib.data.utility.download;


import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;

import javax.jws.WebService;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.soap.SOAPFaultException;
import javax.xml.ws.wsaddressing.W3CEndpointReference;

import org.apache.log4j.Logger;

import eu.dnetlib.common.ws.dataprov.DataProviderException;
import eu.dnetlib.common.ws.dataprov.IDataProviderExt;
import eu.dnetlib.data.utility.download.dataprov.CreateBasicBulkDataDTO;
import eu.dnetlib.data.utility.download.dataprov.IDownloadDataProvider;
import eu.dnetlib.data.utility.download.manager.DownloadManager;
import eu.dnetlib.data.utility.download.rs.iterator.ResultSetIterator;
import eu.dnetlib.enabling.resultset.rmi.ResultSetException;
import eu.dnetlib.enabling.resultset.rmi.ResultSetService;
import eu.dnetlib.enabling.tools.JaxwsServiceResolverImpl;
import eu.dnetlib.enabling.tools.ServiceResolver;
import eu.dnetlib.resultset.impl.builder.IResultSetBuilder;

/**
 * The Class DownloadServiceFacade.
 * 
 * @author <a href="mailto:marek.imialek at uni-bielefeld.de">Marek Imialek</a>
 */
@WebService(endpointInterface="eu.dnetlib.data.utility.download.IDownloadService",
		targetNamespace="http://download.utility.data.dnetlib.eu/")
public class DownloadServiceFacade implements IDownloadService {

	/** The Constant log. */
	protected static final Logger log = Logger
		.getLogger(DownloadServiceFacade.class);
	
	/** Data provider module. */
	private IDownloadDataProvider dataProvider;

	/** Result Set service. */
	private ResultSetService resultSetService;
	
	/** Result Set service location. */
	private String resultSetLocation;

	/** The download handler. */
	private DownloadManager downloadManager;

	/** Current Store service version. */
	private String serviceVersion;

	/** Storing Iterator queue size. */
	private int rsQueueSize;

	/** Storing Iterator single call result size. */
	private int rsPackageSize;

	/** Maximum timeout (in seconds) for retrieving single result from ResultSet while storing records. */
	long maxRSIteratorTimeout;
	
	/** The data provider service address. */
	private String dataProviderServiceAddress;
	
	/** The initial page size. */
	private int initialPageSize;
	
	/** The expiry time. */
	private int expiryTime;
	
	/** The keep alive time. */
	private int keepAliveTime;
	
	/** ResultSet builder module. Provides direct access to the embedded IndexResultSet instance or builds ResultSetPortType to the remote ResultSet service. */
	private IResultSetBuilder resultSetBuilder;

	/** The rs closing timeout. */
	private int rsClosingTimeout;
	
	/**
	 * Inits the.
	 */
	public void init() {
		this.resultSetService = resultSetBuilder.build(resultSetLocation);
	}
	
	/* (non-Javadoc)
	 * @see eu.dnetlib.data.utility.download.IDownloadService#downloadURL(java.lang.String)
	 */
	public String downloadURL(String url) throws DownloadServiceException {
		
		log.info("Method 'downloadURL' requested ("+url+")");
		if (url == null) 
			throw new DownloadServiceException("url can not be null");
		
		List<String> list = new ArrayList<String>();
		list.add(url);
		
		try {
			CreateBasicBulkDataDTO bulkDataDTO = new CreateBasicBulkDataDTO();
			dataProvider.createBulkData(bulkDataDTO);
			log.debug("BulkData identifier: " + bulkDataDTO.getId());
		
			downloadManager.downloadList(list, bulkDataDTO.getId(), null);
		
			String status = dataProvider.getBulkDataStatus(bulkDataDTO.getId());
		
			int counter = 0;
			while (IDataProviderExt.STATUS_OPEN.equals(status)) {
				status = dataProvider.getBulkDataStatus(bulkDataDTO.getId());
				Thread.sleep(3000);
				if (counter == 30) 
					break;
				counter++;
			}
		
			List<String> result = dataProvider.getBulkData(bulkDataDTO.getId(), 1, 2);

			if (result != null & result.size() != 0)
				return (String) result.get(0);
			else
				throw new Exception("Object not found, " +
					"probably was not downloaded correctly.");
		} catch (Exception e) {
			throw new DownloadServiceException(e);
		}
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.data.utility.download.IDownloadService#downloadURLs(java.util.List)
	 */
	public W3CEndpointReference downloadURLs(List<String> urls)
			throws DownloadServiceException {		

		log.info("Method 'downloadURLs' requested");
		if (urls == null) 
			throw new DownloadServiceException("urls list can not be null");
		log.info("Request for downloading "+urls.size() +" objects.");
		
		CreateBasicBulkDataDTO bulkDataDTO = new CreateBasicBulkDataDTO();
		try {
			dataProvider.createBulkData(bulkDataDTO);
			log.debug("BulkData identifier: " + bulkDataDTO.getId());
			
			downloadManager.downloadList(urls, bulkDataDTO.getId(), null);
			
			return setUpDataProvider(bulkDataDTO);
			
		} catch (DataProviderException e) {
			throw new DownloadServiceException("Problem by " +
					"creating BulkData container: " + e.getLocalizedMessage());
		} catch (Exception e) {
			throw new DownloadServiceException("Problem by " +
					"downloading data: " + e.getLocalizedMessage());
		}
		
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.data.utility.download.IDownloadService#downloadURLsFromRS(javax.xml.ws.wsaddressing.W3CEndpointReference)
	 */
	public W3CEndpointReference downloadURLsFromRS(W3CEndpointReference epr) 
		throws DownloadServiceException {
		
		log.info("Method 'downloadURLsFromRS' requested");
		if (epr == null) 
			throw new DownloadServiceException("ResultSet EPR can not be null");
		
		final ServiceResolver serviceResolver = new JaxwsServiceResolverImpl();
		final ResultSetService resultSetService = serviceResolver.getService(
				ResultSetService.class, epr);
		final String rsId = serviceResolver.getResourceIdentifier(epr);

		try {	
			log.debug("Number of elements stored in RS: " 
					+ resultSetService.getNumberOfElements(rsId));
		} catch (ResultSetException e2) {
			throw new IllegalArgumentException(e2);
		} catch (SOAPFaultException e) {
			String excep = "Can not connect ResultSet: "+epr.toString();
			log.error(excep);
			throw new DownloadServiceException (excep);
		}
	
		ResultSetIterator rsIterator = new ResultSetIterator(
				this.rsQueueSize, this.rsPackageSize, 
				this.maxRSIteratorTimeout, resultSetService, rsId, this.rsClosingTimeout);
		
		CreateBasicBulkDataDTO bulkDataDTO = new CreateBasicBulkDataDTO();
		
		try {
			dataProvider.createBulkData(bulkDataDTO);
			log.debug("BulkData identifier: " + bulkDataDTO.getId());
			
			downloadManager.downloadRS(rsIterator, bulkDataDTO.getId(), null);
			
			return setUpDataProvider(bulkDataDTO);
		
		} catch (DataProviderException e) {
			throw new DownloadServiceException("Problem by " +
					"creating BulkData container: " + e.getLocalizedMessage());
		} catch (Exception e) {
			throw new DownloadServiceException("Problem by " +
					"downloading data: " + e.getLocalizedMessage());
		}
	}
	
	/**
	 * Download ur ls from rs.
	 * 
	 * @param epr the epr
	 * 
	 * @return the w3 c endpoint reference
	 * 
	 * @throws DownloadServiceException the download service exception
	 */
	public W3CEndpointReference downloadURLsFromRS(String epr) 
		throws DownloadServiceException {
		
		W3CEndpointReference epr1 = (W3CEndpointReference) W3CEndpointReference
			.readFrom(new StreamSource(new StringReader(epr)));
		return downloadURLsFromRS(epr1);
			
	}	

	/* (non-Javadoc)
	 * @see eu.dnetlib.data.utility.download.IDownloadService#downloadURLsFromXML(javax.xml.ws.wsaddressing.W3CEndpointReference, java.lang.String)
	 */
	public W3CEndpointReference downloadURLsFromXML(W3CEndpointReference epr,
			String urlXPath) throws DownloadServiceException {
		
		log.info("Method 'downloadURLsFromXML' requested (urlXPath =\""+urlXPath+"\")");
		if (epr == null) 
			throw new DownloadServiceException("ResultSet EPR can not be null");
		log.debug("Got following EPR to resolve: "+epr);
		
		final ServiceResolver serviceResolver = new JaxwsServiceResolverImpl();
		final ResultSetService resultSetService = serviceResolver.getService(
				ResultSetService.class, epr);
		final String rsId = serviceResolver.getResourceIdentifier(epr);	
		
		try {	
			log.debug("Number of elements stored in RS: " 
					+ resultSetService.getNumberOfElements(rsId));
		} catch (ResultSetException e2) {
			throw new IllegalArgumentException(e2);
		} catch (SOAPFaultException e) {
			String excep = "Can not connect ResultSet: "+epr.toString();
			log.error(excep);
			throw new DownloadServiceException (excep);
		}
		
		ResultSetIterator rsIterator = new ResultSetIterator(
				this.rsQueueSize, this.rsPackageSize, 
				this.maxRSIteratorTimeout, resultSetService, rsId, this.rsClosingTimeout);
		
		CreateBasicBulkDataDTO bulkDataDTO = new CreateBasicBulkDataDTO();
		bulkDataDTO.setReturnXmls(true);
		
		try {
			dataProvider.createBulkData(bulkDataDTO);
			log.debug("BulkData identifier: " + bulkDataDTO.getId());
			
			downloadManager.downloadRS(rsIterator, bulkDataDTO.getId(), urlXPath);
			
			return setUpDataProvider(bulkDataDTO);
		
		} catch (DataProviderException e) {
			throw new DownloadServiceException("Problem by " +
					"creating BulkData container: " + e.getLocalizedMessage());
		} catch (Exception e) {
			throw new DownloadServiceException("Problem by " +
					"downloading data: " + e.getLocalizedMessage());
		}
	}
	
	/* (non-Javadoc)
	 * @see eu.dnetlib.data.utility.download.IDownloadService#downloadURLsFromXML(java.util.List, java.lang.String)
	 */
	public W3CEndpointReference downloadURLsFromListXML(List<String> urls,
			String urlXPath) throws DownloadServiceException {
		
		log.info("Method 'downloadURLsFromListXML' (urlXPath =\""+urlXPath+"\")");
		if (urls == null) 
			throw new DownloadServiceException("URLs list can not be null");
		
		
		CreateBasicBulkDataDTO bulkDataDTO = new CreateBasicBulkDataDTO();
		bulkDataDTO.setReturnXmls(true);
		
		try {
			dataProvider.createBulkData(bulkDataDTO);
			log.debug("BulkData identifier: " + bulkDataDTO.getId());
			
			downloadManager.downloadList(urls, bulkDataDTO.getId(), urlXPath);
			return setUpDataProvider(bulkDataDTO);
		
		} catch (DataProviderException e) {
			throw new DownloadServiceException("Problem by " +
					"creating BulkData container: " + e.getLocalizedMessage());
		} catch (Exception e) {
			throw new DownloadServiceException("Problem by " +
					"downloading data: " + e.getLocalizedMessage());
		}
	}


	/* (non-Javadoc)
	 * @see eu.dnetlib.data.utility.download.IDownloadService#identify()
	 */
	public String identify() {
		return getServiceVersion();
	}
	
	/**
	 * Sets the up data provider.
	 * 
	 * @param bulkDataDTO the bulk data dto
	 * 
	 * @return the w3c endpoint reference
	 * 
	 * @throws DataAccessServiceException the data access service exception
	 * @throws DownloadServiceException the download service exception
	 */
	private W3CEndpointReference setUpDataProvider(
			CreateBasicBulkDataDTO bulkDataDTO) 
			throws DownloadServiceException {
		
		/**BulkData Id **/
		String bulkId = bulkDataDTO.getId();
		
		W3CEndpointReference epr = 
			resultSetService.createPullRS(dataProviderServiceAddress,
					bulkId, initialPageSize, expiryTime, "OPEN", keepAliveTime, null);
		
		if (epr == null) {
			String errorContent = "Exception occured " +
					"when creating pull result set for " +
					"bdId " + bulkId;
			log.error(errorContent);
			throw new DownloadServiceException(errorContent);	
		}
		
		log.debug("Returning following ResultSet EPR to the client: " +epr);
		
		return epr;
	}
	
	/**
	 * Returns current service version.
	 * 
	 * @return current service version
	 */
	public String getServiceVersion() {
		return this.serviceVersion;
	}

	/**
	 * Sets current service version.
	 * 
	 * @param serviceVersion the service version
	 */
	public void setServiceVersion(String serviceVersion) {
		this.serviceVersion = serviceVersion;
	}	

	/**
	 * Returns ResultSetIterator single call result size.
	 * 
	 * @return ResultSetIterator single call result size
	 */
	public int getRsPackageSize() {
		return this.rsPackageSize;
	}

	/**
	 * Sets ResultSetIterator single call result size.
	 * 
	 * @param rsPackageSize the rs package size
	 */
	public void setRsPackageSize(int rsPackageSize) {
		this.rsPackageSize = rsPackageSize;
	}

	/**
	 * Returns ResultSetIterator queue size.
	 * 
	 * @return ResultSetIterator queue size
	 */
	public int getRsQueueSize() {
		return this.rsQueueSize;
	}

	/**
	 * Sets ResultSetIterator queue size.
	 * 
	 * @param rsQueueSize the rs queue size
	 */
	public void setRsQueueSize(int rsQueueSize) {
		this.rsQueueSize = rsQueueSize;
	}

	/**
	 * Returns maximum timeout (in seconds) for retrieving single result from
	 * ResultSet while storing records.
	 * 
	 * @return maximum timeout (in seconds) for retrieving single result from
	 * ResultSet while storing records
	 */
	public long getMaxRSIteratorTimeout() {
		return this.maxRSIteratorTimeout;
	}

	/**
	 * Sets maximum timeout (in seconds) for retrieving single result from
	 * ResultSet while storing records.
	 * 
	 * @param maxRSIteratorTimeout the max rs iterator timeout
	 */
	public void setMaxRSIteratorTimeout(long maxRSIteratorTimeout) {
		this.maxRSIteratorTimeout = maxRSIteratorTimeout;
	}
	
	/**
	 * Gets the download handler.
	 * 
	 * @return the download handler
	 */
	public DownloadManager getDownloadHandler() {
		return downloadManager;
	}

	/**
	 * Sets the download handler.
	 * 
	 * @param downloadHandler the new download handler
	 */
	public void setDownloadHandler(DownloadManager downloadHandler) {
		this.downloadManager = downloadHandler;
	}
	
	/**
	 * Returns ResultSet builder module.
	 * @return ResultSet builder module
	 */
	public IResultSetBuilder getResultSetBuilder() {
		return resultSetBuilder;
	}

	/**
	 * Sets ResultSet builder module.
	 * @param resultSetBuilder
	 */
	public void setResultSetBuilder(IResultSetBuilder resultSetBuilder) {
		this.resultSetBuilder = resultSetBuilder;
	}
	
	/**
	 * Returns result set data provider service address.
	 * 
	 * @return result set data provider service address
	 */
	public String getDataProviderServiceAddress() {
		return dataProviderServiceAddress;
	}

	/**
	 * Sets result set data provider service address.
	 * 
	 * @param dataProviderServiceAddress
	 */
	public void setDataProviderServiceAddress(String dataProviderServiceAddress) {
		this.dataProviderServiceAddress = dataProviderServiceAddress;
	}

	/**
	 * Returns result set expiry time.
	 * 
	 * @return result set expiry time
	 */
	public int getExpiryTime() {
		return expiryTime;
	}

	/**
	 * Sets result set expiry time.
	 * 
	 * @param expiryTime
	 */
	public void setExpiryTime(int expiryTime) {
		this.expiryTime = expiryTime;
	}
	
	/**
	 * Returns keep alive time.
	 * @return keep alive time
	 */
	public int getKeepAliveTime() {
		return keepAliveTime;
	}

	/**
	 * Sets keep alive time.
	 * @param keepAliveTime
	 */
	public void setKeepAliveTime(int keepAliveTime) {
		this.keepAliveTime = keepAliveTime;
	}

	/**
	 * Returns result set initial page size.
	 * 
	 * @return result set initial page size
	 */
	public int getInitialPageSize() {
		return initialPageSize;
	}

	/**
	 * Sets result set initial page size.
	 * 
	 * @param initialPageSize
	 */
	public void setInitialPageSize(int initialPageSize) {
		this.initialPageSize = initialPageSize;
	}
	
	/**
	 * Returns target Result Set service location.
	 * 
	 * @return target Result Set service location
	 */
	public String getResultSetLocation() {
		return resultSetLocation;
	}

	/**
	 * Sets target Result Set service location.
	 * 
	 * @param resultSetLocation
	 */
	public void setResultSetLocation(String resultSetLocation) {
		this.resultSetLocation = resultSetLocation;
	}
	
	/**
	 * Gets the data provider.
	 * 
	 * @return the data provider
	 */
	public IDownloadDataProvider getDataProvider() {
		return dataProvider;
	}

	/**
	 * Sets the data provider.
	 * 
	 * @param dataProvider the new data provider
	 */
	public void setDataProvider(IDownloadDataProvider dataProvider) {
		this.dataProvider = dataProvider;
	}
	
	/**
	 * Gets the rs closing timeout.
	 * 
	 * @return the rs closing timeout
	 */
	public int getRsClosingTimeout() {
		return this.rsClosingTimeout;
	}

	/**
	 * Sets the rs closing timeout.
	 * 
	 * @param rsClosingTimeout the new rs closing timeout
	 */
	public void setRsClosingTimeout(int rsClosingTimeout) {
		this.rsClosingTimeout = rsClosingTimeout;
	}

}
