package eu.dnetlib.functionality.convert.app;

import static eu.dnetlib.domain.functionality.ConversionStatus.Status.COMPLETED;
import static eu.dnetlib.domain.functionality.ConversionStatus.Status.IN_PROGRESS;
import static eu.dnetlib.domain.functionality.ConversionStatus.Status.ON_QUEUE;
import eu.dnetlib.api.functionality.ConversionService;
import eu.dnetlib.api.functionality.ConversionServiceException;
import eu.dnetlib.data.utility.resource_discovery.api.ResourceDiscoveryAPI;
import eu.dnetlib.domain.functionality.ConversionStatus;
import eu.dnetlib.domain.functionality.ConversionStatus.Status;
import eu.dnetlib.functionality.convert.app.operator.ConversionStatusConverter;
import eu.dnetlib.functionality.convert.app.operator.ConversionTask;
import eu.dnetlib.functionality.convert.store.ConversionServiceStore;
import eu.dnetlib.functionality.convert.store.ConversionServiceStoreException;
import gr.uoa.di.driver.app.DriverServiceImpl;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
import org.springframework.core.task.TaskExecutor;

public class ConversionServiceImpl extends DriverServiceImpl implements ConversionService {
	
	private String serviceUrl = null;
	private ConversionServiceStore storage = null;
	private TaskExecutor conversionExecutor = null;
	private ConversionStatusConverter statusConverter = null;
	private ResourceDiscoveryAPI fileDiscoverer = new ResourceDiscoveryAPI();

	private static ExecutorService pool = Executors.newSingleThreadScheduledExecutor();	
	
	private static Logger logger  = Logger.getLogger(ConversionServiceImpl.class);
	
	@Override
	public ConversionStatus addJob(String fileUrl, String format)
			throws ConversionServiceException {
		String conversionId = new String(Base64.encodeBase64((fileUrl + format).getBytes()));
		
			//Check if the conversion file already exists (on queue, in progress or completed)
			ConversionStatus conversionStatus = statusConverter.toConversionStatus(storage.retrieveConvertedObjectStatusStream(conversionId));
			try { 
			if (conversionStatus != null && conversionStatus.getConversionId() != null 
					&& (conversionStatus.getStatus().equals(IN_PROGRESS) || conversionStatus.getStatus().equals(ON_QUEUE)
							|| conversionStatus.getStatus().equals(COMPLETED))) {
				conversionStatus.setConvertedFileUrl(createConvertedFileUrl(conversionId));
				return conversionStatus;
			}
			
			conversionStatus = submitConversion(fileUrl, format);

			if (!conversionStatus.getStatus().equals(Status.CANCELLED)) {
				conversionStatus.setConvertedFileUrl(createConvertedFileUrl(conversionId));
			}
			
		} catch (UnsupportedEncodingException uee) {
			logger.debug("Failed to update converted file url string" , uee);
		}
		
		return conversionStatus;
	}

	@Override
	public ConversionStatus getStatus(String conversionId) throws ConversionServiceException {
		ConversionStatus status = statusConverter.toConversionStatus(storage.retrieveConvertedObjectStatusStream(conversionId));
		try {
			status.setConvertedFileUrl(createConvertedFileUrl(conversionId));
			
		}  catch (UnsupportedEncodingException uee) {
			status.setConversionId("");
			logger.debug("Failed to update converted file url string" , uee);
		}
		
		return status; 
		
	}
	
	private ConversionStatus submitConversion(String fileUrl, String format) throws ConversionServiceException {
		String conversionId = new String(Base64.encodeBase64((fileUrl + format).getBytes()));
		try {
			ConversionTask task = new ConversionTask(conversionId, fileUrl, format, storage, statusConverter, fileDiscoverer, conversionExecutor);
			pool.submit(task);
			
			return task.getResult();

		} catch (ConversionServiceStoreException csse) {
			throw new ConversionServiceException("Failed to submit new conversion job.", csse);
			
		}
	}
	public void setServiceUrl(String serviceUrl) {
		this.serviceUrl = serviceUrl;
	}
	public void setStorage(ConversionServiceStore storage) {
		this.storage = storage;
	}

	public ConversionServiceStore getStorage() {
		return storage;
	}

	public void setConversionExecutor(TaskExecutor conversionExecutor) {
		this.conversionExecutor = conversionExecutor;
	}

	public void setStatusConverter(ConversionStatusConverter statusConverter) {
		this.statusConverter = statusConverter;
	}
	
	private String createConvertedFileUrl(String conversionId) throws UnsupportedEncodingException{
		return serviceUrl + "/source.jsp?id="+ URLEncoder.encode(conversionId, "UTF-8");
	}
}
