package eu.dnetlib.data.information.similarity;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;

import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.junit.Before;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

import com.thoughtworks.xstream.XStream;

import pl.edu.icm.driver.is.ISUtils;

import eu.dnetlib.data.mdstore.IMDStoreService;
import eu.dnetlib.data.sts.das.IDataAccessService;
import eu.dnetlib.enabling.resultset.rmi.ResultSetService;

public class SimilarityManualTest {

	String backendServiceLocation = "http://146.48.85.149:8680/icm-data-information-similarity-latest/services/similarityServiceBackend";
	String frontendServiceLocation = "http://146.48.85.149:8680/icm-data-information-similarity-latest/services/similarityService";
	ISimilarityServiceBackend backend;
	ISimilarityService frontend;
	
	String mdStoreServiceLocation = "http://146.48.87.232:8180/dnet-mdstore/services/MDStoreService";
	IMDStoreService mdStoreService;
	
	@Before
	public void init() {
		JaxWsProxyFactoryBean backendFactory = new JaxWsProxyFactoryBean();
		backendFactory.setServiceClass(ISimilarityServiceBackend.class);
		backendFactory.setAddress(backendServiceLocation);
		backend = (ISimilarityServiceBackend) backendFactory.create();
		
		JaxWsProxyFactoryBean frontendFactory = new JaxWsProxyFactoryBean();
		frontendFactory.setServiceClass(ISimilarityService.class);
		frontendFactory.setAddress(frontendServiceLocation);
		frontend = (ISimilarityService) frontendFactory.create();
		
		JaxWsProxyFactoryBean mdStoreFactory = new JaxWsProxyFactoryBean();
		mdStoreFactory.setServiceClass(IMDStoreService.class);
		mdStoreFactory.setAddress(mdStoreServiceLocation);
		mdStoreService = (IMDStoreService) mdStoreFactory.create();
	}
	
//	@Test
	public void testBackendDelete() {
		try {
			backend.delete("someId");
			fail("exception expected");
		} catch (SimilarityServiceException e) {
			System.out.println(e.getMessage());
//			ok
		}
	}
	
//	@Test
	public void testBackendCreate() throws Exception {
		String format ="DMF";
		String layout = "index";
		String interpretationName = "driver";
		backend.create(format, layout, interpretationName);
	}
	
//	@Test
	public void testBackendFeed() throws Exception {
		String ixId = "someId";
		String resultSetEPRBase64Enc = "invalidResultSetEPRBase64Enc";
		backend.feed(ixId, resultSetEPRBase64Enc);
	}
	
//	@Test
	public void testFrontendForNonExistingId() throws Exception {
		String id = "invalidId";
		DNetSimilarityResult[] results = frontend.findSimilarDirectly(id, false);
		assertNull(results);
	}


//	@Test
	public void testListRecordsIdsFromMDStoreAndPersistAsCSV() throws Exception {
		List<String> mdStores = mdStoreService.getListOfMDStores();
//		some of mdstores are dc;
		Set<String> recordIds = new HashSet<String>(); 
		for (String mdId : mdStores) {
			System.out.println("currentMDStore: " + mdId);
			List<String> records = null;
			int startElement = 0;
			int packageSize = 20;
			boolean isLastPackage = false;
			while (!isLastPackage) {
				records = mdStoreService.deliverMDRecordsDirect_Range(mdId, 
						startElement, startElement + packageSize);
//				"toElement" is inclusive
				startElement = startElement + packageSize + 1;
				if (records ==null || records.size()==0) {
					isLastPackage = true;
				}
				if (records!=null) {
					for (String record: records) {
						String recordId = extractRecordId(record);
						recordIds.add(recordId);
					}
				}
//				FIXME remove after tests;
//				break;
			}
			System.out.println("gathered ids: " + recordIds.size());
		}
		
		FileOutputStream fos = new FileOutputStream("/var/tmp/recordIds.xml");
		XStream xstream = new XStream();
		xstream.toXML(recordIds, fos);
		
	}
	
	protected String extractRecordId(String content) {
//		very simple, ad hoc, implementation
		String start = "<dri:objIdentifier>";
		String finish = "</dri:objIdentifier>";
//		String start = "<dri:recordIdentifier>";
//		String finish = "</dri:recordIdentifier>";
		if (content!=null && content.contains(start)) {
			int startIdx = content.indexOf(start);
			if (startIdx>0) {
				return content.substring(startIdx + start.length(), 
						content.indexOf(finish));
			}
		}
		return null;
	}
	
//	@Test
	public void testFrontend() throws Exception {
//		known ids:
//		3a0d8d3c-8a23-433f-849d-dcaf547536b5_UmVwb3NpdG9yeVNlcnZpY2VSZXNvdXJjZXMvUmVwb3NpdG9yeVNlcnZpY2VSZXNvdXJjZVR5cGU=::oai:etheses.nottingham.ac.uk:652
//		3a0d8d3c-8a23-433f-849d-dcaf547536b5_UmVwb3NpdG9yeVNlcnZpY2VSZXNvdXJjZXMvUmVwb3NpdG9yeVNlcnZpY2VSZXNvdXJjZVR5cGU=::oai:etheses.nottingham.ac.uk:653
		String id = "115-b80a8e4f-8ec2-492e-86e6-614dac98486c_UmVwb3NpdG9yeVNlcnZpY2VSZXNvdXJjZXMvUmVwb3NpdG9yeVNlcnZpY2VSZXNvdXJjZVR5cGU=::oai:doc.utwente.nl:57742";
		DNetSimilarityResult[] results = frontend.findSimilarDirectly(id, false);
		assertNotNull(results);
		System.out.println("similar result lenght: " + (results!=null?results.length:"0"));
		for (DNetSimilarityResult current : results) {
			System.out.println("id :" + current.getId() + " score: " + current.getScore());
		}
	}
	
	@Test
	public void findTheMostSimilar() throws Exception {
//		key = score, value = id of document for which similarity was run
		SortedMap<Float, List<String>> topSimilar = new TreeMap<Float, List<String>>();
		int sizeLimit = 10;
		
		Float treshold = new Float(0.6);
//		number of first X elements to evaluate average for
		int firstXforTreshold = 5;
		
		String filePath = "/var/tmp/index.xml";
		// Standard of reading a XML file
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(true);
		DocumentBuilder builder;
		Document doc = null;
		XPathExpression expr = null;
		builder = factory.newDocumentBuilder();
		doc = builder.parse(filePath);
		// Create a XPathFactory
		XPathFactory xFactory = XPathFactory.newInstance();
		// Create a XPath object
		XPath xpath = xFactory.newXPath();
		// Compile the XPath expression
		expr = xpath.compile("//field[@name='__DOCID__']/val/text()");
		// Run the query and get a nodeset
		Object result = expr.evaluate(doc, XPathConstants.NODESET);
		// Cast the result to a DOM NodeList
		NodeList nodes = (NodeList) result;
		System.out.println("extracted nodes count: " + nodes.getLength());
		for (int i=0; i<nodes.getLength();i++){
			String id = nodes.item(i).getNodeValue();
			if (id.length()>100) {
				DNetSimilarityResult[] results = frontend.findSimilarDirectly(id, false);
				if (results!=null && results.length>0) {
					if (results.length>=firstXforTreshold) {
						float sum = 0;
						for (int k=0; k<firstXforTreshold; k++) {
							sum+=results[k].getScore();
						}
						float average = sum/firstXforTreshold;
						if (average>treshold) {
//							might be more than one entry for given key
							if (topSimilar.get(average)==null) {
								topSimilar.put(average, new ArrayList<String>());
							}
							topSimilar.get(average).add(id);
							if (topSimilar.size()>sizeLimit) {
//								removing the least valuable entry
								topSimilar.remove(topSimilar.firstKey());
							}
						}
						
					} else {
						System.out.println("not enough elements to evaluate average " +
								"when finding similarities for: " + id);
					}
				} else {
					System.out.println("no results for: " + id);
				}
			} else {
				System.out.println("id omitted: " + id);
			}
			if (i%100==0 && i>0) {
				System.out.println("=========== summary after processing " + i + " elements ===========");
				int idx = 0;
				for (Float key : topSimilar.keySet()) {
					System.out.println("[" + (++idx) + "];" + key + ";" + 
							getCSVs(topSimilar.get(key)));
				}
			}
		}
	}
	
	protected String getCSVs(List<String> ids) {
		if (ids==null || ids.size()==0) {
			return "";
		} else if (ids.size()==1) {
			return ids.get(0);
		} else {
			StringBuffer strBuff = new StringBuffer();
			for (int i=0; i<ids.size();i++) {
				if (i>0) {
					strBuff.append(';');
				}
				strBuff.append(ids.get(i));
			}
			return strBuff.toString();
		}
	}
	
}
