package uk.ac.ebi.biosamples.legacyxml;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.zip.GZIPInputStream;

import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.hateoas.Resource;
import org.springframework.stereotype.Component;
import org.xml.sax.Attributes;

import com.opencsv.CSVReader;

import uk.ac.ebi.biosamples.client.BioSamplesClient;
import uk.ac.ebi.biosamples.model.Sample;
import uk.ac.ebi.biosamples.service.XmlGroupToSampleConverter;
import uk.ac.ebi.biosamples.service.XmlSampleToSampleConverter;
import uk.ac.ebi.biosamples.utils.ThreadUtils;
import uk.ac.ebi.biosamples.utils.XmlFragmenter;
import uk.ac.ebi.biosamples.utils.XmlFragmenter.ElementCallback;

@Component
public class ImportXmlRunner implements ApplicationRunner {

	private final Logger log = LoggerFactory.getLogger(getClass());
	
	private final BioSamplesClient client;
	private final XmlFragmenter xmlFragmenter;
	private final XmlSampleToSampleConverter xmlSampleToSampleConverter;
	private final XmlGroupToSampleConverter xmlGroupToSampleConverter;
	
	private static final String DOMAIN="self.BioSamplesMigration";
	
	public ImportXmlRunner(BioSamplesClient client, XmlFragmenter xmlFragmenter,
						   XmlSampleToSampleConverter xmlSampleToSampleConverter, XmlGroupToSampleConverter xmlGroupToSampleConverter) {
		this.client = client;
		this.xmlFragmenter = xmlFragmenter;
		this.xmlSampleToSampleConverter = xmlSampleToSampleConverter;
		this.xmlGroupToSampleConverter = xmlGroupToSampleConverter;
	}
	
	@Override
	public void run(ApplicationArguments args) throws Exception {
		
		if (!"import-xml".equals(args.getNonOptionArgs().get(0))) {
			return;
		}
		
		Path inputXmlPath = Paths.get(args.getNonOptionArgs().get(1));

		try (InputStream is = new GZIPInputStream(new BufferedInputStream(Files.newInputStream(inputXmlPath)))) {

			Map<Element, Future<Resource<Sample>>> futures = new LinkedHashMap<>();

			ElementCallback callback = new ImportElementCallback(futures, 
					client, xmlSampleToSampleConverter, xmlGroupToSampleConverter);

			// this does the actual processing
			xmlFragmenter.handleStream(is, "UTF-8", callback);

			log.info("waiting for futures");

			// wait for anything to finish
			ThreadUtils.checkFutures(futures, 0);
		}
	}
	
	
	private Map<String,Set<String>> readCsv(Path path) throws IOException {
		Map<String, Set<String>> groupMembership = new HashMap<>();
		try (CSVReader csvReader = new CSVReader(Files.newBufferedReader(path))) {
			String[] nextRecord;
            while ((nextRecord = csvReader.readNext()) != null) {
            	String group = nextRecord[0];
            	for (int i = 1; i < nextRecord.length; i++) {
            		if (!groupMembership.containsKey(group)) {
            			groupMembership.put(group, new HashSet<>());
            		}
            		groupMembership.get(group).add(nextRecord[i]);
            	}
            }
		}
		return groupMembership;
	}
	
	private Map<String, Set<String>> reverse(Map<String, Set<String>> input) {
		Map<String, Set<String>> output = new HashMap<>();
		for (String key : input.keySet()) {
			for (String value : input.get(key)) {
				if (!output.containsKey(value)) {
					output.put(value, new HashSet<>());
				}
				output.get(value).add(key);
			}
		}
		return output;
	}
	
	public static class ImportElementCallback implements ElementCallback {

		
		private final Map<Element, Future<Resource<Sample>>> futures;
		private final BioSamplesClient client;
		private final XmlSampleToSampleConverter xmlSampleToSampleConverter;
		private final XmlGroupToSampleConverter xmlGroupToSampleConverter;

		public ImportElementCallback(Map<Element, Future<Resource<Sample>>> futures, BioSamplesClient client, 
				XmlSampleToSampleConverter xmlSampleToSampleConverter, XmlGroupToSampleConverter xmlGroupToSampleConverter) {
			this.futures = futures;
			this.client = client;
			this.xmlSampleToSampleConverter = xmlSampleToSampleConverter;
			this.xmlGroupToSampleConverter = xmlGroupToSampleConverter;
		}
		
		@Override
		public void handleElement(Element e) throws Exception {
			
			Sample sample;
			if (e.getName().equals("BioSample")) {
				sample = xmlSampleToSampleConverter.convert(e);
			} else if (e.getName().equals("BioSampleGroup")) {
				sample = xmlGroupToSampleConverter.convert(e);
			} else {
				return;
			}

			//need to specify domain
			sample = Sample.build(sample.getName(), sample.getAccession(), DOMAIN, 
					sample.getRelease(), sample.getUpdate(), 
					sample.getAttributes(), sample.getRelationships(), sample.getExternalReferences(),
					sample.getOrganizations(), sample.getContacts(), sample.getPublications());
			
			futures.put(e, client.persistSampleResourceAsync(sample, false, true));

			//make sure we don't have too many futures
			ThreadUtils.checkFutures(futures, 100);
		}

		@Override
		public boolean isBlockStart(String uri, String localName, String qName, Attributes attributes) {
			return qName.equals("BioSample") || qName.equals("BioSampleGroup");
		}
		
	}

}

@@ -2721,1027 +2721,8 @@
 %09%7D%0A%09
-%0A%09%0A%09private Map%3CString,Set%3CString%3E%3E readCsv(Path path) throws IOException %7B%0A%09%09Map%3CString, Set%3CString%3E%3E groupMembership = new HashMap%3C%3E();%0A%09%09try (CSVReader csvReader = new CSVReader(Files.newBufferedReader(path))) %7B%0A%09%09%09String%5B%5D nextRecord;%0A            while ((nextRecord = csvReader.readNext()) != null) %7B%0A            %09String group = nextRecord%5B0%5D;%0A            %09for (int i = 1; i %3C nextRecord.length; i++) %7B%0A            %09%09if (!groupMembership.containsKey(group)) %7B%0A            %09%09%09groupMembership.put(group, new HashSet%3C%3E());%0A            %09%09%7D%0A            %09%09groupMembership.get(group).add(nextRecord%5Bi%5D);%0A            %09%7D%0A            %7D%0A%09%09%7D%0A%09%09return groupMembership;%0A%09%7D%0A%09%0A%09private Map%3CString, Set%3CString%3E%3E reverse(Map%3CString, Set%3CString%3E%3E input) %7B%0A%09%09Map%3CString, Set%3CString%3E%3E output = new HashMap%3C%3E();%0A%09%09for (String key : input.keySet()) %7B%0A%09%09%09for (String value : input.get(key)) %7B%0A%09%09%09%09if (!output.containsKey(value)) %7B%0A%09%09%09%09%09output.put(value, new HashSet%3C%3E());%0A%09%09%09%09%7D%0A%09%09%09%09output.get(value).add(key);%0A%09%09%09%7D%0A%09%09%7D%0A%09%09return output;%0A%09%7D%0A
 %09%0A%09p

remove defunct code