import java.lang.*;
import java.util.*;
import java.io.*;
import weka.core.*;
import weka.classifiers.*;
import weka.classifiers.trees.ADTree;
import weka.classifiers.trees.RandomForest;
import weka.classifiers.meta.Bagging;
import weka.classifiers.trees.DecisionStump;
import weka.classifiers.trees.J48;
import weka.classifiers.bayes.net.search.local.K2;
import weka.classifiers.lazy.IBk;

public class wekacaller {

    private FastVector fvWekaAttributes;
    private FastVector fvNominalVal;
    private Attribute grayvalue;
    private Attribute person;
    private Instances isTrainingSet;
    private Instances isTestingSet;
    private Classifier cModel;
    private Instance instance;
    private Evaluation evaluation;
    private int imagesize;
    private int numpeople;
    private boolean hasTested;
    private boolean hasTrained;
    
    public wekacaller(int imagesize,int numpeople){
	//this.fvWekaAttributes=new FastVector(1025);
	//this.instance=new Instance(1025);
	//this.fvNominalVal=new FastVector(3);
	this.fvWekaAttributes=null;
	this.instance=null;
	this.fvNominalVal=null;
	this.grayvalue=null;
	this.person=null;
	this.isTrainingSet=null;
	this.isTestingSet=null;
	this.cModel=null;
	this.imagesize=imagesize*imagesize;
	this.numpeople=numpeople;
	this.hasTested=false;
	this.hasTrained=false;
    }

    //call before calling setNumPeople
    //given the size of an image (i.e 32x32 image supply 32 to method) creates a fastvector with size
    //imagesize+1.  imagesize*imagesize Attribute names are then added to the fastvector
    //leaving one attribute to append to the fastvector which will later be used for the aNominal person attribute
    public void setImageSize(){
	int size=imagesize+1;
	fvWekaAttributes=new FastVector(size);
	instance=new Instance(size);
	for (int i=1;i<size;i++){
	    grayvalue = new Attribute(Integer.toString(i));
	    fvWekaAttributes.addElement(grayvalue);
	}
    }

    //call after setImageSize
    //given the number of people to be analyzed it creates an attribute with values(0,1,2,..,n-1)
    //This should be called after setImageSize so that the person attribute is added at the end of the 
    //the fast vector and not the beginning.  The class index denotes the attribute to classify by and it is referencing the 
    //last element.
    public void setNumPeople(){
	fvNominalVal=new FastVector(numpeople);
	for (int i=0;i<numpeople;i++){
	    fvNominalVal.addElement(Integer.toString(i));
	}
	person = new Attribute("aNominal", fvNominalVal);
	fvWekaAttributes.addElement(person);
    }

    //setNumPeople if different from original value 
    //deletes last value in FastVector and then adds new element with new number of people to observe
    public void setNumPeople(int numpeople){
	fvNominalVal=new FastVector(numpeople);
	for (int i=0;i<numpeople;i++){
	    fvNominalVal.addElement(Integer.toString(i));
	}
	person = new Attribute("aNominal", fvNominalVal);
	fvWekaAttributes.removeElementAt(imagesize+1);
	fvWekaAttributes.addElement(person);
    }
    
    public void buildAttributes(){
	isTrainingSet = new Instances("FacialRecTraining", fvWekaAttributes, 100000);
	isTrainingSet.setClassIndex(imagesize);
	isTestingSet = new Instances("FacialRecTesting", fvWekaAttributes, 100000);
	isTestingSet.setClassIndex(imagesize);
    }

    //choose classifier ADTree
    public void setADTree(){
	String[] options= new String[] {"-B","10","-E","-3"};
	try{
	    cModel=(Classifier)new ADTree();
	    cModel.setOptions(options);
	} catch (Exception e){
	    e.printStackTrace();
	}
    }

    //choose classifier Random Forests
    public void setRandomForests(){
	String[] options= new String[] {"-I","100","-K","0","-S","1"};
	try{
	    cModel=(Classifier)new RandomForest();
	    cModel.setOptions(options);
	} catch (Exception e){
	    e.printStackTrace();
	}
    }

    //choose classifier Bagging w/ADTree
    /*    public void setBagging(){
	Classifier adtree = (Classifier)new ADTree();
	String[] options=new String[] {"-P","100","-S","1","-I","10","-W"};
	String[] adtreeoptions=new String[] {"-B","10","-E","3"};
	try{
	    cModel=(Classifier)new Bagging();
	    cModel.setOptions(options);
	    adtree.setOptions(adtreeoptions);
	    cModel.setClassifier(adtree);
	} catch (Exception e){
	    e.printStackTrace();
	}
	}*/

    //choose classifier Decision Stump
    public void setDecisionStump(){
	try{
	    cModel=(Classifier)new DecisionStump();
	} catch (Exception e){
	    e.printStackTrace();
	}
    }

    //choose classifier J48 ( Just an Example we can change this )
    public void setJ48(){
	String[] options= new String[] {"-C","0.25","-M","2"};
	try{
	    cModel=(Classifier)new J48();
	    cModel.setOptions(options);
	} catch (Exception e){
	    e.printStackTrace();
	}
    }

    //choose classifier Functions K2( Just an Example we can change this )
    /*public void setK2(){
	String[] options= new String[] {"-N","2","-W","1"};
	try{
	    cModel=(Classifier)new K2();
	    cModel.setOptions(options);
	} catch (Exception e){
	    e.printStackTrace();
	}
	}*/

    //choose classifier IBK ( Just an Example we can change this )
    public void setIBk(){
        String[] options= new String[] {"-K","1","-W","0"};
        try{
            cModel=(Classifier)new IBk();
            cModel.setOptions(options);
        } catch (Exception e){
	    e.printStackTrace();
        }
    }

    public void addTestingSet(int[] grayScaleSet, int personSet){
	for (int j=0;j<grayScaleSet.length;j++){
	    instance.setValue((Attribute)fvWekaAttributes.elementAt(j), grayScaleSet[j]);      
	}	
	instance.setValue((Attribute)fvWekaAttributes.elementAt(1024), Integer.toString(personSet));      	
	isTestingSet.add(instance);
    }
    
    public void addTrainingSet(int[] grayScaleSet, int personSet){
	for (int j=0;j<grayScaleSet.length;j++){
	    instance.setValue((Attribute)fvWekaAttributes.elementAt(j), grayScaleSet[j]);      
	}	
	instance.setValue((Attribute)fvWekaAttributes.elementAt(1024), Integer.toString(personSet));      	
	isTrainingSet.add(instance);
    }

    //train the classifier with the the training data
    public void trainDataSet(){
	try {
	    cModel.buildClassifier(isTrainingSet);
	    evaluation = new Evaluation(isTrainingSet);
	    hasTrained=true;
	} catch (Exception e) {
	    e.printStackTrace();
	}
    }

    //using previously trained data evaluate the Testing Data
    public void testDataSet(){
	try {
	    evaluation.evaluateModel(cModel, isTestingSet);
	    hasTested=true;
	} catch (Exception e) {
	    e.printStackTrace();
	}
    }

    //Removes an instance at the given position from the set.
    public void deleteTestingSet(int i){
	isTestingSet.delete(i);
    }

    //Removes all instances from the set.
    public void deleteTestingSet(){
	isTestingSet.delete();
    }

    //Removes an instance at the given position from the set.
    public void deleteTrainingSet(int i){
	isTrainingSet.delete(i);
    }

    //Removes all instances from the set.
    public void deleteTrainingSet(){
	isTrainingSet.delete();
    }

    public void printEvaluation(){
	String strSummary = evaluation.toSummaryString();
	System.out.println(strSummary);
    }

    public void printClassDetails(){
	try {
	    System.out.println(evaluation.toClassDetailsString());
	} catch (Exception e) {
	    e.printStackTrace();
	}
    }

    public void printConfusionMatrix(){
	try {
	    System.out.println(evaluation.toMatrixString());
	} catch (Exception e) {
	    e.printStackTrace();
	}
    }

    public double[] returnPredicatedClass(){
	double[] results=null;
	try{
	    results = evaluation.evaluateModel(cModel, isTestingSet);
	} catch (Exception e){
	    e.printStackTrace();
	}
	return results;
    }

    //will return zeros if testDataSet() has not been called....
    public double[][] returnConfusionMatrix(){
	double[][] results=null;
	try{
	    results = evaluation.confusionMatrix();
	} catch (Exception e){
	    e.printStackTrace();
	}
	return results;
    }


    public void setHasTrained(boolean trained){
	hasTrained=trained;
    }

    public boolean hasTrained(){
	return hasTrained;
    }

    public void setHasTested(boolean tested){
	hasTested=tested;
    }

    public boolean hasTested(){
	return hasTested;
    }

}//end wekacaller
