/*
 * Copyright (C) 2009 by Instytut Podstaw Informatyki Polskiej
 * Akademii Nauk (IPI PAN; Institute of Computer Science, Polish
 * Academy of Sciences; cf. www.ipipan.waw.pl).  All rights reserved.
 *
 * This file is part of WSDDE.
 *
 * WSDDE is free software: it may be distributed and/or modified under
 * the terms of the GNU General Public License version 3 as published
 * by the Free Software Foundation and appearing in the file doc/gpl.txt
 * included in the packaging of this file.
 *
 * A commercial license is available from IPI PAN (contact
 * Michal.Ciesiolka@ipipan.waw.pl or ipi@ipipan.waw.pl for more
 * information).  Licensees holding a valid commercial license from IPI
 * PAN may use this file in accordance with that license.
 *
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
 * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE.
 */
package wsdde;


import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Random;
import java.util.Set;
import java.util.Vector;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import wsdde.general.DatabaseHelper;
import wsdde.general.Utils;
import wsdde.generator.FeatureGenerator;


/**
 * @author Rafał Młodzki
 * @author Marcin Młodzki
 *
 */

public class WSDMethodResultRow {
	
	public HashMap<String, String> fGParamValues;
	
	//contains parameters values in structured way
	private HashMap<String, HashMap<String,String>> fGParametersStruct;
	
	public HashMap<String, String> methodPreferences;

	Element xmlElement;


	Element outputXMLElement;
	Document outputDocument;
	
	static public DatabaseHelper dh;

	private double result;
	
	public WSDMethodResultRow(Element WSDMethod, DatabaseHelper dbh){
		result = 0;
		xmlElement = WSDMethod;
		dh = dbh;
		methodPreferences = new HashMap<String, String>();
		methodPreferences.put(DatabaseHelper.EXPERIMENT_ID, dh.getExperimentID());
		initFromElement();
	}
	
	/*
	 * 
	 */
	public WSDMethodResultRow(ResultSet rs, DatabaseHelper dbh) throws SQLException {
		dh = dbh;
		methodPreferences = new HashMap<String, String>();

		String [] colNames = dh.getColNames();
		for(int i = 0; i < colNames.length; i++){
			String colName = colNames[i];
			try{
				methodPreferences.put(colName, rs.getString(colName));
			}catch (Exception e) {
				//TODO warning!
				methodPreferences.put(colName, "");				
			}
		}
		
		LinkedHashSet<String> fgColumnNames = dh.getFGColumnNames();
		
		fGParamValues = new HashMap<String, String>();
		for(String columnName: fgColumnNames){
			String columnValue = rs.getString(columnName);
			fGParamValues.put(columnName, columnValue);
		}
	}
	

	public String getMethodParamValue(String methodParamName){
		try{
		return methodPreferences.get(methodParamName);
		}
		catch(Exception e){
			System.out.print(e.getMessage());
			e.printStackTrace();
			System.exit(1);
			return "";
		}
	}
		
	public void setResult(Double res){
		this.result = res;
		this.methodPreferences.put("result", Double.toString(result));
	}
	
	/**
	 * 
	 * tylko do testów ustala losowy wynik 
	 */
	public void randomResult(){
		Random generator = new Random();
		Double res = generator.nextDouble();
		setResult(res);
	}
	
	public Element generateOutputXMLNode() {
		//initialize variables
		final String CV = "cv:";
		final String PATH = "path:";
		
		String selection = methodPreferences.get(DatabaseHelper.SELECTION);
		String machineLearning = methodPreferences.get(DatabaseHelper.MACHINE_LEARNING);
		String corpusTraining = methodPreferences.get(DatabaseHelper.CORPUS_TRAINING);
		String corpusTest = methodPreferences.get(DatabaseHelper.CORPUS_TEST);
	
		outputDocument = Utils.createDomDocument();
		Element wsdMethodElement = outputDocument.createElement("wsd_method");
		Element selectionElement = outputDocument.createElement("selection");
		Element machineLearningElement = outputDocument.createElement("machine_learning");
		Element corpusElement = outputDocument.createElement("corpus");
		Element corpusTrainingElement = outputDocument.createElement("training");
		Element corpusTestElement = outputDocument.createElement("test");
		Element corpusCVElement = outputDocument.createElement("cv");
		Element featureGeneratorElement = outputDocument.createElement("feature_generators");
		
		try{
			int num = Integer.parseInt(corpusTest.substring(CV.length()));
			corpusCVElement.setTextContent(corpusTraining);
			corpusCVElement.setAttribute("fold", Integer.toString(num));
			corpusElement.appendChild(corpusCVElement);
		}
		catch(NumberFormatException e){
			corpusTrainingElement.setTextContent(corpusTraining);
			corpusTestElement.setTextContent(corpusTest.substring(PATH.length()));
			corpusElement.appendChild(corpusTrainingElement);
			corpusElement.appendChild(corpusTestElement);
		}
		
		selectionElement.appendChild(outputDocument.createCDATASection(selection));
		machineLearningElement.appendChild(outputDocument.createCDATASection(machineLearning));
		
		// add generators
		
		initFGParametersStruct();
		Set<String> fGSet = fGParametersStruct.keySet();
		for(String fG : fGSet){
//			Element fGElement = outputDocument.createElement("feature_generator");
			Element fGElement = outputDocument.createElement(fG);
//			fGElement.setAttribute("name",fG);
			Set<String> paramSet = fGParametersStruct.get(fG).keySet();
			for(String param : paramSet){
				Element paramElement = outputDocument.createElement("param");
				paramElement.setAttribute("name", param);
				paramElement.setAttribute("value", fGParametersStruct.get(fG).get(param));
				fGElement.appendChild(paramElement);
			}
			featureGeneratorElement.appendChild(fGElement);
		}
		
		wsdMethodElement.appendChild(corpusElement);
		wsdMethodElement.appendChild(selectionElement);
		wsdMethodElement.appendChild(machineLearningElement);
		wsdMethodElement.appendChild(featureGeneratorElement);
		
		wsdMethodElement.setAttribute("id", methodPreferences.get(DatabaseHelper.METHOD_ID));
		
		outputDocument.appendChild(wsdMethodElement);

		//these to lines are only for test
//		String xmlString = Utils.stringFromXML(outputDocument);
//		System.out.println(xmlString);

		return wsdMethodElement;
	}
	
	/*
	 * column name has a structure fg_wsddegeneratorname_wsdparameter
	 */
	private void initFGParametersStruct(){
		if(fGParametersStruct!=null){ 
			return; //parameter structure already initialized
		}
		fGParametersStruct = new HashMap<String, HashMap<String,String>>();
		Set<String> s = fGParamValues.keySet();
		for(String fGParam: s){
			String delimiter = "_";
			String [] temp = fGParam.split(delimiter,3);
			String fGMethodName = temp[1];			
			String fGParamName = temp[2];
			String fGParamVal = fGParamValues.get(fGParam);
			HashMap<String,String> hs;
			if(fGParametersStruct.containsKey(fGMethodName)){
				hs = fGParametersStruct.get(fGMethodName);
			} else{
				hs = new HashMap<String, String>();
				fGParametersStruct.put(fGMethodName, hs);
			}
			hs.put(fGParamName, fGParamVal);
		}
	}
	

	private void initFromElement(){
		try{
			methodPreferences.put(DatabaseHelper.METHOD_ID, xmlElement.getAttribute("id"));
		}
		catch(Exception e){
			methodPreferences.put(DatabaseHelper.METHOD_ID, "");
		}
		initFGParamValues();
		initSelection();
		initCorpus();
		initMachineLearning();
	}
	
	public void setDataBaseHelper(DatabaseHelper dbh){
		dh = dbh;
	}
	
	public void insertRow() {
		try {
			dh.insertWSDMethodResultRow(this);
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	private void initMachineLearning() {
		Element ml = (Element)xmlElement.getElementsByTagName("machine_learning").item(0);
		String machineLearning = ml.getTextContent();
		methodPreferences.put(DatabaseHelper.MACHINE_LEARNING, machineLearning);
	}

	//TODO change to constants
	private void initCorpus() {
		String corpusTraining;
		String corpusTest;
		Element training = (Element)xmlElement.getElementsByTagName("training").item(0);
		if(training==null){
			Element cv = (Element)xmlElement.getElementsByTagName("cv").item(0);
			corpusTraining = cv.getTextContent();
			corpusTest = "cv:"+cv.getAttribute("fold");
		}
		else {
			corpusTraining = training.getTextContent();
			Element test = (Element)xmlElement.getElementsByTagName("test").item(0);
			corpusTest = "path:"+test.getTextContent();
		}
		methodPreferences.put(DatabaseHelper.CORPUS_TRAINING, corpusTraining);
		methodPreferences.put(DatabaseHelper.CORPUS_TEST, corpusTest);
	}

	private void initSelection() {
		Element ml = (Element) xmlElement.getElementsByTagName("selection").item(0);
		String selection = ml.getTextContent();
		methodPreferences.put(DatabaseHelper.SELECTION, selection);
	}
	
	private void initFGParamValues(){
		fGParamValues = new HashMap<String, String>();
//		NodeList gens = xmlElement.getElementsByTagName("feature_generator");
		Element fGenerators = (Element)xmlElement.getElementsByTagName("feature_generators").item(0);
		Vector<Element> featureGensVector = Utils.getChildElementsVector(fGenerators);
		//TODO POPRAWIĆ NA NOWY SYSTEM ZAPISU
		for(int i = 0; i < featureGensVector.size(); i++){
			Element fg = (Element)featureGensVector.get(i);
//			String methodName = fg.getAttribute("name");
			String methodName = fg.getNodeName();
			NodeList params = fg.getElementsByTagName("param");
			for(int j = 0; j < params.getLength(); j++){
				String paramName = ((Element)params.item(j)).getAttribute("name");
				String paramValue = ((Element)params.item(j)).getAttribute("value");
				fGParamValues.put(DatabaseHelper.FG_PREFIX+methodName+"_"+paramName, paramValue);
			}
		}
	}
/*	public double getResult() {
		return result;
	}*/
	public String getResult(){
		return Double.toString(result);
	}
	
	public Vector<WSDMethodResultRow> getSimilarRows(int quantity) {
		initFGParametersStruct(); // inits struct
		Element element = this.generateOutputXMLNode();
		Vector<WSDMethodResultRow> ret = new Vector<WSDMethodResultRow>();
		Set<String> fGSet = fGParametersStruct.keySet();
		for(String fG : fGSet){
			HashMap<String, String> param = fGParametersStruct.get(fG);
			Vector<HashMap<String, String>> params = WSDMethodResultRow.getParametersOption(fG, param, quantity);
			for(int i = 0; i < params.size(); i++){
				WSDMethodResultRow wsdmrr = new WSDMethodResultRow(element,dh);
				wsdmrr.initFGParametersStruct();
				wsdmrr.fGParametersStruct.remove(fG);
				wsdmrr.fGParametersStruct.put(fG, params.get(i));
				ret.add(wsdmrr);
			}
		}
		// TODO Auto-generated method stub
		return ret;
	}
	/*
	 * 
	 * ZAŚLEPKA -- ZASTĄPIĆ FUNKCJĄ KTÓRA 
	 * 
	 */
	static Vector<HashMap<String, String>> getParametersOption(String fGName, HashMap<String, String> params, int quantity){
		return FeatureGenerator.getParametersOption(fGName, params, quantity);
		/*Vector<HashMap<String,String>> ret = new Vector<HashMap<String,String>>(); 
		for(int i = 0; i < quantity; i++){
			ret.add(FeatureGenerator.getChangedParams(fGName, params));
		}*/
		//return ret;
		/*Vector<HashMap<String,String>> ret = new Vector<HashMap<String,String>>();
		for(int i = 0; i < quantity; i++){
			Set<String> pSet = params.keySet();
			HashMap<String,String> hm = new HashMap<String, String>();
			for(String param : pSet){
				hm.put(param, params.get(param)+"popr"+Integer.toString(i));
			}
			ret.add(hm);
		}
		return ret;*/
	}
	
}
