package eu.dnetlib.enabling.nodeManager;

import java.io.IOException;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.reflections.Reflections;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;

import com.google.common.collect.Lists;
import com.google.gson.Gson;

import eu.dnetlib.common.services.locators.DnetServiceLocator;
import eu.dnetlib.enabling.annotations.DnetResource;
import eu.dnetlib.enabling.annotations.DnetResourceHelper;
import eu.dnetlib.enabling.datastructures.BaseResource;
import eu.dnetlib.rmi.objects.is.DnetDataStructure;
import eu.dnetlib.rmi.objects.is.DnetResourceType;
import eu.dnetlib.rmi.soap.InformationService;
import eu.dnetlib.rmi.soap.exceptions.InformationServiceException;

public class ContentInitializer implements ResourceLoaderAware {

	@javax.annotation.Resource
	private DnetServiceLocator serviceLocator;

	private ResourceLoader resourceLoader;

	private String basePackage;
	private String initDsPath;

	private static final Log log = LogFactory.getLog(ContentInitializer.class);

	public void init() {
		try {
			log.info("Content initialization - START");
			final List<Class<? extends BaseResource>> types = registerAnnotatedResouceTypes(basePackage);
			for (Class<? extends BaseResource> type : types) {
				registerDatastructures(initDsPath, type, false);
			}
			log.info("Content initialization - END");
		} catch (Throwable e) {
			log.error("Content Initialization failed: " + e.getMessage(), e);
		}
	}

	@SuppressWarnings("unchecked")
	public List<Class<? extends BaseResource>> registerAnnotatedResouceTypes(final String pkg) throws InformationServiceException {
		log.info("importing types from package " + pkg);

		final List<Class<? extends BaseResource>> res = Lists.newArrayList();
		final InformationService is = serviceLocator.getService(InformationService.class);
		final Reflections reflections = new Reflections(pkg);
		for (Class<?> cl : reflections.getTypesAnnotatedWith(DnetResource.class)) {
			log.info(" - Registering/updating resourceType " + cl.getName());
			final DnetResourceType type = DnetResourceHelper.obtainResourceType(cl);
			is.addResourceType(type);
			if (BaseResource.class.isAssignableFrom(cl)) {
				res.add((Class<? extends BaseResource>) cl);
			} else {
				log.error("Class " + cl + " does not extends BaseResource");
				throw new InformationServiceException("Class " + cl + " does not extends BaseResource");
			}
		}
		return res;
	}

	public void registerDatastructures(final String basePath, final Class<? extends BaseResource> type, final boolean override)
			throws InformationServiceException {

		final DnetResource ann = type.getAnnotation(DnetResource.class);

		final String path;

		switch (ann.format()) {
		case JSON:
			path = basePath + "/" + ann.type() + "/**/*.json";
			break;
		case XML:
			path = basePath + "/" + ann.type() + "/**/*.xml";
			break;
		case TEXT:
			path = basePath + "/" + ann.type() + "/**/*.text";
			break;
		default:
			path = basePath;
		}

		log.info("importing resources from " + path);

		final InformationService is = serviceLocator.getService(InformationService.class);
		final ResourcePatternResolver resourceResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
		try {
			for (Resource resource : resourceResolver.getResources(path)) {
				try {
					final BaseResource r;
					switch (ann.format()) {
					case JSON:
						r = new Gson().fromJson(IOUtils.toString(resource.getInputStream()), type);
						break;
					case XML:
						r = type.getConstructor(String.class).newInstance(IOUtils.toString(resource.getInputStream()));
						break;
					case TEXT:
						r = type.getConstructor(Resource.class).newInstance(resource);
						break;
					default:
						r = null;
					}
					final DnetDataStructure ds = r.asDnetDataStructure();
					if (override || !is.isRegisteredDs(ds.getCode(), ds.getType())) {
						log.info(" - Registering/updating resource " + ds.getCode());
						is.registerDs(ds);
					}
				} catch (Throwable e) {
					log.error("Failed registration of resource " + resource.getFilename(), e);
					throw new InformationServiceException("Failed registration of resource " + resource.getFilename(), e);
				}
			}
		} catch (IOException e) {
			log.error("Failed registration of resources in path " + path, e);
			throw new InformationServiceException("Failed registration of resources in path " + path, e);

		}
	}

	public String getBasePackage() {
		return basePackage;
	}

	@Required
	public void setBasePackage(final String basePackage) {
		this.basePackage = basePackage;
	}

	public String getInitDsPath() {
		return initDsPath;
	}

	@Required
	public void setInitDsPath(final String initDsPath) {
		this.initDsPath = initDsPath;
	}

	@Override
	public void setResourceLoader(final ResourceLoader resourceLoader) {
		this.resourceLoader = resourceLoader;
	}
}
