package eu.dnetlib.enabling.tools;

import java.io.IOException;
import java.io.StringWriter;
import java.net.URL;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;

import eu.dnetlib.enabling.DnetInformationServiceException;
import eu.dnetlib.enabling.locators.UniqueServiceLocator;
import eu.dnetlib.miscutils.coupling.StaticCondition;
import eu.dnetlib.rmi.enabling.ISRegistryException;
import eu.dnetlib.rmi.enabling.ISRegistryService;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.core.io.Resource;
import org.xml.sax.SAXException;

/**
 * This job is scheduled at startup of a test environment to load some data into the store.
 * 
 * <p>
 * You should configure this bean with paths to the profile and schema files you want to add. This can be done setting
 * the 'resources' and 'schemas' properties using the syntax documented <a
 * href="http://static.springframework.org/spring/docs/
 * 2.5.x/reference/resources.html#resources-app-ctx-wildcards-in-resource-paths">here</a>
 * </p>
 * 
 * @author marko
 * 
 */
public class DnetContentInitializer {
	/**
	 * logger.
	 */
	static final Log log = LogFactory.getLog(DnetContentInitializer.class); // NOPMD by marko on 11/24/08 5:02 PM
	/**
	 * milliseconds in a second.
	 */
	private static final double MILLIS = 1000.0;
	/**
	 * hackish way of communicating to the integration tester that we have finished.
	 */
	private static boolean initialized = false;
	
	/**
	 * resourceLoader compatible pattern matching all files to load.
	 */
	private String resources;

	/**
	 * resourceLoader compatible pattern matching all xsd files to load.
	 */
	private String schemas;

	/**
	 * resource loader helper. we cannot implement the ResourceLoaderAware interface.
	 */
	private ResourceLoaderHelper resourceLoader;

	/**
	 * set to true in order to disable notification detection. Useful for bulk profile insertion.
	 */
	private StaticCondition snDisable;

	/**
	 * service locator.
	 */
	private UniqueServiceLocator serviceLocator;
	/**
	 * helper class used to bypass the registry and import resources as-is from a backup dump.
	 */
	private BulkResourceImporter bulkImporter;


	private int timeToSleep;

	public static boolean isInitialized() {
		return initialized;
	}

	public static void setInitialized(final boolean initialized) {
		DnetContentInitializer.initialized = initialized;
	}

	/**
	 * register a schema from a local resource.
	 *
	 * @param url
	 *            url
	 * @throws IOException
	 *             happens
	 * @throws ISRegistryException
	 *             could happen
	 */
	private void registerSchema(final URL url) throws IOException, ISRegistryException {
		final String resourceType = FilenameUtils.getBaseName(url.getPath());
		log.info("registering schema: " + resourceType);

		final StringWriter writer = new StringWriter();
		IOUtils.copy(url.openStream(), writer);
		ISRegistryService service = null;
		while (service == null) {
			try {
				service = serviceLocator.getService(ISRegistryService.class, true);
				service.addResourceType(resourceType, writer.getBuffer().toString());
				log.debug("The is registry service is ready ");
			} catch (Exception e) {
				log.fatal("The is registry service is not ready ", e);
				try {
					Thread.sleep(timeToSleep);
				} catch (InterruptedException e1) {
					log.error(e1);
				}
			}
		}

	}

	/**
	 * register a profile from a local resource.
	 *
	 * @param url
	 *            url
	 * @throws IOException
	 *             could happen
	 * @throws ISRegistryException
	 *             could happen
	 * @throws ParserConfigurationException
	 *             could happen
	 * @throws SAXException
	 *             could happen
	 * @throws XPathExpressionException
	 *             could happen
	 */
	private void registerProfile(final URL url) throws IOException, ISRegistryException, XPathExpressionException, SAXException, ParserConfigurationException {
		log.debug("registering profile: " + url);

		bulkImporter.importResource(new StreamOpaqueResource(url.openStream()));
	}

	public void initialize() throws DnetInformationServiceException {
		log.info("Initializing store with some profiles and collections for test and development ...");

		if (!getBulkImporter().isEnabled()) {
			log.info("skipping store initialization because the database already exists");
			return;
		}
		log.info("loading resources: " + getResources());
		try {
			String type = "schema/profile";
			String name = "none";
			try {
				final long start = System.currentTimeMillis();

				type = "schema";
				for (Resource res : resourceLoader.getResourcePatternResolver().getResources(schemas)) {
					name = res.getURL().toString();
					registerSchema(res.getURL());
				}

				try {
					snDisable.setCondition(true);
					type = "profile";
					for (Resource res : resourceLoader.getResourcePatternResolver().getResources(resources)) {
						name = res.getURL().toString();
						registerProfile(res.getURL());
					}
				} finally {
					snDisable.setCondition(false);
				}

				log.info("bulk registration finished in: " + ((System.currentTimeMillis() - start) / MILLIS) + "s");
			} catch (ISRegistryException e) {
				log.fatal("cannot register " + type + ": " + name, e);
				throw new DnetInformationServiceException("registry exception", e);
			} catch (IOException e) {
				throw new DnetInformationServiceException("io exception", e);
			} catch (XPathExpressionException e) {
				throw new DnetInformationServiceException("xpath exception", e);
			} catch (SAXException e) {
				throw new DnetInformationServiceException("sax exception", e);
			} catch (ParserConfigurationException e) {
				throw new DnetInformationServiceException("parser exception", e);
			}
		} finally {
			log.info("INITIALIZED");
			setInitialized(true);
		}
	}

	public String getResources() {
		return resources;
	}

	public void setResources(final String resources) {
		this.resources = resources;
	}

	@Required
	public ResourceLoaderHelper getResourceLoader() {
		return resourceLoader;
	}

	@Required
	public void setResourceLoader(final ResourceLoaderHelper resourceLoader) {
		this.resourceLoader = resourceLoader;
	}

	public String getSchemas() {
		return schemas;
	}

	@Required
	public void setSchemas(final String schemas) {
		this.schemas = schemas;
	}

	public StaticCondition getSnDisable() {
		return snDisable;
	}

	public void setSnDisable(final StaticCondition snDisable) {
		this.snDisable = snDisable;
	}

	public BulkResourceImporter getBulkImporter() {
		return bulkImporter;
	}

	@Required
	public void setBulkImporter(final BulkResourceImporter bulkImporter) {
		this.bulkImporter = bulkImporter;
	}

	/**
	 * @return the timeToSleep
	 */
	public int getTimeToSleep() {
		return timeToSleep;
	}

	/**
	 * @param timeToSleep
	 *            the timeToSleep to set
	 */
	@Required
	public void setTimeToSleep(final int timeToSleep) {
		this.timeToSleep = timeToSleep;
	}

	public UniqueServiceLocator getServiceLocator() {
		return serviceLocator;
	}

	@Required
	public void setServiceLocator(final UniqueServiceLocator serviceLocator) {
		this.serviceLocator = serviceLocator;
	}


}
