import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.swing.ImageIcon;
import javax.swing.JLabel;

/**
 *
 */
public class FaceRec {

	/**
	 * @param args
	 * 
	 */
	public OpenCV cv; 
	public FaceRecMainGUI mainGUI; 
	private ARFFWriter trainingARFFWriter, testingARFFWriter; 
	
	private final static String DATA_FOLDER       = "data/";
	private final static String TRAINING_FILENAME = DATA_FOLDER + "training_faces.arff";
	private final static String TESTING_FILENAME  = DATA_FOLDER + "testing_faces.arff";
	
	enum State { 
		HALTED,				//doing nothing (not processing templates)
		ACQUIRING_TRAINING, //acquiring images to be used in training
		TRAINING,			//in progress of training data (multi-threaded)
		ACQUIRING_TESTING,  //acquiring images to be used in testing
		TESTING,			//in progress of testing accumulated testing images
		TESTING_ON_FLY		//tests each template individually
		}
	
	public State currentState; 
	private wekacaller wc; 
	
	public FaceRec(OpenCV cv) throws IOException {
		this.cv = cv; 
		currentState = State.HALTED; 
		trainingARFFWriter = new ARFFWriter(TRAINING_FILENAME, 1024, false); 
		testingARFFWriter  = new ARFFWriter(TESTING_FILENAME , 1024, false); 
	}
	
	/**
	 * Call before exiting class (just performs proper cleanup of ARFF file writers.
	 * Not necessary as java classes almost definetly take care of this for us,
	 * but it's good style, so why not :). 
	 */
	public void closeFiles() { 
		trainingARFFWriter.closeFile();
		testingARFFWriter.closeFile(); 
	}
	
	/**
	 * Main entry into the entire program! 
	 * Launches OpenCV, main Java GUI, and the WEKA interface!
	 * @param args
	 */
	public static void main(String[] args) {
		try { 
		
		OpenCV cv = new OpenCV(); 
		FaceRec fr = new FaceRec(cv); 
		cv.attachFR(fr); 
		fr.initWeka(); 
		fr.launchMainGUI(); 
		//fr.start(); 
		cv.openCamera(); 
		System.out.println("OpenCV threads terminated");
		fr.closeFiles(); 
		System.exit(0); 
		} catch (Exception e) {
			e.printStackTrace(); 
		}
	}
	
	private void initWeka() { 
		wc = new wekacaller(32, 3); 
		wc.setImageSize();
		wc.setNumPeople(); 
		wc.buildAttributes(); 
		wc.setJ48(); 
	}
	
	private void launchMainGUI() { 
		mainGUI = new FaceRecMainGUI(this); 
		mainGUI.pack();
		mainGUI.show(); 
	}
	
	
	public void addNewTemplate(int personNum, int picData[], final BufferedImage temp) {
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
	        public void run() {
	        	mainGUI.addPerson(temp); 
	    		mainGUI.repaint(); 
	        }
	    });
		
	}
	
	public void updateLastTemplate(final int personNum, int picData[], final BufferedImage temp, final int correlation) {
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
	        public void run() {
	        	mainGUI.updateTemplate(personNum, temp, correlation, currentState); 
	    		mainGUI.repaint(); 
	        }
	    });
		
		switch (currentState) {
		case HALTED:
			break;
		case ACQUIRING_TRAINING:
			System.out.println("Adding training data");
			wc.addTrainingSet(picData, personNum); 
			trainingARFFWriter.writeFace(picData, personNum); 
			break;
		case TRAINING:
			break;
		case ACQUIRING_TESTING:
			System.out.println("Adding testing data");
			wc.addTestingSet(picData, personNum);
			testingARFFWriter.writeFace(picData, personNum); 
			break;
		case TESTING:
			break;
		}
	}
	
	public void setState(State newState) {
		this.currentState = newState;
		mainGUI.updateState(currentState); 
		
		switch (currentState) {
		case HALTED:
			break;
		case ACQUIRING_TRAINING:
			
			break;
		case TRAINING:
			new WekaThread(0, this);
			break;
		case ACQUIRING_TESTING:
			
			break;
		case TESTING:
			new WekaThread(1, this);
			break;
		}
	}
	
	//notifier called by WekaThread to let this thread know that weka has completed 
	public void wekaActionCompleted() { 
		System.out.println("Weka action completed");
		this.setState(State.HALTED); 
		wc.printEvaluation();
		wc.printClassDetails(); 
	}
	
	/**
	 * Helper thread to perform training/testing so that the process of doing
	 * so doesn't block the rest of the program from operating or freeze the 
	 * GUI. 
	 * 
	 * It will notify the main java thread once it's done. 
	 */
	class WekaThread extends Thread {
		int action;
		FaceRec fr; //used to notify face rec once action is completed
		public WekaThread(int action, FaceRec fr) {
			this.action = action; 
			this.fr = fr; 
			new Thread(this).start(); 
		}
		public void run() { 
			switch (action) {
			case 0: 
				wc.trainDataSet();
				break;
			case 1:
				wc.testDataSet();
				break;
			default:
				System.out.println("Invalid action passed to WekaThread: " + action);
			break;
			}
			fr.wekaActionCompleted(); 
		}
	}
}
