/*
 * Brain.java
 *
 * Created on 6 of December 2006, 1:34
 */

/**
 *
 * @author Paulo Roque Silva
 */
//import icommand.nxt.Motor;
//import icommand.nxt.Battery;
//import icommand.nxt.LightSensor;

import icommand.nxt.Motor;
import icommand.nxt.Battery;
import icommand.nxt.LightSensor;

import java.awt.Color;
import java.util.Iterator;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ConcurrentLinkedQueue;

public class Brain { // Total of used Threads = 2 * NEURONS + ACTRND + Cerebellum
	                  // + Decision + PlayLiberu + Liberu + JavaQueueEvents + Java???internal
// Brain Constants
    static final int  NEURONS = 10000; /* C. elegans = 302 neurons e 5000 connections
                                           39 sensitive neurons
                                           21 principal classes of motion neurons 
										   A 302 C.Elegans (11min)
                                           B 3000 Movie (1h 50min)
    									   C 10000 Ant (6h 6min) */
	static final int  SENSES = 6; // # of Senses (including Proprioception) recorded in the Past
    static final int  ACTS   = 5; // # of Acts of Liberu - BODY
    static final int  MOVES = 12; // # of motion Acts of Liberu - Genetic evolution, got number
// Body Constants    
    static final float MaxInSense = 255.0F;	// Max power for sensing (higher goes pain) and acting
    static final float MinSense = 1.0F; // Minimal differential of sensations or input differential
    static final int  NanalogTaste = 1, //temp NXT 1 Finch 2 Taste (3 Sherin of 3 possible variables)
                      NanalogVision= 1, //dist 2 NXT 1 Vision (3 Sherin of 3 possible variables)
                      NanalogTact  = 1, //botton NXT 1 Finch 2 Tact (1 Sherin of 2 value possible)
                      NanalogSound = Mind.BANDS,// Sound (7 Sherin of 7 possible variables)
                      NanalogSmell = 0,//RGB 3 NXT 0 Olfact (0 Sherin)
                      NanalogProSelf=MOVES;// ProSelf (7 Sherins of 5 moves + 2 motivations)3Dez2011 15:18
    static final int  AWALK     = 0,    // The robot moves backwards
    				  ABACK     = 1,    // The robot moves forward
    				  BWALK     = 2,    // The robot turns him self to the left
    				  BBACK     = 3,    // The robot turns him self to the right
    				  SPEAK     = 4,    // The robot speaks
    				  //  FIM DOS ACTRND
    				  ENERGY    = 5,    // The robot drinks water
    				  PAIN      = 6,    // Touch sense in the wall with pain %ACETILCOLINA STOP
    				  New       = 7,    // Emotion, New expression &SERETONINA ID
    				  Happy     = 8,    // Emotion, happy expression $DOPAMINA acceleration (a)
    				  Sad       = 9,    // Emotion, Sad expression #ADRENALINA ataca-foge Parasimp-simpati (v)
    				  Afraid	=10,    // Emotion, Afraid expression
    	    		  Motive    =11,    // drinking MOTIVATION
    				  //  FIM DOS MOVES

                      // Sense Variables
    				  NO_TACT   = 0,    // fell no tact
    				  TACT 		= 1,    // fell tact - the wall
    
    				  Taste		= 0,
    				  Vision	= 1,
    				  Tact		= 2,
    				  Sound		= 3,
    				  Smell		= 4,
    				  ProSelf	= 5;

// Brain Variables
    Cortex		      past; // Memory network of Liberu
    Emotions          decision;
    static ConcurrentLinkedQueue<Signal> // because, credit is given by who's listening, not who's talking...
    				  BusInTaste, // Bus perception in to the Past - taste
    				  BusInVision, // Bus perception in to the Past - vision
					  BusInTact, // Bus perception in to the Past - touch
					  BusInSound, // Bus perception in to the Past - sound
					  BusInSmell, // Bus perception in to the Past - olfactsound
					  BusInProSelf, // Bus perception in to the Past - proprioception
					  BusInCerebellum, // Bus data from the Decision to Cerebellum nodes
    				  BusSleepPreviews, // Bus SONO Preview
    				  BusSleepOut, // Bus SONO Out
    				  BusSleepPreviews2, // Bus SONO Preview
    				  BusSleepOut2, // Bus SONO Out
					  BusInAct; // Bus data from the Cerebellum to Act nodes
    static Boolean	  BackHome; // true if backs home
    static int		  Best; // best way
    
// Body Variables
    public static Signal []bodyAction; // Actions of the body of the robot
    public static boolean Energy; // motivation to drink
	Iterator<Signal>  listen; // Listen Signal from bus
	Signal			  signInput; // Read input Signal
	
    
// Simulator variables
    CyclicBarrier     stepBarrierAct; // Waiting step act barrier
    CyclicBarrier     stepBarrierDecision; // Waiting step RunEmotionalUnit barrier
    CyclicBarrier     stepBarrierDecision2; // Waiting step RunEmotionalUnit barrier
    CyclicBarrier     stepBarrierPast; // Waiting step Time Present barrier
    CyclicBarrier     stepBarrierCerebellum; // Waiting step Cerebellum barrier
    CyclicBarrier     stepBarrierSleep; // Waiting step Cerebellum barrier
    
    /** Creates a new instance of Liberu */
    public Brain() {
    	int i;
    	
    	past         = new Cortex(SENSES, ACTS, NEURONS);
    	bodyAction   = new Signal[MOVES];
        for (i=0; i<bodyAction.length; i++)
        	bodyAction[i] = new Signal(i, 0, 0);
        setEnergy(false);
        BackHome      = false;
        
        BusInTaste   = new ConcurrentLinkedQueue<Signal>();
        BusInVision  = new ConcurrentLinkedQueue<Signal>();
        BusInTact    = new ConcurrentLinkedQueue<Signal>();
        BusInSound   = new ConcurrentLinkedQueue<Signal>();
        BusInSmell   = new ConcurrentLinkedQueue<Signal>();
        BusInProSelf = new ConcurrentLinkedQueue<Signal>();
        BusInCerebellum = new ConcurrentLinkedQueue<Signal>();
        BusInAct      = new ConcurrentLinkedQueue<Signal>();
        BusSleepPreviews  = new ConcurrentLinkedQueue<Signal>();
        BusSleepOut		  = new ConcurrentLinkedQueue<Signal>();
        BusSleepPreviews2 = new ConcurrentLinkedQueue<Signal>();
        BusSleepOut2	  = new ConcurrentLinkedQueue<Signal>();
    }
    
    public CyclicBarrier initiatePastTime() {

    	// Create Past Time running threads
        stepBarrierPast   = new CyclicBarrier(past.nNeuronsTime+1);
	    for (int n=0; n < past.nNeuronsTime; n++) // ACTION THREADS (NActions)
        	new Thread(new Remember(stepBarrierPast, past.time[n],
        			decision)).start();
        return stepBarrierPast;
    }
    
    public CyclicBarrier initiateDecision() {

    	// Create Decision thread
        stepBarrierDecision  = new CyclicBarrier( 2 );
        stepBarrierDecision2 = new CyclicBarrier( 2 );
        decision = new Emotions(stepBarrierDecision, stepBarrierDecision2, past, bodyAction);
	    new Thread(decision).start();
        return stepBarrierDecision;
    }
    
	public CyclicBarrier initiateSleep() {

    	// Create Sleep running threads
        stepBarrierSleep  = new CyclicBarrier(past.nNeuronsTime + 1 //,
   			 /* Process that runs between steps */
   			 /* new MoveQueueOut(busInProSelf) */ );
	    for (int n=0; n < past.nNeuronsTime; n++) // ACTION THREADS (NActions)
        	new Thread(new Sleep(stepBarrierSleep, past.time[n])).start();
        return stepBarrierSleep;
    }
    
    public CyclicBarrier initiateCerebellum() {

    	// Create Cerebellum thread
        stepBarrierCerebellum  = new CyclicBarrier( 2 );
	    new Thread(new Cerebellum(stepBarrierCerebellum,
	    		past.hemisphereLeft, past.hemisphereRigth,
				past, past.nNeuronsTime)).start();
        return stepBarrierCerebellum;
    }
    
    public CyclicBarrier initiateAct() {

    	// Create Act running threads
        stepBarrierAct  = new CyclicBarrier(past.nNeuronsAct + 1);
    	for (int i=0; i<bodyAction.length; i++)
    		bodyAction[i] = new Signal(i, 0, 0);
	    for (int n=0; n < past.nNeuronsAct; n++)// ACTION THREADS (NActions)
        	new Thread(new Act(stepBarrierAct, past.actNode[n],
        								bodyAction)).start();
        return stepBarrierAct;
    }
    
    public CyclicBarrier initiateDecision2() {

        return stepBarrierDecision2;
    }
    
    public void clearBusesIn(boolean proSelf) {

    	while (!BusInTaste.isEmpty())
    		BusInTaste.poll();
    	while (!BusInVision.isEmpty())
    		BusInVision.poll();
    	while (!BusInTact.isEmpty())
    		BusInTact.poll();
    	while (!BusInSound.isEmpty())
    		BusInSound.poll();
    	while (!BusInSmell.isEmpty())
    		BusInSmell.poll();
    	if (proSelf)
        	while (!BusInProSelf.isEmpty())
        		BusInProSelf.poll();
    	while (!BusInCerebellum.isEmpty())
    		BusInCerebellum.poll();
    }

    public void clearBusSleepPreviews() {

    	while (!BusSleepPreviews.isEmpty())
    		BusSleepPreviews.poll();
    }
    	
    public void clearBusSleepPreviews2() {

    	while (!BusSleepPreviews2.isEmpty())
    		BusSleepPreviews2.poll();
    }
    	
    public void pollSleepOut() {
    	
    	while (!BusSleepOut.isEmpty()) // Empty Out
    		BusSleepOut.poll();
    }

    public void pollSleepOut2() {
    	
    	while (!BusSleepOut2.isEmpty()) // Empty Out
    		BusSleepOut2.poll();
    }

    public void transferOutIn() {
    	
    	while (!BusSleepPreviews.isEmpty()) // Empty In
    		BusSleepPreviews.poll();

    	listen = BusSleepOut.iterator(); // Transfer
    	while (listen.hasNext()) { // read all the input bus
        	signInput = listen.next();
        	BusSleepPreviews.add(new Signal(signInput.emitter,
        			signInput.actPower, signInput.pleasure));
    	}
    	pollSleepOut();
    }

    public void transferOutIn2() {
    	
    	while (!BusSleepPreviews2.isEmpty()) // Empty In
    		BusSleepPreviews2.poll();

    	listen = BusSleepOut2.iterator(); // Transfer
    	while (listen.hasNext()) { // read all the input bus
        	signInput = listen.next();
        	BusSleepPreviews2.add(new Signal(signInput.emitter,
        			signInput.actPower, signInput.pleasure));
    	}
    	pollSleepOut2();
    }

    public boolean decide(int fim, int pr) {
    	boolean deci;
    	
    	Signal				signInput; // Read input Signal
        Iterator<Signal> 	listen; // Listen id from busIn

        deci = false;
    	listen = BusSleepOut.iterator();
    	while (listen.hasNext()) { // read all past beside Good
        	signInput = listen.next();
        	if (Math.abs(fim) == Math.abs(signInput.emitter)) {
        		if (signInput.pleasure / pr > Best) {
        			Best = signInput.pleasure / pr;
            		Mind.setBom(-(int)signInput.actPower);
            		deci = true;
        		}
        	}
        }
    	return deci;
     }
    
    public boolean decide2(int fim, int pr) {
    	boolean deci;

    	Signal				signInput; // Read input Signal
        Iterator<Signal> 	listen; // Listen id from busIn

        deci = false;
    	listen = BusSleepOut2.iterator();
    	while (listen.hasNext()) { // read all past beside Good
        	signInput = listen.next();
        	if (Math.abs(fim) == Math.abs(signInput.emitter)) {
        		if (signInput.pleasure / pr > Best) {
        			Best = signInput.pleasure / pr;
            		Mind.setBom((int)signInput.actPower);  // REVER next
            		deci = true;
        		}
        	}
        }
    	return deci;
     }
    
    public void send(int bom) {

    	if (Math.abs(bom) == Cerebellum.ESCape)
        	BusInCerebellum.add(new Signal( // No action
					Cerebellum.ESCape, 0, 0));
    	else
        	BusInCerebellum.add(new Signal( // bom action
					bom, 0, 0));
    }

    public int addStep() {
    	
    	return past.addDataActual();
    }

    public void idAct1(int idAction) {
    	
    	decision.idAct1Action(idAction);
    }
    
    public void idAct2(int idAction) {
    	
    	decision.idAct2Action(idAction);
    }
    
    public static boolean getEnergy() {

    	return Energy;
    }

    public static void setEnergy(boolean state) {
    	
    	Energy = state;
    }
    
    public static void setBackHome(boolean state) {
    	
    	BackHome = state;
    }
    
    public void motivationNXT(int []senseProSelf, int pw) {
    	
        if (BackHome) { // 9 Volts full (9000mV)
        	senseProSelf[Motive] = pw; // thirsty motivation
        }
    }
    
    public void feelTaste(int []sense, int []plsr) {
    	int       m;
    	
    	for (m=0; m < sense.length; m++) // send all signals of a sense (send a Sherin)
    		BusInTaste.add(new Signal(m, sense[m], plsr[m]));
    }

    public void feelVision(int []sense, int []plsr) {
    	int       m;
    	
    	for (m=0; m < sense.length; m++)
       		BusInVision.add(new Signal(m, sense[m], plsr[m]));
    }

    public void feelTact(int []sense, int []plsr) {
    	int       m;
    	
    	for (m=0; m < sense.length; m++)
    		BusInTact.add(new Signal(m, sense[m], plsr[m]));
    }

    public void feelSound(int []sense, int []plsr) {
    	int       m;
    	
    	for (m=0; m < sense.length; m++)
    		BusInSound.add(new Signal(m, sense[m], plsr[m]));
    }

    public void feelSmell(int []sense, int []plsr) {
    	int       m;
    	
    	for (m=0; m < sense.length; m++)
    		BusInSmell.add(new Signal(m, sense[m], plsr[m]));
    }

    public void feelProSelf(int []sense, int []plsr) {
    	int       m;
    	
    	for (m=0; m < sense.length; m++)
    		BusInProSelf.add(new Signal(m, sense[m], plsr[m]));
    }


    public void act(Signal result[]) {   // miro-acts and micro-contra-acts

    	for (int i=0; i<bodyAction.length; i++) {
    		result[i].setActive(bodyAction[i].active);
    		result[i].setActPower(bodyAction[i].actPower);
    	}
    }

    public int proSelfNXT(int []senseProSelf, LightSensor light, int pw) {
    	int	plsr;
//    	boolean tact; // formica
//    	String s = ""; // formica
    	
/*		try {
            Liberu.output.write((byte)99);  // Alterar para dockStation
            Liberu.output.write('\n');
            while (!Liberu.input.ready())
                Thread.sleep(1);
            s = Liberu.input.readLine();
		}
    	catch (Exception e) {
            System.err.println(e.toString());
    	}
		tact = (s.charAt(0) == 1);
*/        
    	// Drinks when taste water and feels thirsty
    	plsr = 0;
/*        if (BackHome && tact */ // Formica
        		/* Na TOMADA */
        		// Utero body simulation - Ovulacao
        		/*|| bodyAction[DRINK].active && Neuron.step <= Decision.ActRandom  ) { */
        if (bodyAction[ENERGY].active  && Battery.getVoltageMilliVolt() <= 8000
        		/* Na TOMADA */
        		// Utero body simulation - Ovulacao
        		/*|| bodyAction[DRINK].active && Neuron.step <= Decision.ActRandom*/) {
//        	bodyAction[ENERGY].setActive(true);
//        	decision.emotionHappy();
        	plsr = pw;
        	}
        if (Battery.getVoltageMilliVolt() <= 8000
        		/*&& light.getLightPercent() <= 20*/ /* Na TOMADA */) {
        	bodyAction[ENERGY].setActive(true);
//        	setEnergy(false);
//        	decision.emotionHappy();
        	plsr = pw;
        	}

        if (bodyAction[SPEAK].active)
        	senseProSelf[SPEAK] = pw; // sense speak

        if (bodyAction[ENERGY].active)
        	senseProSelf[ENERGY] = pw; // sense energy
    	if (bodyAction[ENERGY].active && past.time[0][0].step >= Emotions.ActRandom) {
    		Energy = true;
            Liberu.jButtonDAct.setBackground(Color.RED);
    	}

//    	if (bodyAction[AWALK].active && bodyAction[BBACK].active) { // in degrees
    	if (Motor.A.getTachoCount() > 0 && Motor.B.getTachoCount() < 0) { // in degrees
        	senseProSelf[AWALK] = pw;
        	senseProSelf[BBACK] = pw;
    	}
//    	if (bodyAction[ABACK].active && bodyAction[BWALK].active) { // in degrees
    	if (Motor.B.getTachoCount() > 0 && Motor.A.getTachoCount() < 0) {
        	senseProSelf[ABACK] = pw;
        	senseProSelf[BWALK] = pw;
    	}
//    	if (bodyAction[AWALK].active && bodyAction[BWALK].active) { // in degrees
        if ((Motor.A.getTachoCount() > 0) && (Motor.B.getTachoCount() > 0)) {
        	senseProSelf[AWALK] = pw;
        	senseProSelf[BWALK] = pw;
        }
//    	if (bodyAction[ABACK].active && bodyAction[BBACK].active) { // in degrees
        if ((Motor.A.getTachoCount() < 0) && (Motor.B.getTachoCount() < 0)) {
        	senseProSelf[ABACK] = pw;
        	senseProSelf[BBACK] = pw;
        }
        Motor.A.resetTachoCount();
        Motor.B.resetTachoCount();

        if (bodyAction[Brain.Motive].active)
			senseProSelf[Brain.Motive] = pw;

        for (int i=0; i<bodyAction.length; i++) {
            bodyAction[i].setActive(false);
            bodyAction[i].setActPower(0);
            bodyAction[i].setPleasure(0);
        }
        return plsr;
    }

    public void endLiberu(boolean endL) {
        
    	Neuron.end = endL;
    }

    public void sono() {
    	int i, size;
    	
    	size = Emotions.Average.size();
    	for (i=0; i < size; i++)
    		Emotions.Average.remove(0);
    	clearBusSleepPreviews();
    	Emotions.IdAct1 = Cerebellum.ESCape;
	}
}