package eu.dnetlib.data.transform;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.annotation.PostConstruct;

import com.google.common.collect.Maps;
import com.google.gson.Gson;
import eu.dnetlib.enabling.locators.UniqueServiceLocator;
import eu.dnetlib.rmi.enabling.ISLookUpException;
import eu.dnetlib.rmi.enabling.ISLookUpService;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Component;

/**
 * Created by claudio on 12/12/2016.
 */
@Component
public class OntologyLoader {

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

	private static final String PATTERN = "classpath*:/eu/dnetlib/bootstrap/profiles/OntologyDSResources/OntologyDSResourceType/*.xml";

    private static UniqueServiceLocator staticServiceLocator;

    @Autowired
    private UniqueServiceLocator serviceLocator;

	public static Ontology loadOntologyFromCp(final InputStream profile) {
        return loadOntologyProfilefromPath(profile);
    }

    public static Ontologies loadOntologiesFromCp() throws IOException {
	    final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
	    final List<InputStream> files = Arrays.asList(resolver.getResources(PATTERN)).stream().map(r -> {
		    try {
			    return r.getInputStream();
		    } catch (IOException e) {
			    throw new RuntimeException(e);
		    }
	    }).collect(Collectors.toList());

        return createOntologies(files);
    }

    public static Ontologies createOntologies(List<InputStream> files) {
        final Ontologies ontologies = new Ontologies();
        files.stream().map(
                s -> loadOntologyProfilefromPath(s))
                .collect(Collectors.toMap(Ontology::getCode, Function.identity()))
                .entrySet()
                .forEach(it -> ontologies.put(it.getKey(), it.getValue()));
        return ontologies;
    }

    public static Ontologies loadOntologiesFromIS() throws ISLookUpException {
        final ISLookUpService lookUpService = staticServiceLocator.getService(ISLookUpService.class);
        List<String> files = lookUpService.quickSearchProfile("for $x in collection('/db/DRIVER/OntologyDSResources/OntologyDSResourceType') return $x");
        final Ontologies ontologies = new Ontologies();
        files.stream().map
                (OntologyLoader::getOntologyFromProfile)
                .collect(Collectors.toMap(Ontology::getCode, Function.identity()))
                .entrySet()
                .forEach(it -> ontologies.put(it.getKey(), it.getValue()));
        return ontologies;
    }

	public static Ontologies loadOntologies(final String json) throws IOException {
		return new Gson().fromJson(json, Ontologies.class);
	}

    private static Ontology loadOntologyProfilefromPath(final InputStream profile) {
        final String profileXML;
        try {
            profileXML = IOUtils.toString(profile);
        } catch (IOException e) {
            log.error(e);
            return null;
        }
        return getOntologyFromProfile(profileXML);
    }

    private static Ontology getOntologyFromProfile(final String profile) {
        SAXReader saxReader = new SAXReader();
        Document doc = null;
        StringReader reader = new StringReader(profile);

        try {
            doc = saxReader.read(reader);
        } catch (DocumentException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        return new Ontology()
                .setCode(doc.valueOf("//ONTOLOGY_NAME/@code"))
                .setDescription(doc.valueOf("//ONTOLOGY_DESCRIPTION/text()"))
                .setTerms(asTerms(doc.selectNodes("//TERMS/TERM")));
    }

	private static Map<String,OntologyTerm> asTerms(final List list) {
		final Map<String, OntologyTerm> terms = Maps.newHashMap();
		if (list != null) {
			list.forEach(o -> {
				final Element e = (Element) o;
				final OntologyTerm ot = OntologyTerm.newInstance()
						.setCode(e.attributeValue("code"))
						.setEncoding(e.attributeValue("encoding"))
						.setEnglishName(e.attributeValue("english_name"))
						.setNativeName(e.attributeValue("native_name"))
						.setInverseCode(((Node) o).valueOf("./RELATIONS/RELATION[@type = 'inverseOf']/@code"));

				terms.put(ot.getCode(), ot);
			});
		}
		return terms;
	}

    @PostConstruct
    public void init() {
        OntologyLoader.staticServiceLocator = serviceLocator;
    }

}
