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

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

import java.util.Iterator;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CyclicBarrier;
import javax.swing.*;
import javax.swing.event.*;

public class RunEmotionalUnit extends Thread {
	static final int				ActRandom   = 20; // Time in steps to be baby
    								// Time to test Personality in Liberu's step's
    static final int  				Xanos 	    = (int)Liberu.NNEURONT / 3;
    static final int 		    	BALANCE 	= 7;   // Window of time correlation
	// 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, 50};
	private static final short[]	noteDor     = {2349, 50};
	private static final short[]	noteAppy    = {20, 50};
	private static final short[] 	noteSad     = {1500, 50};
    Interface  					    ecran;			// interface with Humans
    Net								mem;			// pointer to memory
    Iterator<Signal> 				listen;         // Listen id from busIn
    CyclicBarrier 				    stepBarrREU;    // Establish a barrier to the act steps
    ConcurrentLinkedQueue<Signal> 	busEuIn;        // Queue in - Bus Past Out - and REmotionalU In
//    ConcurrentLinkedQueue<Signal> 	busEuInProSelf; // Queue in Proself
    static ConcurrentLinkedQueue<Signal>
    								busEuOut;       // Queue out - Bus Past In - and REmotionalU Out
    Signal  				        []bodyAct;      // Actions on the body of the robot
    Signal							signInput;	 	// Read input Signal
    static int						pwEm;			// code for 3 sources (+Pass;0Random;-Body) 
	float							senseP,			// Trace bigger Value from senses (in percentage - racio)
									tempSenses;		// temporary variable
    int 							recent,			// Trace most recent from id
    								best,			// Trace bigger Value from value
    								backDecision,   // before present
    								countBusOut,    // Fires count of Bus Past Out - and REmotionalU In -ln DOPAMINA$
    								countSense,     // Count senses == senseP
    								pain,			// MEMORIZES EQUALITY - ACETILCOLINA%-COLINERGIC
    								homeostase;     // Homeostase
    static boolean					emotional,		// If gets emotional
    								actualPain,		// if Feels Pain...
    								ansia,  	    // If have a motivation... thirsty or hungry- SERETONINA&
    								sono;  	        // If is a sleep
    static int  					alt;			// Alternates between random and past recent
    JTextField      				text;           // learned text
    DocumentListener				listener;       // action
    String		  					key;			// Pressed key
    
    public RunEmotionalUnit(CyclicBarrier barrREU,
    						Interface panelG,
    						JTextField txt,
    						Net me,
    						ConcurrentLinkedQueue<Signal> busIn,
    						ConcurrentLinkedQueue<Signal> busOut,
    						ConcurrentLinkedQueue<Signal> busInProSelf,
    						Signal []bodyA) {

    	stepBarrREU  = barrREU;
        ecran 		 = panelG;
	    text		 = txt;
    	mem 		 = me;
    	busEuIn 	 = busIn;
    	busEuOut 	 = busOut;
//    	busEuInProSelf= busInProSelf;
    	bodyAct 	 = bodyA;
    	pwEm 		 = PlayLiberu.Power;
	    recent 		 = 0;
	    best   		 = 0;
		backDecision = 0; 
	    emotional 	 = false;
	    pain 		 = 0;
    	actualPain   = false;
	    ansia	 	 = false;
	    sono	 	 = false;
	    alt			 = 0;
	    homeostase	 = 0;

        key = "";
	    listener     = new LearnListener();
        text.getDocument().addDocumentListener(listener);
    }
    
    public static boolean ansia() {
    	
		return ansia;
    }
	
    public void anda() {
		
		Motor.A.forward();
		Motor.B.forward();
		try {
			sleep(1500);
			}
		catch (InterruptedException ex) { 
			return;
			}
		Motor.A.stop();
		Motor.B.stop();
    }
	
    public void recua() {
		
		Motor.A.backward();
		Motor.B.backward();
		try {
			sleep(1500);
			}
		catch (InterruptedException ex) { 
			return;
			}
		Motor.A.stop();
		Motor.B.stop();
    }
	
	public void andaEsqu() {
		
		Motor.A.backward();
		Motor.B.forward();
		try {
			sleep(1500);
			}
		catch (InterruptedException ex) { 
			return;
			}
		Motor.A.stop();
		Motor.B.stop();
	}
	
	public void andaDire() {
		
		Motor.A.forward();
		Motor.B.backward();
		try {
			sleep(1500);
			}
		catch (InterruptedException ex) { 
			return;
			}
		Motor.A.stop();
		Motor.B.stop();
	}
	
	public void neckLeft() {
		
		Motor.C.rotate(-180);
	}
	
	public void neckRight() {
		
		Motor.C.rotate(180);
	}
	
    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];
//    		if (n != 0)
    			Sound.playTone(n, w * 10);
    		//System.out.println("Sound.playTone(" + n + ", " + (w * 10) + ")");
//    		try {
//    			Thread.sleep(w * 10);
//    			}
//    		catch (InterruptedException e) {
//    		}
    	}
    }
	
	public void emotionNew(boolean empty) {

		// calls emotion of New event - Genetic and pre-coded = only the emotional expression (U Actions)
		if (empty) {
			emotional = true;
        	ecran.saveValue(15, 1);             // value 15
//        	express(noteNew);   														 // NXT command
//        	busEuInProSelf.add(new Signal(Liberu.New, pwEm));	   // genetic expression
			System.out.println("Emotion NEW...    :-0");
		}
		else
        	ecran.saveValue(15, 0);             // value 15
	}
	
	public void emotionNormal() {

			emotional = false;
			ansia = false;
	    	actualPain = false;
			ecran.saveValue(14, 0);             // value 14
        	ecran.saveValue(18, 0);             // value 18
        	ecran.saveValue(19, 0);             // value 19
        	ecran.saveValue(20, 0);             // value 20
//			busEuInProSelf.add(new Signal(-Liberu.WALK, pwEm));	    // genetic expression
			System.out.println("Emotion Normal... :-|");
	}
	
	public void emotionBelo(int belo) {

		System.out.print("Emotion Belo...   ;-)");
		System.out.println(belo);
		emotional = true;
    	if (belo > 0)
        	ecran.saveValue(16, (int)Math.log(belo));          // value 16
    	else
        	ecran.saveValue(16, 0);          // value 16

//        	busEuInProSelf.add(new Signal(Liberu.Belo+(int)Math.log(belo), pwEm));   // genetic expression
	}
	
	public void emotionHappy() {

		emotional = true;
		ecran.saveValue(18, 1);             // value 18
//    	express(noteAppy); 															   // NXT command
//		busEuInProSelf.add(new Signal(Liberu.Happy, pwEm));	    // genetic expression
		System.out.println("Emotion HAPPY...  :-)");
	}
	
	public void emotionSad() {

		emotional = true;
        ecran.saveValue(19, 1);             // value 19

//    	express(noteSad);   															 // NXT command
//        busEuInProSelf.add(new Signal(Liberu.Sad, pwEm));	   // genetic expression
		System.out.println("Emotion Sad...    :-(");
	}
	
	public void emotionPAIN() {

    	pain += 1; //  O cerebro sabe contar pela diferença da substancia... IMPOSSIVEL C/ VAR. BOOLEANAS
	    if (pain >= 2)   // DETECTA REPETICAO 1a derivada=0, descobre o numero 2 - acrescentar o inv. motiv.
	    	emotionSad();
    	actualPain   = true;
        ecran.saveValue(14, 1);             // value 14 Dor
	    sono	 	 = false;   			// despertar por dor
        ecran.saveValue(21, 0);             // value 21 Estado acordado:dormir
    	emotional = true;

//    	express(noteDor);  																  // NXT command
//        busEuInProSelf.add(new Signal(Liberu.PAIN, pwEm));	    // genetic expression
		System.out.println("Emotion PAIN...   ;-(");
	}
			
	public void ansiaFeel() {

		ansia = true;
		emotional = true;
        ecran.saveValue(20, 1);             // value 20
	}
			
    public void sono() { // ACRESCENTAR Quando esta parado 2 vezes dorme... Estado = Dormindo/Acordado s/botao
    	int t, n;
    	
	    sono	 	 = true;
        ecran.saveValue(21, 1);             // value 21
        for (n=0; n<mem.nNeuronsPTaste; n++) 
     	   mem.nodeTaste[n].setThreshold(mem.nodeTaste[n].thresholdINIT);
        for (n=0; n<mem.nNeuronsPSmell; n++) 
     	   mem.nodeSmell[n].setThreshold(mem.nodeSmell[n].thresholdINIT);
        for (n=0; n<mem.nNeuronsPVision; n++) 
     	   mem.nodeVision[n].setThreshold(mem.nodeVision[n].thresholdINIT);
        for (n=0; n<mem.nNeuronsPTact; n++) 
     	   mem.nodeTact[n].setThreshold(mem.nodeTact[n].thresholdINIT);
        for (n=0; n<mem.nNeuronsPSound; n++) 
     	   mem.nodeSound[n].setThreshold(mem.nodeSound[n].thresholdINIT);
        for (n=0; n<mem.nNeuronsPProSelf; n++) 
     	   mem.nodeProSelf[n].setThreshold(mem.nodeProSelf[n].thresholdINIT);
	    for (t=0; t<Liberu.NTRAIL; t++)
	           for (n=0; n<Liberu.NNEURONT; n++) 
	        	   mem.time[n][t].setThreshold(mem.time[n][t].thresholdINIT);
        for (n=0; n<mem.nNeuronsPProSelf; n++) 
        	mem.actNode[n].setThreshold(mem.actNode[n].thresholdINIT);
    }
    
	public void runCerebellum() {		  // the action after the 2 kinds of decisions
		int idAct,
			i, j;
	    
    	try {
            stepBarrREU.await();  			  // synchronize nodes with the past nodes
        }
        catch(InterruptedException e) {
                e.printStackTrace();
            }
        catch (BrokenBarrierException ex) { 
            return; 
        }
       	System.out.print("step= "); // a retirar
       	System.out.println(mem.time[0][0].step); // a retirar
    	// ################################# DECIDE ##############################################
       	if (!sono && !actualPain)
           	for (j=0; j<Liberu.NTRAIL; j++)
            	for (i=0; i<Liberu.NNEURONT; i++) {
            		mem.time[i][j].addStep();              // CLOCK...
            		mem.time[i][j].addPointer();              // CLOCK...
                	if ( mem.time[i][j].pointer > Liberu.NNEURONT - 1)
                		mem.time[i][j].setPointer(0);
            	}

       	emotionNew(busEuIn.isEmpty());
       	senseP = 0;
       	listen = busEuIn.iterator();
        while (listen.hasNext()) {        // read all the input bus
        	signInput = listen.next();
			if (signInput.status != 0)
				tempSenses = signInput.actPower / signInput.status;
			else
				tempSenses = 0;
    				
    		if (tempSenses > senseP) {
        		senseP = tempSenses ;
    		}
		}       // bus pseudo-empty
		countBusOut = 0;
		countSense = 0;
	    recent = 0;
       	listen = busEuIn.iterator();
        while (listen.hasNext()) {        // read all the input bus
        	signInput = listen.next();
    		// decision of bigger credit or more recent on bus Out
			if (signInput.status != 0)
				tempSenses = signInput.actPower / signInput.status;
			else
				tempSenses = 0;
    		if (tempSenses == senseP) {
            	if (countBusOut == 0) {
            		recent = signInput.emitter;
        			best = (int)signInput.recValue;
            	}
        		if (Liberu.ratVSemo)
    			    if (signInput.emitter >= recent &&
    			    		(recent > mem.time[0][0].pointer || signInput.emitter <= mem.time[0][0].pointer))
    			    	recent = signInput.emitter;
    	    		else
    					if (recent > mem.time[0][0].pointer && signInput.emitter <= mem.time[0][0].pointer)
    				    	recent = signInput.emitter;
        		if (!Liberu.ratVSemo && signInput.recValue >= best) {
        			recent = signInput.emitter;
            		best = (int)signInput.recValue;
        		}
        		countSense += 1;
    		}
    		countBusOut += 1;
        }      // end pseudo-bus
    	emotionBelo(countBusOut);

    	System.out.print("senseP= "); // a retirar
    	System.out.println(senseP);       // a retirar
    	System.out.print("CountsenseP= "); // a retirar
    	System.out.println(countSense);       // a retirar
        
	    if (recent >= backDecision && (backDecision > mem.time[0][0].pointer
	    																|| recent <= mem.time[0][0].pointer)
	    		|| recent < backDecision && backDecision > mem.time[0][0].pointer
	    																&& recent <= mem.time[0][0].pointer)
	    	Liberu.perTime += 1;

    	System.out.print("recent= "); // a retirar
    	System.out.println(recent); // a retirar
        for (i=0; i<mem.time[recent][Liberu.NTRAIL-1].dendrites.size(); i++)                // a retirar
        	System.out.println(mem.time[recent][Liberu.NTRAIL-1].getDendrite(i).emitter);   // a retirar
    	if (recent == 0) {             // CHORO nao act
        	bodyAct[Liberu.CHORA].setActive(true);
    		recent = backDecision + 1;
    		ecran.saveValue(22, 1);             // value 22
    	}
    	else
    		ecran.saveValue(22, 0);             // value 22
        if (recent == Liberu.NNEURONT-1)
        	recent = 0;
    	for (j=0; j<Liberu.NTRAIL; j++) {     // update value
//        	mem.time[recent][j].addValue();
        	mem.time[recent+1][j].addValue();
        	}
    	// #######################################################################################
    	// ################################# ACT #################################################
    	idAct = -1;     // Random choice
		System.out.print("Key****= ");    // retirar
		System.out.println(key);    // retirar
		if (key.equals("d")) idAct = Liberu.DRINK;            // key D pressed 100
		if (key.equals("e")) idAct = Liberu.EAT;              // key E pressed 101
		if (key.equals("w")) idAct = Liberu.WALK;             // key W pressed 119
		if (key.equals("l")) idAct = Liberu.TURN_LEFT;        // key L pressed 108
		if (key.equals("r")) idAct = Liberu.TURN_RIGHT;       // key R pressed 114
    	while (!busEuIn.isEmpty())
    		busEuIn.poll();                // redundante já no PlayLiberu Liberu.clearTimeOut()
    	while (!busEuOut.isEmpty())
    		busEuOut.poll();
    	ecran.saveValue(35, 0);          // value 35
        if ( idAct == -1 && alt!=0 && mem.time[0][0].step > ActRandom) {
        	 // beginning If
        	if (actualPain) { // If havePain proxima recent sem dor gravado
        	    sono	 	 = false;   // despertar por dor
                ecran.saveValue(21, 0);             // value 21
// Se Dor executa proximo Bom e nao o +1
            	for (i=0; i<mem.time[recent+1][Liberu.NTRAIL-1].dendrites.size(); i++) {
                	busEuOut.add(new Signal(mem.time[recent+1][Liberu.NTRAIL-1].getDendrite(i).emitter,
                			mem.time[recent+1][Liberu.NTRAIL-1].getDendrite(i).actPower));
            		System.out.print("ProSelf recent emitter fired...");     // a retirar
                	System.out.println(mem.time[recent+1][Liberu.NTRAIL-1].getDendrite(i).emitter); // a retirar
                    }
            	System.out.print("ProSelf recent PAIN =");     // a retirar
            	System.out.println(recent+1); // a retirar
                for (j=0; j<Liberu.NTRAIL; j++) {     // update value
//                	mem.time[backDecision][j].setValue(0);
                	if (backDecision == recent) mem.time[backDecision+1][j].addValue();
                }
            	if (recent < Liberu.NNEURONT - 1 && mem.time[0][0].pointer < Liberu.NNEURONT - 1)
                	mem.time[mem.time[0][0].pointer][Liberu.NTRAIL-1].setValue(mem.time[recent][Liberu.NTRAIL-1].value);

                if (busEuOut.isEmpty()) {
            		sono();
                    // Random choice
                    	// Mathematics Induct Principle, from 0 goes to 1
                       	idAct = (int)Math.rint(Math.random()*(Liberu.ACTRND-1));
                    	bodyAct[idAct].setActive(true);
                		System.out.print("Random= ");    // retirar
                		System.out.println(idAct);    // retirar
                    	ecran.saveValue(35, 2);          // value 35
            	}
        	}
        	else {
    	    	pain = 0;
            	System.out.println("Pain 0.......................................");     // a retirar
        	    
        	    if (ansia) { // Se varia a Homeostase actua
// Se Dor executa proximo Bom e nao o +1
        	    	for (i=0; i<mem.time[recent+1][Liberu.NTRAIL-1].dendrites.size(); i++) {
                    	busEuOut.add(new Signal(mem.time[recent+1][Liberu.NTRAIL-1].getDendrite(i).emitter,
                    			mem.time[recent+1][Liberu.NTRAIL-1].getDendrite(i).actPower
                    															/* NOROADRENALINA# */));
                    	System.out.print("ProSelf recent emitter fired...");     // a retirar
                    	System.out.println(mem.time[recent+1][Liberu.NTRAIL-1].getDendrite(i).emitter); // a retirar
                   	}
                	if (recent < Liberu.NNEURONT - 1 && mem.time[0][0].pointer < Liberu.NNEURONT - 1)
                    	mem.time[mem.time[0][0].pointer][Liberu.NTRAIL-1].setValue(mem.time[recent][Liberu.NTRAIL-1].value);
                	System.out.print("ProSelf recent +1=");     // a retirar
                	System.out.println(recent+1); // a retirar

                	if (busEuOut.isEmpty()) {
                		sono();
                        // Random choice
                        	// Mathematics Induct Principle, from 0 goes to 1
                           	idAct = (int)Math.rint(Math.random()*(Liberu.ACTRND-1));
                        	bodyAct[idAct].setActive(true);
                    		System.out.print("Random= ");    // retirar
                    		System.out.println(idAct);    // retirar
                	}
                	else {
                	    sono	 	 = false;
                        ecran.saveValue(21, 0);             // value 21
                	}
            	}
        	}
        } // End If
        // Random choice
        else {
        	// Mathematics Induct Principle, from 0 goes to 1
        	if (idAct != -1) {
        		bodyAct[idAct].setActive(true);
        		System.out.print("Mentor= ");    // retirar
        		System.out.println(idAct);    // retirar
            	ecran.saveValue(35, 2);          // value 35
        	}
        	else {
               	idAct = (int)Math.rint(Math.random()*(Liberu.ACTRND-1));
                if (mem.time[0][0].step == 1)
                	idAct = Liberu.WALK;					// forces walking - genetic - simpatic system
        		bodyAct[idAct].setActive(true);
        		System.out.print("Random= ");    // retirar
        		System.out.println(idAct);    // retirar
            	ecran.saveValue(35, 1);          // value 35
        	}
        }
        	
        alt += 1;
        if (alt == 2 + (int)(mem.time[0][0].step / (int)(Liberu.NNEURONT / Liberu.RNDelay)))
        	alt = 0;

        backDecision = recent;

        if (mem.actual != 0)
        	if (100 * Liberu.perTime / mem.actual >= Liberu.PerTime && mem.actual >= Xanos) {
        		Liberu.ratVSemo = true;
        		System.out.println("Analog behavior...");    // Analog behavior
            }

        if (emotional)
        	emotionNormal();
	}

    public void setLearn() {
    	
		key = (String)text.getText().trim();
		ecran.repaint();
		System.out.print("Key= ");    // retirar
		System.out.println(key);    // retirar
    }
	
    private class LearnListener implements DocumentListener {
    	
    	public void insertUpdate(DocumentEvent e) { setLearn(); }
    	public void removeUpdate(DocumentEvent e) { setLearn(); }
    	public void changedUpdate(DocumentEvent e) {}
    }
    
    public void run() {
        
//		Motor.A.setSpeed(100);    // 900 = 100% power                                       NXT
//		Motor.B.setSpeed(100);    // degrees/s												NXT
//		Motor.C.setSpeed(100);    // degrees/s												NXT
		while(!interrupted() && !mem.time[0][0].end) {
    		runCerebellum();
    	}
        System.out.println("End of the RunEmotionalUnit Thread...");
    }
}
