package jp.ac.osaka_u.sanken.sparql.plugin.compare;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.hp.hpl.jena.rdf.model.RDFNode;
import com.ibm.icu.text.SimpleDateFormat;

import jp.ac.osaka_u.sanken.sparql.EndpointSettings;
import jp.ac.osaka_u.sanken.sparql.EndpointSettingsManager;
import jp.ac.osaka_u.sanken.sparql.PlainSparqlAccessor;
import jp.ac.osaka_u.sanken.sparql.SparqlQueryListener;

public class CompareSubject {
	
	private File csvFile;
	
	private File propertyFile;
	private HashMap<String, PlainSparqlAccessor> accessorMap;
	
	private static final String SEPARATOR = ",";
	
	private Thread thread;
	private boolean finalizeThread = false;
	private SparqlQueryListener listener;

	public CompareSubject(File csvFile, File endpointFile){
		this(csvFile, endpointFile, null);
	}
		
	public CompareSubject(File csvFile, File endpointFile, SparqlQueryListener listener){
		this.csvFile = csvFile;
		this.propertyFile = endpointFile;
		this.listener = listener;
		
		this.accessorMap = new HashMap<String, PlainSparqlAccessor>();
	}
	
	
	public void outputResult(File out, CompareResultListener listener){
		thread = new QueryThread(new Object[]{out}, listener){
			public void run(){
				try {
					File file = (File)((Object[])getOption())[0];
					getCompareResultListener().resultReceived(outputResult(file));
				} catch(Exception e){
					throw new RuntimeException(e);
				}
			}
		};
		thread.setUncaughtExceptionHandler(listener);
		thread.start();
	}

	
	private boolean outputResult(File out) throws IOException {
		if (out.getParentFile() != null && !out.getParentFile().exists()){
			out.getParentFile().mkdirs();
		}
		out.createNewFile();
		if (!out.canWrite()){
			return false;
		}
		
		return outputResult(new PrintWriter(new OutputStreamWriter(new FileOutputStream(out), "SJIS")));
	}
	
	public void stop(){
		println(getCurrentTime()+"Stop Request");
		finalizeThread = true;
	}

	
	private boolean outputResult(PrintWriter out){
		HashMap<String, List<String>> propertyHash = readPropertyFile();
		
		getResources(propertyHash, out);
		
		out.flush();
		out.close();
		
		println(getCurrentTime()+"query end.");

		return true; // TODO
	}

	private HashMap<String, List<String>> readPropertyFile(){
		HashMap<String, List<String>> propertyHash = new HashMap<String, List<String>>();
		List<String> contents = FileUtil.readFileText(propertyFile, "UTF-8");
		
		for (String line : contents){
			List<String> property = FileUtil.splitLine(line, SEPARATOR);
			if (property != null && property.size() > 1){
				// 一カラム目はendpoint
				List<String> properties = new ArrayList<String>();
				propertyHash.put(property.get(0), properties);
				for (int i=1; i<property.size(); i++){
					properties.add(property.get(i));
				}
			}
		}
		
		return propertyHash;
	}
	

	private void println(){
		println("");
	}

	private void println(String str){
		print(str + "\n");
	}

	private void print(String str){
		if (listener != null){
			listener.sparqlExecuted(str);
		}
	}

	
	private void getResources(HashMap<String, List<String>> propertyHash, PrintWriter out){
		List<String> endpoints = null;
		List<String> files = FileUtil.readFileText(csvFile, "SJIS");
		
		println();
		println(getCurrentTime()+"query start.");
		
		if (files != null && files.size() > 0){
			// 一行目はヘッダ
			endpoints = getEndpoints(files.get(0));
			
			setEndpoint(endpoints);
		}
		
		// header出力
		StringBuilder line = new StringBuilder();
		line.append("");
		line.append(SEPARATOR);
		for (String endpoint : endpoints){
			line.append(endpoint);
			line.append(SEPARATOR);
			line.append("");
			line.append(SEPARATOR);
			line.append("");
			line.append(SEPARATOR);
		}
		out.append(line.toString());
		out.append("\n");

		for (int i=1; i<files.size(); i++){
			if (!finalizeThread){
				getResource(endpoints, FileUtil.splitLine(files.get(i), SEPARATOR), propertyHash, out);
			} else {
				break;
			}
		}
		
	}
	
	/**
	 * 一行ごとに
	 * @param endpoints
	 * @param contents
	 * @param propertyHash
	 * @param out
	 */
	private void getResource(List<String> endpoints, List<String> contents, HashMap<String, List<String>> propertyHash, PrintWriter out){
		int index=1;
		int max = 0;
		
		String label = contents.get(0);
		List<HashMap<String, String>> ret = new ArrayList<HashMap<String,String>>();

		print(getCurrentTime()+"searching ["+label+"]");

		for (String endpoint : endpoints){
			print("*");
			HashMap<String, String> properToResultHash = new HashMap<String, String>();
			ret.add(properToResultHash);
			// エンドポイントごとのリソース取得
			List<String> properties = propertyHash.get(endpoint);
			if (properties != null){
				HashMap<String, List<String>> results = getResource(endpoint, contents.get(index), properties);
				if (max < results.size()){
					max = results.size();
				}
				for (String property : results.keySet()){
					// propertyごとの結果
					String result = getResult(results.get(property));
					properToResultHash.put(property, result);
				}
			}
			index++;
		}
		println();
		
		// TODO properToResultHashの最大値を取得する
		// その数だけ行を追加する。
		
		/*      1,   endpoint1,          ,   endpoint2,           ,.....
		 * label1, property1-1, object1-1, property2-1, object2-1 ,....o　　↑
		 * label1, property1-2, object1-2, property2-2, object2-2 ,....o この範囲を司る
		 * label1, property1-3, object1-3,            ,           ,....o　　↓
		 * label2, property1-4, object1-1, property2-3, object2-3 ,.....
		 * label2, property1-5, object1-2, property2-4, object2-4 ,.....
		 * label2,            ,          , property2-5, object2-5 ,.....
		 */
		
		
		for (int i=0; i<max; i++){
			StringBuilder line = new StringBuilder();
			line.append(label);
			line.append(SEPARATOR);
			int lp = 1;
			for (HashMap<String, String> values : ret){
				String[] hoge = values.keySet().toArray(new String[]{});
				if (hoge.length > i && !contents.get(lp).trim().isEmpty()){
					String resultProperty = hoge[i];
					String resultObject = values.get(resultProperty);
					line.append(contents.get(lp));
					line.append(SEPARATOR);
					line.append(resultProperty);
					line.append(SEPARATOR);
					line.append(resultObject);
					line.append(SEPARATOR);
				} else {
					line.append(SEPARATOR);
					line.append(SEPARATOR);
					line.append(SEPARATOR);
				}
				lp++;
			}
			out.append(line.toString());
			out.append("\n");
		}
		
	}
	
	private String getResult(List<String> result){
		if (result.size() == 0){
			return "";
		}
		if (result.size() == 1){
			return result.get(0);
		}

		StringBuilder sb = new StringBuilder();
		for (int i=0; i<result.size()-1; i++){
			sb.append(result.get(i));
			sb.append("<>"); // TODO セパレータ
		}
		sb.append(result.get(result.size()-1));
		
		return sb.toString();
	}
	
	/**
	 * 
	 * @param endpoint
	 * @param content
	 * @param properties
	 * @return propertyとそのresultのmap
	 */
	private HashMap<String, List<String>> getResource(String endpoint, String content, List<String> properties){
		HashMap<String, List<String>> ret = new HashMap<String, List<String>>();
		PlainSparqlAccessor sa = accessorMap.get(endpoint);
		for (String property : properties){
			List<String> res = new ArrayList<String>();
			ret.put(property, res);

			if (!content.trim().isEmpty()){
				String query = makeQuery(content, property);
				try {
					List<Map<String, RDFNode>> results = sa.executeQuery(query);
					if (results != null){
						for (Map<String, RDFNode> result : results){
							RDFNode s = result.get("o");
							res.add(s.toString());
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		
		return ret;
	}
	
	private String makeQuery(String s, String p){
		return "select distinct ?o where {\n" +
				"<" + s.trim() + "> <" + p.trim() + "> ?o \n" +
				"}";

	}
	
	public void addEndpoint(String endpoint){
		EndpointSettings settings = EndpointSettingsManager.instance.getSetting(endpoint);
		
		PlainSparqlAccessor sa = new PlainSparqlAccessor(settings);
		accessorMap.put(endpoint, sa);
	}

	private void setEndpoint(List<String> endpoints){
		for (String ep : endpoints){
			addEndpoint(ep);
		}
	}
	
	private List<String> getEndpoints(String line){
		List<String> headers = FileUtil.splitLine(line, SEPARATOR);
		
		if (headers.size() > 0){
			headers.remove(0);
		}
		
		return headers;
	}

	private String getCurrentTime(){
		Timestamp time = new Timestamp(new Date().getTime());
		
		return new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(time) + ":";
	}

	
	private class QueryThread extends Thread {
		
		private Object option;
		private CompareResultListener listener;
		

		public QueryThread(Object option, CompareResultListener listener){
			this.option = option;
			this.listener = listener;
		}

		protected Object getOption(){
			return this.option;
		}
		
		protected CompareResultListener getCompareResultListener(){
			return this.listener;
		}

	}
	
	public static void main(String[] args){
		String epFile = "C:\\Users\\kato\\Desktop\\out\\endp_prop.txt";
		String inFile = "C:\\works\\daily\\20130926\\input\\動詞索引.csv";
		String outFile = "C:\\works\\daily\\20130926\\input\\動詞索引_out.csv";
		
		try {
			/*
			for (int i=0; i<args.length; i++){
				if (args[i].startsWith("-") && i < (args.length-1)){
					
					if (args[i].equals("-o")){
						outFile = args[i+1];
					}
					if (args[i].equals("-i")){
						inFile = args[i+1];
					}
					if (args[i].equals("-e")){
						epFile = args[i+1];
					}
					i++;
				}
				
			}
			*/
			
			EndpointSettings[] settings = EndpointSettings.inputXML(new FileInputStream("settings.xml"));
			if (settings != null){
				EndpointSettingsManager.instance.setSettings(settings);
				
			}

			
			if (epFile == null || inFile == null || outFile == null){
				System.out.println("usage:java -jar SparqlTestTool.jar Compare -e endpointFile -i inputFile -o outputFile -t type(s:subject/l:label object/o:all object)");
			} else {
				CompareSubject ct = new CompareSubject(new File(inFile), new File(epFile));
				ct.outputResult(new File(outFile));
			}
		} catch (UnsupportedEncodingException e) {
			// TODO 自動生成された catch ブロック
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			// TODO 自動生成された catch ブロック
			e.printStackTrace();
		} catch (IOException e) {
			// TODO 自動生成された catch ブロック
			e.printStackTrace();
		}
		
	}

}
