/**
 * 
 */
package eu.dnetlib.data.collective.manager.scheduling;

import java.util.Date;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import eu.dnetlib.common.profile.Resource;
import eu.dnetlib.data.collective.manager.IInstanceListener;
import eu.dnetlib.data.collective.manager.nh.INotificationConsumer;
import eu.dnetlib.data.collective.manager.profile.AbstractInstance;
import eu.dnetlib.data.collective.manager.utils.InstanceRegistry;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException;
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;
import eu.dnetlib.enabling.tools.ServiceLocator;
import eu.dnetlib.miscutils.datetime.DateUtils;

/**
 * @author jochen
 * @param <T>
 *
 */
public class EventScheduling implements INotificationConsumer{

	private static final Log log = LogFactory.getLog(EventScheduling.class);
	private EventRegistry<AbstractInstance> eventRegistry;
	private IInstanceListener<AbstractInstance> instanceListener;
	private ServiceLocator<ISLookUpService> lookupLocator;
	
	@javax.annotation.Resource(name = "instanceRegistry")
	private InstanceRegistry<AbstractInstance> instanceRegistry;

	/**
	 * @return the instanceRegistry
	 */
	public InstanceRegistry<AbstractInstance> getInstanceRegistry() {
		return instanceRegistry;
	}

	/**
	 * @param instanceRegistry the instanceRegistry to set
	 */
	public void setInstanceRegistry(
			InstanceRegistry<AbstractInstance> instanceRegistry) {
		this.instanceRegistry = instanceRegistry;
	}

	/**
	 * unsupported
	 */
	@Override
	public boolean addResource(Resource aResource) {
		// unsupported
		return false;
	}

	/**
	 * unsupported
	 */
	@Override
	public boolean removeResource(Resource aResource) {
		// unsupported
		return false;
	}

	/**
	 * schedules a job for immediate execution, if the resource is registered for an event
	 * it checks on non emptiness for a single data source and on proper data source update date if more than one data source exists
	 * @see eu.dnetlib.data.collective.manager.nh.INotificationConsumer#updateResource(eu.dnetlib.common.profile.Resource)
	 */
	@Override
	public boolean updateResource(Resource aResource) {
		String dataSourceId = aResource.getValue("//RESOURCE_IDENTIFIER/@value");
		log.debug("recevied notification to update with id: " + dataSourceId);
		if (this.eventRegistry.isRegisteredEvent(dataSourceId)){
			AbstractInstance instance = this.instanceRegistry.getInstance(this.eventRegistry.getInstanceId(dataSourceId));
			List<String> dataSourceIds = instance.getDataSourceIds();
			// compare the lastUpdateDate of the instance with the last_storage_date of the dataSource (if dataSourceList > 1)
			try {
				if (dataSourceIds.size() > 1){
					if (!areDataSourcesUpdated(dataSourceIds, instance.getLastUpdateAsDate())){
						return false;
					}
				}else if (dataSourceIds.size() == 1){
					// TODO can we use this check also in the case of ds size > 1 ?
					// check that number of records in the data source > 0
					if (isDataSourceEmpty(dataSourceId)){
						return false;
					}
				}
			} catch (ISLookUpException e) {
				throw new IllegalStateException(e);
			}
			
			// schedule a new job			
			log.debug("schedule for instance id: " + instance.getResourceId());
			instanceListener.fireOnce(instance, instance.getDefaultBlackboardMessageAction());
			return true;
		}else{			
			log.debug("not registered event resource");
			return false;			
		}
	}
	
	private boolean areDataSourcesUpdated(List<String> aDataSourceIds, Date aInstanceLastUpdateDate) throws ISLookUpException{
		StringBuilder queryBuilder = new StringBuilder();
		for (String dataSourceId: aDataSourceIds){
			queryBuilder.append("collection('/db/DRIVER/')//RESOURCE_PROFILE[.//RESOURCE_IDENTIFIER/@value='" + dataSourceId + "']//LAST_STORAGE_DATE/text() ,");
		}
		List<String> dataSourceLastUpdateDates = lookupLocator.getService().quickSearchProfile(queryBuilder.toString().substring(0, queryBuilder.toString().lastIndexOf(',')));
		DateUtils du = new DateUtils();
		for (String dataSourceDate: dataSourceLastUpdateDates){
			if (du.parse(dataSourceDate).before(aInstanceLastUpdateDate)){
				return false;
			}
		}
		return true;
	}
	
	private boolean isDataSourceEmpty(String dataSourceId) throws ISLookUpException{
		String query = "collection('/db/DRIVER/')//RESOURCE_PROFILE[.//RESOURCE_IDENTIFIER/@value='" + dataSourceId + "']//NUMBER_OF_RECORDS/text()";
		List<String> dataSourceSizeList = lookupLocator.getService().quickSearchProfile(query);
		if (!dataSourceSizeList.isEmpty()){
			if (Integer.parseInt(dataSourceSizeList.get(0)) > 0){
				return false;
			}
		}
		return true;
	}

	/**
	 * @param eventRegistry the eventRegistry to set
	 */
	public void setEventRegistry(EventRegistry<AbstractInstance> eventRegistry) {
		this.eventRegistry = eventRegistry;
	}

	/**
	 * @return the eventRegistry
	 */
	public EventRegistry<AbstractInstance> getEventRegistry() {
		return eventRegistry;
	}

	/**
	 * @param instanceListener the instanceListener to set
	 */
	public void setInstanceListener(IInstanceListener<AbstractInstance> instanceListener) {
		this.instanceListener = instanceListener;
	}

	/**
	 * @return the instanceListener
	 */
	public IInstanceListener<AbstractInstance> getInstanceListener() {
		return instanceListener;
	}

	/**
	 * @param lookupLocator the lookupLocator to set
	 */
	public void setLookupLocator(ServiceLocator<ISLookUpService> lookupLocator) {
		this.lookupLocator = lookupLocator;
	}

	/**
	 * @return the lookupLocator
	 */
	public ServiceLocator<ISLookUpService> getLookupLocator() {
		return lookupLocator;
	}
}
