package eu.dnetlib.x3m;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Iterator;
import java.util.function.Function;
import java.util.function.IntFunction;

import gr.forth.ics.isl.x3ml.X3MLEngineFactory;
import gr.forth.ics.isl.x3ml.X3MLEngineFactory.OutputFormat;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;

/**
 * Created by alessia on 31/01/17.
 */

public class X3MLEngineTest {

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

	final String recordPath = "/eu/dnetlib/x3m/new-10304736.xml";
	final String mappingPath = "/eu/dnetlib/x3m/mappings.x3ml";
	final String policyPath = "/eu/dnetlib/x3m/maria-policy.xml";

	final String forthInputPath ="/eu/dnetlib/x3m/input.xml";
	final String forthMappingPath = "/eu/dnetlib/x3m/mappingsWithoutGenerator.x3ml";

	final String[] allInputFiles =
			new String[] { recordPath, "/eu/dnetlib/x3m/new-10304737.xml", "/eu/dnetlib/x3m/new-10304738.xml", "/eu/dnetlib/x3m/new-10304739.xml",
					"/eu/dnetlib/x3m/new-10304740.xml", "/eu/dnetlib/x3m/new-10304741.xml", "/eu/dnetlib/x3m/new-10304742.xml" };

	@Test
	public void testWithURLMapping() throws MalformedURLException {
		X3MLEngineFactory x3mEngine = X3MLEngineFactory.create()
				.withMappings(new URL("https://mapping.d4science.org/3MEditor/Services?id=575&output=text/xml&method=export"))
				.withVerboseLogging()
				.withGeneratorPolicy(getInputStreamFromClasspath("/eu/dnetlib/x3m/ariadne_policy.xml"))
				.withInput(getInputStreamFromClasspath("/eu/dnetlib/x3m/ariadneADSsample.xml"))
				.withOutput(System.out, OutputFormat.RDF_XML);
		x3mEngine.execute();
	}

	@Test
	public void testForth() throws Exception{
		X3MLEngineFactory x3mEngine = X3MLEngineFactory.create()
				.withMappings(getInputStreamFromClasspath(forthMappingPath))
				.withVerboseLogging()
				.withInput(getInputStreamFromClasspath(forthInputPath))
				.withOutput(System.out, OutputFormat.RDF_XML);
		x3mEngine.execute();
	}

	@Test
	public void testURIShortener() throws Exception{
		X3MLEngineFactory x3mEngine = X3MLEngineFactory.create()
				.withMappings(getInputStreamFromClasspath("/eu/dnetlib/x3m/mapping464_with_shortener.x3ml"))
				.withGeneratorPolicy(getInputStreamFromClasspath("/eu/dnetlib/x3m/parthenos_policy_uri_shortener.xml"))
				.withVerboseLogging()
				.withInput(getInputStreamFromClasspath("/eu/dnetlib/x3m/record_shortener.xml"))
				.withOutput(System.out, OutputFormat.RDF_XML);
		x3mEngine.execute();
	}

	@Test
	public void test() throws Exception {
		final ByteArrayOutputStream os = new ByteArrayOutputStream();
		final PrintStream ps = new PrintStream(os);

		X3MLEngineFactory x3mEngine = X3MLEngineFactory.create().withGeneratorPolicy(getInputStreamFromClasspath(policyPath))
				.withMappings(getInputStreamFromClasspath(mappingPath))
				.withVerboseLogging()
				.withInput(getInputStreamFromClasspath(recordPath))
				.withOutput(ps, OutputFormat.RDF_XML);
		x3mEngine.execute();

		logStream(os);

	}

	@Test
	public void testRDFPlain() throws Exception {
		final ByteArrayOutputStream os = new ByteArrayOutputStream();
		final PrintStream ps = new PrintStream(os);

		X3MLEngineFactory x3mEngine = X3MLEngineFactory.create().withGeneratorPolicy(getInputStreamFromClasspath(policyPath))
				.withMappings(getInputStreamFromClasspath(mappingPath))
				.withVerboseLogging()
				.withInput(getInputStreamFromClasspath(recordPath))
				.withOutput(ps, OutputFormat.RDF_XML_PLAIN);
		x3mEngine.execute();

		logStream(os);

	}


	@Test
	public void testAll() throws Exception {
		final ByteArrayOutputStream os = new ByteArrayOutputStream();
		final PrintStream ps = new PrintStream(os);
		X3MLEngineFactory x3mEngine = X3MLEngineFactory.create().withGeneratorPolicy(getInputStreamFromClasspath(policyPath))
				.withMappings(getInputStreamFromClasspath(mappingPath))
				.withVerboseLogging()
				.withInput(convertArray(allInputFiles, X3MLEngineTest::getInputStreamFromClasspath, InputStream[]::new))
				.withOutput(ps, OutputFormat.RDF_XML);
		x3mEngine.execute();
		logStream(os);
	}

	@Test
	@Ignore
	/**
	 * Test if it is possible to re-use the same X3MLEngineFactory to transform different XMLs.
	 * The test fails with an exception when trying to parse the mapping file for the second time.
	 */
	public void testAllIteratorReuse() throws Exception {
		Iterator<InputStream> it = convertIterator(allInputFiles, X3MLEngineTest::getInputStreamFromClasspath);
		X3MLEngineFactory x3mEngine = X3MLEngineFactory.create().withGeneratorPolicy(getInputStreamFromClasspath(policyPath))
				.withMappings(getInputStreamFromClasspath(mappingPath))
				.withVerboseLogging();
		int count =0;
		while(it.hasNext()){
			count++;
			System.out.println("Transforming file number "+count);
			final ByteArrayOutputStream os = new ByteArrayOutputStream();
			final PrintStream ps = new PrintStream(os);
			InputStream s = it.next();
			x3mEngine.withInput(s).withOutput(ps, OutputFormat.RDF_XML);
			x3mEngine.execute();
			logStream(os);
			os.close();
		}
	}

	@Test
	public void testAllIterator() throws Exception {
		Iterator<InputStream> it = convertIterator(allInputFiles, X3MLEngineTest::getInputStreamFromClasspath);

		int count =0;
		while(it.hasNext()){
			count++;
			System.out.println("Transforming file number "+count);
			final ByteArrayOutputStream os = new ByteArrayOutputStream();
			final PrintStream ps = new PrintStream(os);
			X3MLEngineFactory x3mEngine = X3MLEngineFactory.create().withGeneratorPolicy(getInputStreamFromClasspath(policyPath))
					.withMappings(getInputStreamFromClasspath(mappingPath))
					.withVerboseLogging();
			InputStream s = it.next();
			x3mEngine.withInput(s).withOutput(ps, OutputFormat.RDF_XML).execute();
			logStream(os);
			os.close();
		}
	}

	private void logStream(final ByteArrayOutputStream os){
		log.info("**************Result:\n");
		String s = new String(os.toByteArray());
		log.info(s);
	}

	public static InputStream getInputStreamFromClasspath(final String s) {
		try {
			final ClassPathResource resource = new ClassPathResource(s);
			return resource.getInputStream();
		}catch(IOException e){
			return null;
		}
	}

	public static <T, U> U[] convertArray(T[] from,
			Function<T, U> func,
			IntFunction<U[]> generator) {
		return Arrays.stream(from).map(func).toArray(generator);
	}

	public static <T, U> Iterator<U> convertIterator(T[] from,
			Function<T, U> func) {
		return Arrays.stream(from).map(func).iterator();
	}
}
