/*
 * Emotions.java
 *
 * Created on 21 of January 2010, 16:42
 */

/**
 *
 * @author Paulo Roque Silva
 */
//import icommand.nxt.Sound;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import icommand.nxt.Sound;

public class Emotions implements Runnable {
	static final int 				ActRandom 	= 30; // Time in steps to be baby BB
	static final int				AverageLast = 10; // the last 10 goods
	static final int				Normal = 20;
	// NOTE: This tune was generated from a midi using Guy
	// Truffelli's Brick Music Studio www.aga.it/~guy/lego
	private static final short[] 	noteNew     = {5, 40};
	private static final short[]	noteDor     = {2349, 40};
	private static final int		SpeakTime = 20; // Speak Time percent 
    public static int				QF, // final quality
    								QP, // perception quality
    								QR, // remembered quality
    								Good, // recent plus decision
    								IdAct1, // Action Key
    								IdAct2, // Action Key
    								IdAct3, // Action Key
    								PwEm; // code for 3 sources (+Pass;0Random;-Body) 
    public static boolean			EmotionNew, // if new
    								ActualPain, // if Feels Pain...
    								WARNING;
    public static Signal 	        []BodyAct; // Actions on the body of the robot
    public static ArrayList<Integer> Average; // average sum of the qF in the last AverageLast
    private Cortex					past; // pointer to memory
    Iterator<Signal> 				listen; // Listen id from busIn
    CyclicBarrier 				    stepBarrDecision; // Establish a barrier to the act steps
    CyclicBarrier 				    stepBarrDecision2; // Establish a barrier to the act steps
    private int						backDecision; // before present time in the past
    private boolean					emotional; // If gets emotional
//    								repete; // Turns around
    
    public Emotions(CyclicBarrier barrD, CyclicBarrier barrD2,
    						Cortex me, Signal []bodyA) {

    	stepBarrDecision = barrD;
    	stepBarrDecision2 = barrD2;
    	past 		 = me;
    	BodyAct 	 = bodyA;
    	PwEm 		 = Mind.Power;
		backDecision = 0; 
	    emotional 	 = false;
    	ActualPain   = false;
    	IdAct1 		 = Cerebellum.ESCape; // Random choice
    	IdAct2 		 = 1; // 1 or 0 B motor
    	IdAct3 		 = 0; // 1 or 0 Speek
//    	repete 		 = false;
        Liberu.Already = true;
        Average = new ArrayList<Integer>(AverageLast);
        WARNING = false;
    }
    
    public void idAct1Action(int idAction) {
    	
		IdAct1 = idAction;
    }
	
    public void idAct2Action(int idAction) {
    	
		IdAct2 = idAction;
    }
	
    private static void express(short[] note) {
    	int i;
		
    	for (i = 0; i < note.length; i += 2) {
    		final short w = note[i + 1];
    		final int n = note[i];
    		Sound.playTone(n, w * 10);
    	}
/*		try {
            Liberu.output.write((byte)110);
            Liberu.output.write('\n');
		}
    	catch (Exception e) {
            System.err.println(e.toString());
    	}
*/    }
	
	public void emotionSpeak() {
		short []happySound = {1963, 80};

    	BodyAct[Brain.SPEAK].setActive(true); // genetic expression happy
    	happySound[0] = (short)(QF * 10 + 2000);
    	if (happySound[0] > 3000)
    		happySound[0] = 3000;
    	express(happySound);			//NXT
    	System.out.println("SPEEK...  :-)"); //REMOVE
	}
	
	public void emotionNew(boolean empty) {

		// calls emotion of New event - Genetic and pre-coded
		if (empty) {
			emotional = true;
			EmotionNew = true;
        	express(noteNew); // NXT command
/*			try {
	            Liberu.output.write((byte)106);
	            Liberu.output.write('\n');
			}
	    	catch (Exception e) {
	            System.err.println(e.toString());
	    	}
*/			System.out.println("Emotion NEW...    :-0"); //REMOVE
			BodyAct[Brain.New].setActive(true);
			WARNING = true;
		}
	}
	
	private void emotionNormal() {

			emotional = false;
	    	ActualPain = false;
			EmotionNew = false;
			System.out.println("Emotion Normal... :-|"); //REMOVE
/*			try {
	            Liberu.output.write((byte)107);
	            Liberu.output.write('\n');
	            Liberu.output.write((byte)108);
	            Liberu.output.write('\n');
	            Liberu.output.write((byte)109);
	            Liberu.output.write('\n');
			}
	    	catch (Exception e) {
	            System.err.println(e.toString());
	    	}
*/	}
	
	public void emotionHappy() {
		short []happySound = {1963, 40};

		emotional = true;
    	BodyAct[Brain.Happy].setActive(true); // genetic expression happy
    	happySound[0] = (short)(QF * 10 + 1000);
    	if (happySound[0] > 3000)
    		happySound[0] = 3000;
    	express(happySound);	//NXT
/*		try {
            Liberu.output.write((byte)104);
            Liberu.output.write('\n');
		}
    	catch (Exception e) {
            System.err.println(e.toString());
    	}
*/    	System.out.println("Emotion HAPPY...  :-)"); //REMOVE
	}
	
	public void emotionSad() {
		short []sadSound = {0403, 40};

		emotional = true;
    	BodyAct[Brain.Sad].setActive(true); // genetic expression sad
    	sadSound[0] = (short)(QF * 10 + 1000);
    	if (sadSound[0] < 20)
    		sadSound[0] = 20;
    	express(sadSound);
/*		try {
            Liberu.output.write((byte)105);
            Liberu.output.write('\n');
		}
    	catch (Exception e) {
            System.err.println(e.toString());
    	}
*/		System.out.println("Emotion Sad...    :-("); //REMOVE
	}
	
	public void emotionPAIN() {

    	ActualPain   = true;
    	emotional = true;
    	express(noteDor);
    	BodyAct[Brain.PAIN].setActive(true); // genetic expression pain
		System.out.println("Emotion PAIN...   ;-("); //REMOVE
	}

	public void emotionAfraid() {

    	emotional = true;
    	BodyAct[Brain.Afraid].setActive(true);
		System.out.println("Emotion Afraid...   ,-("); //REMOVE
	}
			
    public void addAverage(int e) {

    	Average.add((Integer)e);
    }
    
	public void actDecision(int timeBom) {
		int tBom; // absolute timeBom

		// Write BusDecisionOut
		tBom = Math.abs(timeBom);
		if (timeBom != Cerebellum.ESCape)
	    	Brain.BusInCerebellum.add(new Signal(timeBom, past.time[tBom][Brain.ProSelf].finalQuality,
					 past.time[tBom][Brain.ProSelf].finalQuality));
		else
	    	Brain.BusInCerebellum.add(new Signal(Cerebellum.ESCape, 0, 0));
    	System.out.println(timeBom); //REMOVE
		}
			
	public void runCerebellum() { // the action after the 2 kinds of decisions
		int i, j; // indexes
		int avrSum; // average sum
		
    	try {
            stepBarrDecision.await(); // synchronize nodes with the past nodes
        }
        catch(InterruptedException e) {
                e.printStackTrace();
            }
        catch (BrokenBarrierException ex) { 
            return;
        }
    	// ################################# DECIDE ###########################
        // ######  recent decided ######
    	Interface.Ink = 0;
        for (i=0; i<Brain.SENSES; i++)
        	if (i!=4)
            	emotionNew(Remember.NewSense[i]);
    	if (Mind.getBom() != Cerebellum.ESCape)
        	backDecision = Mind.exec;
    	
    	QP = past.time[past.time[0][0].pointer][Brain.ProSelf].finalQuality;
    	QR = past.time[Math.abs(Good)][Brain.ProSelf].finalQuality;
    	if (QR < 0) {
    		emotionAfraid();
    		emotionSpeak();
    	}
    	avrSum = 0;
    	for (i=0; i<Average.size(); i++)
    		avrSum += (Integer)Average.get(i);
    	if (Average.size() == 0)
        	QF = QP + QR;
    	else
        	QF = QP + QR + avrSum / Average.size();
    	Interface.QF = QF;
    	// turns around
//    	if (past.time[Math.abs(Good)][Mind.SENSES-1].getPerception(0).emitter == 
//    			past.time[Math.abs(backDecision)][Mind.SENSES-1].getPerception(0).emitter
//    			&& past.time[Math.abs(backDecision)][Mind.SENSES-1].getPerception(Mind.BACK).recPower >=
//    					Mind.MinSense || repete) {
//    		repete = !repete;
//    		if (Math.random() < 0.5)
//    			IdAct = Mind.TURN_LEFT;
//    		else
//    			idAct = Liberu.TURN_RIGHT;
//    	}
    	
    	if (ActualPain) // Instinct
    		Good = -backDecision; // Action opposite in Act
		// ###############################
    	if (QF < -Normal)
        	emotionSad();
    	else
        	if (QF > Normal)
            	emotionHappy();
    	for (i=0; i<past.nNeuronsTime; i++)
        	for (j=0; j<past.time[i][0].learn.size(); j++) {
        		if (Math.abs(past.time[i][0].getLearn(j).emitter) == past.time[i][0].pointer)
        			past.time[i][0].removeLearn(j);
        	}
    	// ################################# ACT ##############################
    	while (!Brain.BusInCerebellum.isEmpty())
    		Brain.BusInCerebellum.poll();                // redundante ja no PlayLiberu Liberu.clearTimeOut()
    	while (!Brain.BusInAct.isEmpty())
    		Brain.BusInAct.poll();
        if ( IdAct1 == Cerebellum.ESCape && past.time[0][0].step >= ActRandom) {

//        	actDecision(Good); // ADD signals to busDecisionOut
            
        }
        else // SE VOLTAR PARA TRAS - PENSA, PARADO
            actDecision(Cerebellum.ESCape); // Add signals to busDecisionOut

        
        // ####################################### RANDOM - MENTOR ############
    	try {
            stepBarrDecision2.await(); // synchronize nodes with the past nodes
        }
        catch(InterruptedException e) {
                e.printStackTrace();
            }
        catch (BrokenBarrierException ex) { 
            return; 
        }
        // Random choice
        if (IdAct1 != Cerebellum.ESCape || past.time[0][0].step < ActRandom) {
        	// Mathematics Induct Principle, from 0 goes to 1
        	if (IdAct1 != Cerebellum.ESCape) {
        		BodyAct[IdAct1].setActive(true);
        		BodyAct[IdAct1].setActPower(PwEm);
        		BodyAct[IdAct2].setActive(true);
        		BodyAct[IdAct2].setActPower(PwEm);
        		System.out.print("MentorA= "); //REMOVE
        		System.out.println(IdAct1); //REMOVE
        		System.out.print("MentorB= "); //REMOVE
        		System.out.println(IdAct2); //REMOVE
        		Interface.Ink = 2; // Green
        	}
        	else {
               	IdAct1 = Brain.AWALK+(int)Math.rint(Math.random());
               	IdAct2 = Brain.BWALK+(int)Math.rint(Math.random());
               	IdAct3 = (int)(Math.random() * 100);
                if (past.time[0][0].step == 0) {
                	IdAct1 = Brain.AWALK; // forces walking - genetic - simpatic system
                	IdAct2 = Brain.BWALK; // forces walking - genetic - simpatic system
                }
                if (IdAct3 <= SpeakTime) {
            		BodyAct[Brain.SPEAK].setActive(true);
            		BodyAct[Brain.SPEAK].setActPower(PwEm);
            		past.actNode[Brain.SPEAK].standby = true;
                }
        		BodyAct[IdAct1].setActive(true);
        		BodyAct[IdAct1].setActPower(PwEm);
        		BodyAct[IdAct2].setActive(true);
        		BodyAct[IdAct2].setActPower(PwEm);
        		System.out.print("Random1= "); //REMOVE
        		System.out.println(IdAct1); //REMOVE
        		System.out.print("Random2= "); //REMOVE
        		System.out.println(IdAct2); //REMOVE
        		System.out.print("Random3= "); //REMOVE
        		System.out.println(IdAct3); //REMOVE
        		Interface.Ink = 1; // Red
        	}
    		past.actNode[IdAct1].standby = true;
    		past.actNode[IdAct2].standby = true;
        	IdAct1 = Cerebellum.ESCape; // Random choice
        }

       	// #################################### C L O C K #####################
        System.out.print("step= "); // REMOVE
       	System.out.println(past.time[0][0].pointer); // REMOVE
       	for (j=0; j<Cortex.Senses; j++)
        	for (i=0; i<past.nNeuronsTime; i++) {
        		past.time[i][j].addStep();              // CLOCK... life time
        		past.time[i][j].addPointer();              // CLOCK... windows time
            	if ( past.time[i][j].pointer > past.nNeuronsTime - 1)
            		past.time[i][j].setPointer(0);
        	}
    	for (i=0; i<past.nNeuronsTime; i++) {
    		past.hemisphereLeft[i].addStep();              // CLOCK... life time
    		past.hemisphereLeft[i].addPointer();              // CLOCK... windows time
        	if ( past.hemisphereLeft[i].pointer > past.nNeuronsTime - 1)
        		past.hemisphereLeft[i].setPointer(0);
    		past.hemisphereRigth[i].addStep();              // CLOCK... life time
    		past.hemisphereRigth[i].addPointer();              // CLOCK... windows time
        	if ( past.hemisphereRigth[i].pointer > past.nNeuronsTime - 1)
        		past.hemisphereRigth[i].setPointer(0);

    	}
    	// ############################# End Clock ############################
    	
    	if (Average.size() < AverageLast)
    		addAverage(QF);
    	else
    		Average.remove(0);
        
        if (emotional) // Last thing to do
        	emotionNormal();
        Remember.QualityMax = 0;
    	Remember.RecentMost = 0;
		Remember.threshold = 0;
	}

    public void run() {
        
		while(!stepBarrDecision.isBroken() && !stepBarrDecision2.isBroken() && !Neuron.end) {
    		runCerebellum();
    	}
        System.out.println("End of Emotions Thread...");
    }
}