package eu.dnetlib.index.utils;

import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.dom4j.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import com.google.common.collect.Maps;

import eu.dnetlib.rmi.provision.IndexServiceException;

/**
 * The Class ZkUtils.
 */
public class ZkUtils {

	/**
	 * The log.
	 */
	private static final Log log = LogFactory.getLog(ZkUtils.class); // NOPMD by marko on 11/24/08 5:02 PM

	/**
	 * The Constant CONFIGS_PATH.
	 */
	private static final String CONFIGS_PATH = "/configs";

	/**
	 * The schema factory.
	 */
	@Autowired
	private IndexSchemaFactory schemaFactory;

	/**
	 * The config factory.
	 */
	private IndexConfigFactory configFactory;

	/**
	 * The static configuration classpath.
	 */
	private String staticConfigurationClasspath;

	/**
	 * Upload zookeper config.
	 *
	 * @param zkClient
	 *            the zk client
	 * @param coreName
	 *            the core name
	 * @param fields
	 *            the fields
	 * @param params
	 *            the params
	 * @param overwrite
	 *            the overwrite
	 */
	public void uploadZookeperConfig(final SolrZkClient zkClient,
			final String coreName,
			final Document fields,
			final Map<String, String> params,
			final boolean overwrite) {

		final String basepath = CONFIGS_PATH + "/" + coreName;

		log.info("uploading solr configuration to ZK for index collection: " + coreName);

		try {
			if (overwrite) {
				log.info("cleanup ZK configuration: " + coreName);
				for (final String child : zkClient.getSolrZooKeeper().getChildren(basepath, false)) {
					final String path = basepath + "/" + child;
					log.debug("cleanup ZK file: " + path);
					zkClient.delete(path, -1, true);
				}
				zkClient.delete(basepath, -1, true);
			}
			if (!zkClient.exists(basepath, true)) {
				log.info("upload ZK configuration: " + coreName);
				zkClient.makePath(basepath, true);
				uploadConfiguration(zkClient, basepath, buildConfiguration(coreName, fields, params));
			}
			log.info("upload ZK configuration complete");
		} catch (final Exception e) {
			log.error("unable to upload solr configuration", e);
		}
	}

	/**
	 * Builds the configuration.
	 *
	 * @param indexName
	 *            the index name
	 * @param fields
	 *            the fields
	 * @param params
	 *            the params
	 * @return the map
	 * @throws IndexServiceException
	 *             the index service exception
	 */
	private Map<String, byte[]> buildConfiguration(final String indexName, final Document fields, final Map<String, String> params)
			throws IndexServiceException {

		final Map<String, byte[]> res = Maps.newHashMap();

		try {
			log.debug("adding schema.xml to the resource map");
			res.put("schema.xml", schemaFactory.getSchema(fields).getBytes());

			res.put("solrconfig.xml", configFactory.getConfig(params).getBytes());
			log.debug("adding solrconfig.xml to the resource map");
			final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
			final Resource[] resources = resolver.getResources(getStaticConfigurationClasspath());

			for (final Resource r : resources) {
				final InputStream is = r.getInputStream();
				if ((r.getFilename() != null) && !r.getFilename().isEmpty()) {
					res.put(r.getFilename(), IOUtils.toByteArray(is));
					log.debug("adding " + r.getFilename() + " to the resource map");
					is.close();
				}
			}
			return res;
		} catch (final Throwable e) {
			throw new IndexServiceException("failed to build configuration", e);
		}
	}

	/**
	 * Upload configuration.
	 *
	 * @param zkClient
	 *            the zk client
	 * @param basePath
	 *            the base path
	 * @param resources
	 *            the resources
	 * @throws KeeperException
	 *             the keeper exception
	 * @throws InterruptedException
	 *             the interrupted exception
	 * @throws IOException
	 *             Signals that an I/O exception has occurred.
	 */
	private void uploadConfiguration(final SolrZkClient zkClient, final String basePath, final Map<String, byte[]> resources) throws KeeperException,
			InterruptedException, IOException {

		if (!zkClient.exists(basePath, true)) {
			zkClient.makePath(basePath, true);
		}

		for (final Entry<String, byte[]> e : resources.entrySet()) {
			final String path = basePath + "/" + e.getKey();
			log.debug("upload ZK configuration: " + path);
			zkClient.create(path, e.getValue(), CreateMode.PERSISTENT, true);
		}
	}

	/**
	 * Gets the config factory.
	 *
	 * @return the config factory
	 */
	public IndexConfigFactory getConfigFactory() {
		return configFactory;
	}

	/**
	 * Sets the config factory.
	 *
	 * @param configFactory
	 *            the new config factory
	 */
	@Required
	public void setConfigFactory(final IndexConfigFactory configFactory) {
		this.configFactory = configFactory;
	}

	/**
	 * Gets the static configuration classpath.
	 *
	 * @return the static configuration classpath
	 */
	public String getStaticConfigurationClasspath() {
		return staticConfigurationClasspath;
	}

	/**
	 * Sets the static configuration classpath.
	 *
	 * @param staticConfigurationClasspath
	 *            the new static configuration classpath
	 */
	@Required
	public void setStaticConfigurationClasspath(final String staticConfigurationClasspath) {
		this.staticConfigurationClasspath = staticConfigurationClasspath;
	}

}
