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

/**
 *
 * @author Paulo Roque Silva
 */

import java.awt.Color;
import java.util.Iterator;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.swing.*;
import icommand.nxt.Motor;
import icommand.nxt.Battery;
import icommand.nxt.LightSensor;

public class Brain {
 // Constantes do c�rebro
    static final int  NEURONS = 5000; // N�mero passos
    static final int  SENSES = 6; // N�mero sentidos
    // com propriocetivo guardados no passado
    static final int  ACTS   = 5; // N�mero de a��es rob�
    static final int  ACTSeVOGALS   = 8;
    // N�mero de a��es e vogais rob�
    static final int  MOVES = 15; // N�mero de movimentos
 // Constantes do corpo   
    static final float MaxInSense = 255.0F;
    // M�xima amplitude nos sensores e na a��o
    static final float MinSense = 1.0F;
    // M�nima sensa��o analisada
    static final int  NanalogTaste = 3,
    // N�mero de sinais para cada sentido
                      NanalogVision= 3,
                      NanalogTact  = 2,
                      NanalogSound = 7,
                      NanalogSmell = 64,
                      NanalogProSelf=MOVES;
    static final int  NanalogTasteR = 1,
            		  NanalogVisionR= 2,
            		  NanalogTactR  = 1,
            		  NanalogSoundR = Mind.BANDS,
            		  NanalogSmellR = 3;
    static final int  NanalogTasteN = 1,
            		  NanalogVisionN= 1,
            		  NanalogTactN  = 1,
            		  NanalogSoundN = Mind.BANDS,
            		  NanalogSmellN = 0;
    static final int  AWALK     = 0,
    				  ABACK     = 1,
    				  BWALK     = 2,
    				  BBACK     = 3,
    				  SPEAK     = 4,
    				  //  FIM DOS ACTRND
    				  Vogal1	= 5,
    				  Vogal2	= 6,
    				  Vogal3	= 7,
    				  ENERGY    = 8,
    				  PAIN      = 9,
    				  New       =10,
    				  Happy     =11,
    				  Sad       =12,
    				  Afraid	=13,
    	    		  Motive    =14,
    				  //  FIM DOS MOVES

                      // Vari�veis para os sentidos
    				  NO_TACT   = 0, // N�o sente obst�culo
    				  TACT 		= 1, // Sente obst�culo
    
    				  Taste		= 0,
    				  Vision	= 1,
    				  Tact		= 2,
    				  Sound		= 3,
    				  Smell		= 4,
    				  ProSelf	= 5;

    static final int  DRINK     = 0,
			  EAT       = 1,
			  WALK      = 2,
			  TURN_LEFT = 3,
			  TURN_RIGHT= 4;
			  //  FIM DOS ACTRND
 // Vari�veis do Brain
    Cortex		      past; // Rede de mem�ria
    Emotions          decision;
    static ConcurrentLinkedQueue<Signal>
    				  BusInTaste,
    				  BusInVision,
					  BusInTact,
					  BusInSound,
					  BusInSmell,
					  BusInProSelf,
    				  BusSleepPreviews,
    				  BusSleepOut,
    				  BusSleepPreviews2,
    				  BusSleepOut2,
					  BusInAct;
    static Boolean	  BackHome; // Se a caminho de casa
    static int		  Best; // Melhor caminho
    
// Vari�veis do corpo
    public static Signal []bodyAction;
    // A��es no corpo do rob�
    public static boolean Energy; // Motiva��o para energia
	Iterator<Signal>  listen; // Ouve sinais dos buses
	Signal			  signInput; // Guarda o sinal
	
    
// Vari�veis da simula��o
    CyclicBarrier     stepBarrierAct;
    CyclicBarrier     stepBarrierDecision;
    CyclicBarrier     stepBarrierDecision2;
    CyclicBarrier     stepBarrierPast;
    CyclicBarrier     stepBarrierCerebellum;
    CyclicBarrier     stepBarrierSleep;
    
    JTextField        texto; // Tecla primida
    static boolean    Saudade, Ansiedade;

    /** Creates a new instance of Brain */
    public Brain(JTextField txt) {
    	int i;
    	
        texto        = txt;
    	past         = new Cortex(SENSES, ACTSeVOGALS, 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>();
        BusInAct      =
        		new ConcurrentLinkedQueue<Signal>();
        BusSleepPreviews  =
        		new ConcurrentLinkedQueue<Signal>();
        BusSleepOut		  =
        		new ConcurrentLinkedQueue<Signal>();
        BusSleepPreviews2 =
        		new ConcurrentLinkedQueue<Signal>();
        BusSleepOut2	  =
        		new ConcurrentLinkedQueue<Signal>();
        Ansiedade = false;
        Saudade = false;
    }
    
    public CyclicBarrier initiatePastTime() {

    	// Cria os processos do Remember
        stepBarrierPast   =
        		new CyclicBarrier(past.nNeuronsTime+1);
	    for (int n=0; n < past.nNeuronsTime; n++)
        	new Thread(new Remember(stepBarrierPast,
        			past.time[n],
        			decision)).start();
        return stepBarrierPast;
    }
    
    public CyclicBarrier initiateDecision() {

    	// Cria os processos do Emotions
        stepBarrierDecision  = new CyclicBarrier( 2 );
        stepBarrierDecision2 = new CyclicBarrier( 2 );
        decision = new Emotions(stepBarrierDecision,
        		stepBarrierDecision2, past, bodyAction,
        		texto);
	    new Thread(decision).start();
        return stepBarrierDecision;
    }
    
	public CyclicBarrier initiateSleep() {

    	// Cria os processos do Sleep
        stepBarrierSleep  =
        		new CyclicBarrier(past.nNeuronsTime + 1 );
	    for (int n=0; n < past.nNeuronsTime; n++)
        	new Thread(new Sleep(stepBarrierSleep,
        			past.time, n)).start();
        return stepBarrierSleep;
    }
    
    public CyclicBarrier initiateCerebellum() {

    	// Cria os processos do Cerebellum
        stepBarrierCerebellum  = new CyclicBarrier( 2 );
	    new Thread(new Cerebellum(stepBarrierCerebellum,
	    		past.hemisphereLeft, past.hemisphereRigth,
				past, past.nNeuronsTime)).start();
        return stepBarrierCerebellum;
    }
    
    public CyclicBarrier initiateAct() {

    	// Cria os processos do Act
        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++)
        	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();
    }

    public void clearBusSleepPreviews() {

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

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

    public void pollSleepOut2() {
    	
    	while (!BusSleepOut2.isEmpty()) // Esvazia bus
    		BusSleepOut2.poll();
    }

    public void transferOutIn() {
    	
    	while (!BusSleepPreviews.isEmpty()) // Esvazia bus
    		BusSleepPreviews.poll();

    	listen = BusSleepOut.iterator(); // Transfere
    	while (listen.hasNext()) { // L� todo o bus
        	signInput = listen.next();
        	BusSleepPreviews.add(new Signal(
        			signInput.emitter, signInput.actPower,
        			signInput.pleasure));
    	}
    	pollSleepOut();
    }

    public void transferOutIn2() {
    	
    	while (!BusSleepPreviews2.isEmpty()) // Esvazia bus
    		BusSleepPreviews2.poll();

    	listen = BusSleepOut2.iterator(); // Transfere
    	while (listen.hasNext()) { // L� todo o bus
        	signInput = listen.next();
        	BusSleepPreviews2.add(new Signal(
        			signInput.emitter, signInput.actPower,
        			signInput.pleasure));
    	}
    	pollSleepOut2();
    }

    public void decide(int pr) {  // Saudade
    	
    	Signal				signInput; 
        Iterator<Signal> 	listen;

    	listen = BusSleepOut.iterator();
    	while (listen.hasNext()) {
        	signInput = listen.next();
    		if (signInput.pleasure / (pr + 1) > Best) {
    			Best = signInput.pleasure / (pr + 1);
        		Mind.setBom((int)signInput.actPower);
        		Saudade = true;
    		}
        }
     }
    
    public void decide2(int pr) {   // Ansiedade

    	Signal				signInput;
        Iterator<Signal> 	listen;

    	listen = BusSleepOut2.iterator();
    	while (listen.hasNext()) {
        	signInput = listen.next();
    		if (signInput.pleasure / (pr + 1) > Best) {
    			Best = signInput.pleasure / (pr + 1);
        		Mind.setBom((int)signInput.actPower);
        		Ansiedade = true;
    		}
        }
     }
    
    public int addStep() {
    	
    	return past.addDataActual();
    }

    public void idAct1(int idAction) {
    	
    	decision.idAct1Action(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)
        	senseProSelf[Motive] = pw;
    }
    
    public void feelTaste(int []sense, int []plsr) {
    	int       m;
    	
    	for (m=0; m < sense.length; m++)
    		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[]) {


    	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;

    	plsr = 0;
        if (bodyAction[ENERGY].active &&
        		Battery.getVoltageMilliVolt() <= 8000) {
        	plsr = pw;
        	}
        if (Battery.getVoltageMilliVolt() <= 8000) {
        	bodyAction[ENERGY].setActive(true);
        	plsr = pw;
        	}

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

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

    	if (Motor.A.getTachoCount() > 0 &&
    			Motor.B.getTachoCount() < 0) { // em graus
        	senseProSelf[TURN_LEFT] = (int)bodyAction[Brain.
        	             	         TURN_LEFT].actPower;
    	}
    	if (Motor.B.getTachoCount() > 0 &&
    			Motor.A.getTachoCount() < 0) {
        	senseProSelf[TURN_RIGHT] = (int)bodyAction[Brain.
        	              	         TURN_RIGHT].actPower;
    	}
        if ((Motor.A.getTachoCount() > 0) &&
        		(Motor.B.getTachoCount() > 0)) {
        	senseProSelf[WALK] = (int)bodyAction[Brain.
        	                         WALK].actPower;
        }
        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].setPleasure(0);
        }
        return plsr;
    }
    
    public int proSelfRobot(int []senseProSelf, int pw) {
    	int	plsr;
    	boolean tact;
    	String s = "0";
    	
		try {
            Liberu.output.write((byte)99);
            Liberu.output.write('\n');
            while (!Liberu.input.ready())
                Thread.sleep(1);
            s = Liberu.input.readLine();
		}
    	catch (Exception e) {
            System.err.println(e.toString());
    	}
		tact = false;
		if (!s.isEmpty())
			tact = (s.charAt(0) == 1);
        
    	plsr = 0;
        if (BackHome && tact) {
        	bodyAction[ENERGY].setActive(true);
        	plsr = pw;
        	}

        if (Energy) {
        	plsr += (pw - Mind.local) * 10;
            Liberu.jButtonDAct.setBackground(Color.RED);
        	bodyAction[ENERGY].setActive(true);
        }

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

        if (bodyAction[Brain.Motive].active) {
			senseProSelf[Brain.Motive] = pw;
        	plsr += pw;
        }

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

    public int proSelf(int []senseProSelf,int []senseTaste,
    		int pw) {
    	int	plsr=0;
        
        // Come quando tem fome e sente comida
        if (bodyAction[EAT].active  &&
        		senseTaste[World.FOOD-1] == pw ||
        		bodyAction[EAT].active &&
        		past.time[0][0].step <=
        		Emotions.ActRandom) {
        	senseProSelf[EAT] = pw; // Sente comida
        	plsr += pw;
            bodyAction[EAT].setActive(false);
        	}
        if (Energy) {
        	plsr += (pw - Mind.local) * 10;
            Liberu.jButtonDAct.setBackground(Color.RED);
        	bodyAction[ENERGY].setActive(true);
        }

        if (bodyAction[ENERGY].active) {
        	bodyAction[ENERGY].setActive(false);
        	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[Brain.Motive].active) {
			senseProSelf[Brain.Motive] = pw;
        	plsr += pw;
        }

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

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