package org.aksw.simba.sparqlbench.hierarchical;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.aksw.simba.benchmark.Config;
import org.aksw.simba.benchmark.Similarity;
import org.aksw.simba.benchmark.clustring.QueryClustering;
import org.aksw.simba.benchmark.comparisons.AvgStats;
import org.aksw.simba.benchmark.log.operations.CleanQueryReader;
import org.aksw.simba.sparqlbench.featurevectors.FeatureVector;
import org.aksw.simba.sparqlbench.rdf.PrintBenchmark;
import org.openrdf.query.MalformedQueryException;
import org.openrdf.query.QueryEvaluationException;
import org.openrdf.repository.RepositoryException;
import org.openrdf.rio.RDFHandlerException;
import org.yooreeka.algos.clustering.hierarchical.Dendrogram;
import org.yooreeka.algos.clustering.hierarchical.SingleLinkAlgorithm;
import org.yooreeka.algos.clustering.model.DataPoint;

import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.query.QueryFactory;

public class Agglomerative {

	static Map<String, Double[]> normalizedVectors;


	public static void main(String[] args) throws RepositoryException, MalformedQueryException, QueryEvaluationException, RDFHandlerException, IOException {
		int noQueries = 5;
		String endpointURL = "http://localhost:8890/sparql";	 //The endpoint URL containing the containment RDF data
		String outputFile = "benchmark.txt";
		String queryFile = "../QALDBench-Generator/personalized-query.txt";
		runAgglomerative(noQueries,endpointURL,queryFile,outputFile) ;
	}

	public static void runAgglomerative(int noQueries, String endpointURL, String queryFile, String outputFile) throws RepositoryException, MalformedQueryException, QueryEvaluationException, IOException, RDFHandlerException {
		long curTime = System.currentTimeMillis();
		normalizedVectors  = FeatureVector.getNormalizedFeaturesVectors(endpointURL,queryFile);
		 System.out.println("Total questions to be considered: "+normalizedVectors.keySet().size());
			System.out.println("Total questions to be selected: "+noQueries);
			System.out.println("Clustering method: Agglomerative");
		Map<Integer, double[]> distanceTable = getDistanceTableNew(normalizedVectors);	
		DataPoint[] elements = new DataPoint[normalizedVectors.keySet().size()];
		Set<String> keySet = normalizedVectors.keySet();
		String[] keys = keySet.toArray(new String[keySet.size()]);

		for(int n=0; n<normalizedVectors.keySet().size(); n++) {
			double[] value = distanceTable.get(n);
			elements[n] = new DataPoint(keys[n], value);
		}
		SingleLinkAlgorithm sla = new SingleLinkAlgorithm(elements, distanceTable);
		Dendrogram dnd = sla.cluster();
		String[] queries = dnd.print(noQueries);

		Set<String> queriesIds = new HashSet<>();			

		Set<String> cSet = new HashSet<String>();
		for(int c = 0; c < queries.length; c++) {	
			String curCluster =queries[c];
			cSet.clear();

			String[] prts =  curCluster.replace("{", "").replace("}", "").replace("\n", "").split(",");
			for(String prt:prts) {
				cSet.add(prt);
			}
			if(cSet.size()==1)
				queriesIds.add(cSet.toArray()[0].toString());
			else
				queriesIds.add(new QueryClustering().getMostRepresentative(normalizedVectors, cSet));
			//			clusters.add(queries[c]);
		}
		System.out.println(queriesIds);		
//		for(String qry:queriesIds){
//			System.out.println("------------------\n"+qry);
//		}
		Set<String> benchmarkQueriesIds = new HashSet<String>(queriesIds); //we need to copy the query ids here/ later being used for similarity calculation. The next function will clear the queriesIds. So we have to copy before
		Double similarityScore = Similarity.getSimilarityScore(normalizedVectors,benchmarkQueriesIds);
		Double diversityScore = Similarity.getDiversityScore(normalizedVectors,benchmarkQueriesIds);
		String method = "Agglomerative";
		long exeTime = (System.currentTimeMillis()-curTime)/1000;
		PrintBenchmark.getRDF(queriesIds, outputFile, endpointURL,method,similarityScore,exeTime,normalizedVectors, diversityScore);
		System.out.println("Similarity Error : " + similarityScore);
		System.out.println("Diversity Score: " + diversityScore);
		System.out.println("Benchmark generation time (sec): "+exeTime);
		
	}

	/**
	 * Print benchmark queries into console
	 * @param queries queries
	 * @param outputDir output dir
	 * @throws IOException 
	 */
	public static void printBenchmarkQueries(Set<String> queries, String outputDir) throws IOException {
		System.out.println("Total Queries: "+ queries.size());
		int count = 1;
		BufferedWriter	bwq= new BufferedWriter(new FileWriter(outputDir+"queries.txt")); //queries writer
		BufferedWriter	bw= new BufferedWriter(new FileWriter(outputDir+"queries-stats.txt"));
		for(String queryStr:queries)
		{
			String stats = "";
			//System.out.println(queryStr);
			String [] qryParts = queryStr.split("Query String:");
			Query query = QueryFactory.create(qryParts[1].trim());
			stats = "#-------------------------------------------------------\n";
			bwq.write(stats);
			stats=stats+"Query No: "+count+"\n";
			if(query.isAskType())
				stats=stats+"Query Type: ASK\n";
			else if (query.isConstructType())
				stats=stats+"Query Type: SELECT\n";
			else if (query.isDescribeType())
				stats=stats+"Query Type: DESCRIBE\n";
			else if (query.isSelectType())
				stats=stats+"Query Type: SELECT\n";
			stats=stats+qryParts[0];
			//System.out.print(stats);
			bwq.write(qryParts[1].trim()+"\n");
			//System.out.println("Query String: "+qryParts[1].trim());
			stats=stats+"Query String: "+java.net.URLEncoder.encode(qryParts[1].trim(), "UTF-8")+"\n";
			bw.write(stats);
			count++;
		}
		bw.write("#--end---");
		bwq.close();
		bw.close();
	}

	private static Map<Integer, double[]> getDistanceTable(Map<String, Double[]> vectors) {
		Map<Integer, double[]> distance = new HashMap<>();	
		int size = vectors.keySet().size();
		for(int i=0; i<vectors.keySet().size(); i++) {
			double[] features = new double[size];
			int index = 0;
			for(int j=i; j<vectors.keySet().size(); j++) {					
				features[index] =new QueryClustering().getDistance(vectors.get(""+i), vectors.get(""+j));	
				index++;
			}
			distance.put(i, features);	
			size--;
		}		
		return distance;

	}
	private static Map<Integer, double[]> getDistanceTableNew(Map<String, Double[]> vectors) {
		Map<Integer, double[]> distance = new HashMap<>();	
		int size = vectors.keySet().size();
		int i = 0 ;
		Set<String> usedKeyComb = new HashSet<String>();
		for(String key: vectors.keySet()) {
			double[] features = new double[size];
			int index = 0;
			int j = 0;
			for(String key1: vectors.keySet()) {
				if(!usedKeyComb.contains(key+key1) && !usedKeyComb.contains(key1+key)){
				features[index] =new QueryClustering().getDistance(vectors.get(key), vectors.get(key1));	
				index++;
				j++;
				usedKeyComb.add(key+key1);
				}
			}
			
			distance.put(i, features);	
			i++;
			size--;
		}		
		return distance;

	}

	




}
