/*
 * 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.util.Iterator;
import java.util.Vector;

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

import weka.classifiers.Classifier;
import weka.filters.Filter;
import wsdde.corpus.WSDCorpus;
import wsdde.general.Utils;
import wsdde.generator.FeatureGenerator;


public class WSDMethod extends Vector<FeatureGenerator> implements Runnable {

	private static final long serialVersionUID = -1455331906805868042L;

	String id;
	
	String ml; //machine learning
	String sa; //selection

	int maxExecutionTimeMs = 10000000;
	
	Classifier classifier = null;
	Filter filter = null;
	String trainingSetName, testSetName;
	/**
	 * number of folds in cv;
	 * if <2 -- use test set
	 * if =>number of instances -- use cv leave-one-out  
	 */
	private int cvFold;
	private boolean isCV;
	
	WSDCorpus trainingSet;// = TFG.corpus();
	WSDCorpus testSet;
	WSDExperiment wsdexp;
	
	public EvaluationResult results;

	private Element wsdMethodXml;

	private boolean writeModels = false;
	
	private WSDMethodResultRow wsdmrr = null;
	
	
	public WSDMethod(Element wsdMethodXml, WSDExperiment wsdexp) {
		this.wsdexp = wsdexp;
		this.wsdMethodXml = wsdMethodXml;
		
		Element fgsEl = (Element)wsdMethodXml.getElementsByTagName("feature_generators").item(0);
		Vector<Element> v = Utils.getChildElementsVector(fgsEl);
		for (Element fg : v) {
			add(FeatureGenerator.getGenerator(fg));
		}
		/*NodeList featureGeneratorsList = wsdMethodXml.getElementsByTagName("feature_generator");
		for (int i=0; i<featureGeneratorsList.getLength(); i++) {
			Element featureGeneratorElement = (Element)featureGeneratorsList.item(i);
			add(FeatureGenerator.getGenerator(featureGeneratorElement));
		}*/
		
//		for (FeatureGenerator fg : this) {
//			System.out.println(fg.description());
//		}
//		
		
		
		id = wsdMethodXml.getAttribute("id");
		if (wsdMethodXml.hasAttribute("max_method_time")) {
				maxExecutionTimeMs  = Integer.parseInt(wsdMethodXml.getAttribute("max_method_time"));
		}

		//ML
		ml= ((Element)wsdMethodXml.getElementsByTagName("machine_learning").item(0)).getTextContent();
		
		//MAXTIME
		
		//Selection //A JAK NIC NIE MA?
		sa = ((Element)wsdMethodXml.getElementsByTagName("selection").item(0)).getTextContent();
		
		
		
		
		ml= ((Element)wsdMethodXml.getElementsByTagName("machine_learning").item(0)).getTextContent();
		
		//Corpora
		
		if (wsdMethodXml.getElementsByTagName("cv").getLength()>0) {//CROSS VALIDATION
			isCV = true;			
			cvFold = Integer.parseInt(((Element)wsdMethodXml.getElementsByTagName("cv").item(0)).getAttribute("fold"));			
			trainingSetName = ((Element)wsdMethodXml.getElementsByTagName("cv").item(0)).getTextContent();
		} else {
			trainingSetName = ((Element)wsdMethodXml.getElementsByTagName("training").item(0)).getTextContent();
			testSetName = ((Element)wsdMethodXml.getElementsByTagName("test").item(0)).getTextContent();
		}
/*		
		trainingSetName = ((Element)wsdMethodXml.getElementsByTagName("training").item(0)).getAttribute("path");
		isCV = ((Element)wsdMethodXml.getElementsByTagName("test").item(0)).getAttribute("type").equals("cv");
		if (isCV) {
			cvFold = Integer.parseInt(((Element)wsdMethodXml.getElementsByTagName("test").item(0)).getAttribute("value"));
		} else {
			testSetName = ((Element)wsdMethodXml.getElementsByTagName("test").item(0)).getAttribute("value");
		}*/
		//TUTAJ TYLKO NAZWY
		//TakipiXMLParser txp = new TakipiXMLParser();
		//trainingSet = txp.parse("jezykb.xml.takipized");
		//trainingSet = WSDCorpus.load("t0.wsdcorp");
		//testSet = WSDCorpus.load("t1.wsdcorp");
		
		
		// (ew.) max_time
	}
	
	public void initDBRow() {
			wsdmrr = new WSDMethodResultRow(wsdMethodXml, wsdexp.dbh);
	}
	
	public void setSaveModels(boolean bool) {
		writeModels=bool;
	}
	
	public void loadCorpora() {
		trainingSet = WSDCorpus.getCorpus(trainingSetName);
		//Collections.shuffle(trainingSet);
		if (!this.isCV) {testSet = WSDCorpus.getCorpus(testSetName);
		//Collections.shuffle(testSet);
		}
	}
	
	public void run() {
		filter = WEKALoader.filter(sa);
		classifier = WEKALoader.classifier(ml);
		results = new EvaluationResult();
		results.timeStart();
		trainingSet = WSDCorpus.getCorpus(trainingSetName);
	
		if (this.isCV) { //podziel trening secik na drobne
			Vector<WSDCorpus> folds = trainingSet.splitInto(cvFold); //random split
			//TODO sprawdz czy foldow jest >1 i mniej niz zbior+1//
			WSDCorpus cvTrainSet;
			WSDCorpus cvTestSet;
			for (int i=0; i<folds.size(); i++) {
				cvTrainSet = new WSDCorpus(null);
				cvTestSet = new WSDCorpus(null);
				for (int j=0; j<folds.size(); j++) {
					if (i==j) {
						cvTestSet.addCorpus(folds.get(j));
					} else {
						cvTrainSet.addCorpus(folds.get(j));
					} 
				}
				
				WSDModel model = new WSDModel(cvTrainSet, this);
				model.classify(cvTestSet, results, false);
				results.addCVFold();
			
				if (writeModels) {
					trainingSet = WSDCorpus.getCorpus(trainingSetName);
					model = new WSDModel(cvTrainSet, this);
					model.saveModel(id+".wsdmeth");
				}
				
				
			}
		} else {
			testSet = WSDCorpus.getCorpus(testSetName);
			WSDModel model = new WSDModel(trainingSet,this);
			model.classify(testSet,results, false);	
			if (writeModels) {
				model.saveModel(id+".wsdmeth");
			}
		}
		results.timeStop();
		
		
		/*System.out.println(results.detailedResults());*/
		
		//System.out.println(results.getResult()+" "+results.correctCounter+" "+results.errorCounter);
		
	}
	
	public String toString() {
		StringBuffer sb = new StringBuffer();
		for (FeatureGenerator fg : this) {
			sb.append(fg.id());
			sb.append(": ");
			sb.append(fg.parameters);
			sb.append('\n');
		}
		sb.append('\n');
		sb.append(sa);
		sb.append('\n');
		sb.append(ml);
		return sb.toString();
	}
	
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {

		try {
			//String [] b = weka.core.Utils.splitOptions("-C 1.0 -L 0.0010 -P 1.0E-12 -N 0 -V -1 -W 1 -K \"weka.classifiers.functions.supportVector.PolyKernel -C 250007 -E 1.0\"");
	//		String [] b = weka.core.Utils.splitOptions("weka.filters.MultiFilter -E \"weka.attributeSelection.InfoGainAttributeEval \" -S \"weka.attributeSelection.Ranker -T -1.7976931348623157E308 -N 30\"");
			//weka.filters.supervised.attribute.AttributeSelection -E "weka.attributeSelection.CfsSubsetEval " -S "weka.attributeSelection.BestFirst -D 1 -N 5"
			//weka.filters.MultiFilter -F "weka.filters.supervised.attribute.ClassOrder -R 1 -C 0" -F "weka.filters.supervised.attribute.AttributeSelection -E \"weka.attributeSelection.CfsSubsetEval \" -S \"weka.attributeSelection.BestFirst -D 1 -N 5\""
			
			//weka.filters.MultiFilter -F "weka.filters.supervised.attribute.AttributeSelection -E \"weka.attributeSelection.InfoGainAttributeEval \" -S \"weka.attributeSelection.Ranker -T -1.7976931348623157E308 -N -1\"" -F "weka.filters.supervised.attribute.AttributeSelection -E \"weka.attributeSelection.CfsSubsetEval \" -S \"weka.attributeSelection.BestFirst -D 1 -N 5\""
			
		} catch (Exception e) {
		
			e.printStackTrace();
		}
		
		

	}

	public void cleanAfterStop() {
		if (trainingSet!=null) trainingSet.clear();
		if (testSet!=null) testSet.clear();
		if (results!=null) results.interrupted();
		if (results!=null) results.timeStop();
	}
	
	/***
	 * no timeout --> results ok --> update xml
	 */
	public void pushOKData() {
		//System.out.println(results.getResult() + " ****************** " + results.getElapssedTime());
		Element score = wsdMethodXml.getOwnerDocument().createElement("result");
		score.setTextContent(""+results.getResult());
		//System.out.println(score.getTextContent());
		wsdMethodXml.appendChild(score);
		
		Element time = wsdMethodXml.getOwnerDocument().createElement("time_elapsed_ms");	
		time.setTextContent(""+results.getElapssedTime());
		wsdMethodXml.appendChild(time);
		
		Element errors = wsdMethodXml.getOwnerDocument().createElement("errors");	
		errors.setTextContent(""+results.getErrors());
		wsdMethodXml.appendChild(errors);
		
		if (results.interrupted) {
			Element interrupted = wsdMethodXml.getOwnerDocument().createElement("interrupted");
			wsdMethodXml.appendChild(interrupted);
		}
		
		if (wsdexp.toDB) {
			wsdmrr.setResult(results.getResult());
			wsdmrr.insertRow();
		}
	}


}
