package com.taxonic.carml.util;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Set;
import java.util.function.Function;

import com.taxonic.carml.model.impl.CarmlTriplesMap;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.rio.RDFFormat;
import com.google.common.collect.ImmutableSet;
import com.taxonic.carml.model.TermType;
import com.taxonic.carml.model.TriplesMap;
import com.taxonic.carml.model.impl.CarmlStream;
import com.taxonic.carml.rdf_mapper.impl.MappingCache;
import com.taxonic.carml.rdf_mapper.util.RdfObjectLoader;
import com.taxonic.carml.vocab.Rdf;
import com.taxonic.carml.vocab.Rdf.Rr;

public class RmlMappingLoader {
	
	private RmlConstantShorthandExpander shorthandExpander;
	
	public RmlMappingLoader(
		RmlConstantShorthandExpander shorthandExpander
	) {
		this.shorthandExpander = shorthandExpander;
	}

	public Set<TriplesMap> load(String resource, RDFFormat rdfFormat) {
		InputStream input = RmlMappingLoader.class.getClassLoader().getResourceAsStream(resource);		
		return load(input, rdfFormat);
	}
	
	public Set<TriplesMap> load(Path pathToFile, RDFFormat rdfFormat) {
		InputStream input;
		try {
			input = Files.newInputStream(pathToFile);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}		
		return load(input, rdfFormat);
	}
	
	public Set<TriplesMap> load(InputStream input, RDFFormat rdfFormat) {
		// TODO: PM do we really need IoUtils?
		Model originalModel = IoUtils.parse(input, rdfFormat);
		return
			ImmutableSet.<TriplesMap>copyOf(
				RdfObjectLoader.load(
					selectTriplesMaps, 
					CarmlTriplesMap.class,
					originalModel, 
					shorthandExpander,
					this::addTermTypes,
					m -> m.addDecidableType(Rdf.Carml.Stream, CarmlStream.class)
				)
			);
	}
	
	private void addTermTypes(MappingCache cache) {
		class AddTermTypes {
			
			void add(IRI iri, TermType termType) {
				cache.addCachedMapping(iri, TermType.class, termType);
			}
			
			void run() {
				add(Rr.BlankNode, TermType.BLANK_NODE);
				add(Rr.IRI      , TermType.IRI       );
				add(Rr.Literal  , TermType.LITERAL   );
			}
		}
		new AddTermTypes().run();
	}
	
	public static RmlMappingLoader build() {
		return new RmlMappingLoader(
			new RmlConstantShorthandExpander()
		);
	}
	
	private static Function<Model, Set<Resource>> selectTriplesMaps = 
		model ->
			ImmutableSet.copyOf(
				model
				.filter(null, Rdf.Rml.logicalSource, null)
				.subjects()
			);
}

@@ -245,24 +245,76 @@
 TriplesMap;%0A
+import com.taxonic.carml.model.impl.CarmlXmlSource;%0A
 import org.e
@@ -499,24 +499,71 @@
 mutableSet;%0A
+import com.taxonic.carml.model.NameableStream;%0A
 import com.t
@@ -626,32 +626,74 @@
 del.TriplesMap;%0A
+import com.taxonic.carml.model.XmlSource;%0A
 import com.taxon
@@ -1961,16 +1961,24 @@
 %09%09%09m -%3E 
+%7B%0A%09%09%09%09%09%09
 m.addDec
@@ -2010,26 +2010,258 @@
 am, 
-CarmlStream.class)
+NameableStream.class); %0A%09%09%09%09%09%09m.addDecidableType(Rdf.Carml.XmlDocument, XmlSource.class);%0A%09%09%09%09%09%09m.bindInterfaceImplementation(NameableStream.class, CarmlStream.class);%0A%09%09%09%09%09%09m.bindInterfaceImplementation(XmlSource.class, CarmlXmlSource.class);%0A%09%09%09%09%09%7D
 %0A%09%09%09
@@ -2420,24 +2420,40 @@
 apping(iri, 
+ImmutableSet.of(
 TermType.cla
@@ -2454,16 +2454,17 @@
 pe.class
+)
 , termTy

add extra decidable types