/**
 * 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.util.ArrayList;
import java.util.List;

import javax.xml.ws.wsaddressing.W3CEndpointReference;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.log4j.Logger;

import eu.dnetlib.common.ws.dataprov.DataProviderException;

import eu.dnetlib.data.utility.download.dataprov.CreateBasicBulkDataDTO;
import eu.dnetlib.data.utility.download.dataprov.IDataAccessServiceDataProvider;
import eu.dnetlib.data.utility.download.manager.DownloadManager;
import eu.dnetlib.data.utility.download.manager.DownloadManagerResponse;
import eu.dnetlib.data.utility.download.rs.iterator.ResultSetFeedIterator;
import eu.dnetlib.data.utility.download.rs.thread.DownloadFromRSThread;
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>
 */
public class DownloadServiceFacade implements IDownloadService {

	/** The Constant log. */
	protected static final Logger log = Logger
		.getLogger(DownloadServiceFacade.class);
	
	/** Data provider module. */
	private IDataAccessServiceDataProvider 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() {
		
		JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
		factory.setServiceClass(ResultSetService.class);
		factory.setAddress(resultSetLocation);
		//this.resultSetService = (ResultSetService) factory.create();
		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 {
		
		if (url == null) 
			throw new DownloadServiceException("url can not be null");
		
		List<String> list = new ArrayList<String>();
		list.add(url);
		String procesId = CreateBasicBulkDataDTO.generateBulkDataId();
		DownloadManagerResponse response = 
			downloadManager.startDownloadProcess(list, procesId);
		
		if (response.getDownloadedObjectsPaths() != null)
			return response.getDownloadedObjectsPaths().get(0);
		else 
			return null;
	}

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

		if (urls == null) 
			throw new DownloadServiceException("urls list can not be null");
		
		CreateBasicBulkDataDTO bulkDataDTO = new CreateBasicBulkDataDTO();
		
		DownloadManagerResponse response = 
			downloadManager.startDownloadProcess(urls, bulkDataDTO.getId());
		
		ArrayList<String> objectsPaths = null;
		String downloadId = null;
		int resultsNumber = 0;
		
		if (response != null) {
			objectsPaths = response.getDownloadedObjectsPaths();
			downloadId = response.getDownloadProcessID();
		} else {
			String msg = "Objects not downloaded, empty" +
				" response received from DownloadManager";
			log.warn(msg);
		}
			
		if (objectsPaths != null)
			resultsNumber = objectsPaths.size();
		
		log.debug("Objects number to be stored in ResultSet: " + resultsNumber);
		
		bulkDataDTO.setCache(objectsPaths);
		bulkDataDTO.setTotalNumberOfResults(resultsNumber);
		bulkDataDTO.setId(downloadId);
		
		return setUpDataProvider(bulkDataDTO, resultsNumber);
	}

	/* (non-Javadoc)
	 * @see eu.dnetlib.data.utility.download.IDownloadService#downloadURLsFromRS(javax.xml.ws.wsaddressing.W3CEndpointReference)
	 */
	public W3CEndpointReference downloadURLsFromRS(W3CEndpointReference epr) 
		throws DownloadServiceException {
		
		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);

		int resultsNumber = 0;
		try {
			resultsNumber = resultSetService.getNumberOfElements(rsId);
			log.debug("Number of elements stored in RS: " + resultsNumber);
		} catch (ResultSetException e2) {
			throw new IllegalArgumentException(e2);
		}
	
		ResultSetFeedIterator rsIterator = new ResultSetFeedIterator(
				this.rsQueueSize, this.rsPackageSize, 
				this.maxRSIteratorTimeout, resultSetService, rsId,
				this.rsClosingTimeout);
		
		CreateBasicBulkDataDTO bulkDataDTO = new CreateBasicBulkDataDTO();
		bulkDataDTO.setTotalNumberOfResults(resultsNumber);
		
		try {
			DownloadFromRSThread downloadThread = 
				new DownloadFromRSThread(rsId, rsIterator, downloadManager,
						bulkDataDTO.getId());
			downloadThread.start();
			
			dataProvider.createBulkData(bulkDataDTO);
			log.debug("BulkData identifier: " + bulkDataDTO.getId());
		} 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());
		}
		
		return setUpDataProvider(bulkDataDTO, resultsNumber);
	}

	/* (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
	 * @param resultsNumber the results number
	 * 
	 * @return the w3c endpoint reference
	 * 
	 * @throws DataAccessServiceException the data access service exception
	 */
	private W3CEndpointReference setUpDataProvider(
			CreateBasicBulkDataDTO bulkDataDTO, int resultsNumber) 
			throws DownloadServiceException {
		
		/**BulkData Id **/
		String bulkId = bulkDataDTO.getId();
		
		W3CEndpointReference epr = 
			resultSetService.createPullRS(dataProviderServiceAddress,
					bulkId, initialPageSize, expiryTime, "", keepAliveTime, 
				resultsNumber);
		
		if (epr == null) {
			String errorContent = "Exception occured " +
					"when creating pull result set for " +
					"bdId " + bulkId;
			log.error(errorContent);
			throw new DownloadServiceException(errorContent);	
		}
		
		log.debug("ResultSet EPR: " +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 IDataAccessServiceDataProvider getDataProvider() {
		return dataProvider;
	}

	/**
	 * Sets the data provider.
	 * 
	 * @param dataProvider the new data provider
	 */
	public void setDataProvider(IDataAccessServiceDataProvider 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;
	}

}
