package org.petapico.npop;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPOutputStream;

import net.trustyuri.TrustyUriException;

import org.nanopub.MalformedNanopubException;
import org.nanopub.MultiNanopubRdfHandler;
import org.nanopub.MultiNanopubRdfHandler.NanopubHandler;
import org.nanopub.Nanopub;
import org.nanopub.NanopubImpl;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.RDFParseException;
import org.openrdf.rio.Rio;

import com.beust.jcommander.JCommander;
import com.beust.jcommander.ParameterException;

public class Topic {

	@com.beust.jcommander.Parameter(description = "input-nanopubs", required = true)
	private List<File> inputNanopubs = new ArrayList<File>();

	@com.beust.jcommander.Parameter(names = "-o", description = "Output file")
	private File outputFile;

	@com.beust.jcommander.Parameter(names = "--in-format", description = "Format of the input nanopubs: trig, nq, trix, trig.gz, ...")
	private String inFormat;

	@com.beust.jcommander.Parameter(names = "-u", description = "Include the nanopub URI in output")
	private boolean outputNanopubUri = false;

	@com.beust.jcommander.Parameter(names = "-i", description = "Property URIs to ignore, separated by '|' (has no effect if -d is set)")
	private String ignoreProperties;

	@com.beust.jcommander.Parameter(names = "-d", description = "Topic detector class")
	private String detectorClass;

	public static void main(String[] args) {
		NanopubImpl.ensureLoaded();
		Topic obj = new Topic();
		JCommander jc = new JCommander(obj);
		try {
			jc.parse(args);
		} catch (ParameterException ex) {
			jc.usage();
			System.exit(1);
		}
		try {
			obj.run();
		} catch (Exception ex) {
			ex.printStackTrace();
			System.exit(1);
		}
	}

	private RDFFormat rdfInFormat;
	private OutputStream outputStream = System.out;
	private BufferedWriter writer;
	private Map<String,Boolean> ignore = new HashMap<>();

	private void run() throws IOException, RDFParseException, RDFHandlerException,
			MalformedNanopubException, TrustyUriException {
		if (ignoreProperties != null) {
			for (String s : ignoreProperties.trim().split("\\|")) {
				if (!s.isEmpty()) ignore.put(s, true);
			}
		}
		for (File inputFile : inputNanopubs) {
			if (inFormat != null) {
				rdfInFormat = Rio.getParserFormatForFileName("file." + inFormat);
			} else {
				rdfInFormat = Rio.getParserFormatForFileName(inputFile.toString());
			}
			if (outputFile != null) {
				if (outputFile.getName().endsWith(".gz")) {
					outputStream = new GZIPOutputStream(new FileOutputStream(outputFile));
				} else {
					outputStream = new FileOutputStream(outputFile);
				}
			}

			writer = new BufferedWriter(new OutputStreamWriter(outputStream));

			MultiNanopubRdfHandler.process(rdfInFormat, inputFile, new NanopubHandler() {

				@Override
				public void handleNanopub(Nanopub np) {
					try {
						process(np);
					} catch (RDFHandlerException ex) {
						throw new RuntimeException(ex);
					} catch (IOException ex) {
						throw new RuntimeException(ex);
					} catch (ReflectiveOperationException ex) {
						throw new RuntimeException(ex);
					}
				}

			});

			writer.flush();
			if (outputStream != System.out) {
				writer.close();
			}
		}
	}

	private void process(Nanopub np) throws RDFHandlerException, IOException, ReflectiveOperationException {
		String topic;
		if (detectorClass != null && !detectorClass.isEmpty()) {
			String detectorClassName = detectorClass;
			if (!detectorClass.contains(".")) {
				detectorClassName = "org.petapico.npop.topic." + detectorClass;
			}
			TopicDetector td = (TopicDetector) Class.forName(detectorClassName).newInstance();
			topic = td.getTopic(np);
		} else {
			topic = getTopic(np, ignore);
		}
		writer.write(topic);
		if (outputNanopubUri) {
			writer.write(" " + np.getUri());
		}
		writer.write("\n");
	}

	public static String getTopic(Nanopub np, Map<String,Boolean> ignore) {
		Map<Resource,Integer> resourceCount = new HashMap<>();
		for (Statement st : np.getAssertion()) {
			Resource subj = st.getSubject();
			if (subj.equals(np.getUri())) continue;
			if (ignore.containsKey(st.getPredicate().stringValue())) continue;
			if (!resourceCount.containsKey(subj)) resourceCount.put(subj, 0);
			resourceCount.put(subj, resourceCount.get(subj) + 1);
		}
		int max = 0;
		Resource topic = null;
		for (Resource r : resourceCount.keySet()) {
			int c = resourceCount.get(r);
			if (c > max) {
				topic = r;
				max = c;
			} else if (c == max) {
				topic = null;
			}
		}
		return topic + "";
	}


	public interface TopicDetector {

		public String getTopic(Nanopub np);

	}

}

@@ -2082,32 +2082,324 @@
 xit(1);%0A%09%09%7D%0A%09%7D%0A%0A
+%09public static Topic getInstance(String args) throws ParameterException %7B%0A%09%09NanopubImpl.ensureLoaded();%0A%09%09if (args == null) args = %22%22;%0A%09%09args = args.trim() + %22 dummy-input-file%22;%0A%09%09Topic obj = new Topic();%0A%09%09JCommander jc = new JCommander(obj);%0A%09%09jc.parse(args.split(%22 %22));%0A%09%09return obj;%0A%09%7D%0A%0A
 %09private RDFForm
@@ -4362,16 +4362,8 @@
 c(np
-, ignore
 );%0A%09
@@ -4488,23 +4488,16 @@
 %09public 
-static 
 String g
@@ -4518,36 +4518,8 @@
 b np
-, Map%3CString,Boolean%3E ignore
 ) %7B%0A

Change argument handling for Topic class too