using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using System.Diagnostics;
using Clarion;
using Clarion.Framework;
using Clarion.Framework.Core;
using Clarion.Framework.Templates;
using Clarion.Framework.Extensions;
using WorldServerLibrary.Model;

namespace ClarionSimulation { 
	                                    //IMPORTANTE: Desativando o Motivation System (MS)
										// O agente demora a aprender e a interferência em suas ações é muito relevante. Por hora, o agente está apenas rotacionando e andando para frente.
										// PARA REATIVAR O MS -> RETIRE OS COMENTÁRIOS COM "MS-OFF"

	//#################################### HANDLERS ##########################################
	/// This event will be fired at the beginning of each cognitive cycle in order to get the current visual sensor information.
	/// <returns>The Visual Sensor Information Itens</returns> 
	public delegate IList<Thing> InputVisualSensorialInformationEventHandler();

	/// This event will be fired at the end of each cognitive cycle in order to translate the selected action to a World Server Command
	/// <param name="externalAction">The external action that was choice</param>
	public delegate void OutputActionChunkEventHandler(ClarionAgentActionType externalAction);


	//#################################### ACTION TYPES ##########################################
	/// Public enum that represents all possibilities of agent actions
	public enum ClarionAgentActionType { 

		DO_NOTHING,
		ROTATE,
		GO_AHEAD,
		GET_JEWEL,
		EAT_FOOD,
		MOVE_JEWEL,  
		MOVE_FOOD,
	
	}



	//#################################### CLARION AGENT (CLARION SIMULATOR) ##########################################
	public class ClarionAgent { 



		#region Constants
		// CONSTANTES SINALIZADORAS DE EVENTOS E STATUS DA ARENA:
		private String SENSOR_VISUAL_DIMENSION = "VisualSensor"; // Constant that represents the Visual Sensor
		private String DIMENSION_WALL_AHEAD = "WallAhead";
		private String DIMENSION_WALL_FARAWAY = "WallFarAway";
		private String DIMENSION_JEWEL_AHEAD = "JewelAhead";
		private String DIMENSION_JEWEL_FARAWAY= "JewelFarAway";
		private String DIMENSION_FOOD_AHEAD = "FoodAhead";
		private String DIMENSION_FOOD_FARAWAY= "FoodFarAway";

		// CONSTANTES DE RESTRIÇÃO E CONDICIONAMENTO - PROCESSO REATIVO MS:
		private double DIST_MIN_WALL = 40.0;		

		//private String GO_AHEAD = "GoalGoAhead";
		//private String ROTATE = "GoalAvoidWall";
		#endregion



		#region Properties
		#region Simulation
		// CONSTANTES DE SIMULAÇÃO:
		// If this value is greater than zero, the agent will have a finite number of cognitive cycle. Otherwise, it will have infinite cycles.
		public double MaxNumberOfCognitiveCycles = -1;
		// Current cognitive cycle number
		private double CurrentCognitiveCycle = 0;
		// Time between cognitive cycle in miliseconds
		public Int32 TimeBetweenCognitiveCycles = 0;
		// A thread Class that will handle the simulation process
		private Thread runThread; // executa o ciclo cognitivo.
		#endregion Simulation

		public Int32 rotateStuck = 0;
		public Int32 goAheadStuck = 0;
		public Int32 getJewelStuck = 0;

		#region Agent
		// ATRIBUTOS E MÉTODOS DO AGENTE:
		// The agent: 
		private Clarion.Framework.Agent CurrentAgent;

		// HANDLERS - ORIGINALMENTE DO SIMULATOR, ATRIBUIDO AO AGENTE PARA PERCEPÇÃO EM OUTRO PASSO:
		/// For each cognitive cycle, this event will be called in order to the agent receives the current sensorial information
		public event InputVisualSensorialInformationEventHandler OnNewVisualSensorialInformation;
		/// For each cognitive cycle, this event will be called when the agent selects one action
		public event OutputActionChunkEventHandler OnNewExternalActionSelected;
		#endregion Agent


		#region Perception Input
		// PERCEPÇÕES:
		private DimensionValuePair InputWallAhead;
		private DimensionValuePair InputWallFarAway;
		private DimensionValuePair InputJewelAhead;
		private DimensionValuePair InputJewelFarAway;
		private DimensionValuePair InputFoodAhead;
		private DimensionValuePair InputFoodFarAway;
		#endregion Perception Input




		#region Action Output
		// COMANDOS DE SAÍDA PARA O WS3D:
		/// Output action that makes the agent to rotate clockwise (to avoid wall)
		private ExternalActionChunk OutputRotateClockwise;
		private ExternalActionChunk OutputGoAhead;  // Output action that makes the agent go ahead
		private ExternalActionChunk OutputGetJewel; // APANHA A JÓIA PROXIMA
		private ExternalActionChunk OutputEatFood;  // COME A COMIDA PRÓXIMA
		private ExternalActionChunk OutputMoveJewel;// MOVE EM DIREÇÃO À JÓIA MAIS PRÓXIMA
		private ExternalActionChunk OutputMoveFood; // MOVE EM DIREÇÃO À COMIDA MAIS PRÓXIMA

		#endregion Action Output



		//MS-OFF   #region Goal Chunk
		// OBJETIVOS - EVITAR PAREDES E CONTINUAR ANDANDO - PROCESSO REATIVO:
		//MS-OFF private GoalChunk GoalAvoidWall;
		//MS-OFF private GoalChunk GoalGoAhead;  // BEE CURIOUS!!

		// MS-OFF  #endregion Goal Chunk
		#endregion Properties



		#region Constructor
		//#################################### CLARION CONSTRUCTOR (CLARION SIMULATOR) ##########################################
		public ClarionAgent() {         

			// Initialize the agent
			CurrentAgent = World.NewAgent("Current Agent");

			// INICIALIZAÇÃO DE PERCEPÇÕES (INPUT):
			InputWallAhead = World.NewDimensionValuePair(SENSOR_VISUAL_DIMENSION, DIMENSION_WALL_AHEAD);
			InputWallFarAway = World.NewDimensionValuePair(SENSOR_VISUAL_DIMENSION, DIMENSION_WALL_FARAWAY);
			InputJewelAhead = World.NewDimensionValuePair(SENSOR_VISUAL_DIMENSION, DIMENSION_JEWEL_AHEAD);
			InputJewelFarAway = World.NewDimensionValuePair(SENSOR_VISUAL_DIMENSION, DIMENSION_JEWEL_FARAWAY);
			InputFoodAhead = World.NewDimensionValuePair(SENSOR_VISUAL_DIMENSION, DIMENSION_FOOD_AHEAD);
			InputFoodFarAway = World.NewDimensionValuePair(SENSOR_VISUAL_DIMENSION, DIMENSION_FOOD_FARAWAY);

			// INICIALIZAÇÃO DE COMANDOS (OUTPUT):
			OutputRotateClockwise = World.NewExternalActionChunk(ClarionAgentActionType.ROTATE.ToString());
			// OutputRotateClockwise.Add (World.NewDimensionValuePair("type", ClarionAgentActionType.ROTATE.ToString()));
			OutputGoAhead = World.NewExternalActionChunk(ClarionAgentActionType.GO_AHEAD.ToString());
			// OutputGoAhead.Add (World.NewDimensionValuePair("type", ClarionAgentActionType.GO_AHEAD.ToString()));
			OutputGetJewel = World.NewExternalActionChunk(ClarionAgentActionType.GET_JEWEL.ToString());
			OutputEatFood = World.NewExternalActionChunk(ClarionAgentActionType.EAT_FOOD.ToString());
			OutputMoveJewel = World.NewExternalActionChunk(ClarionAgentActionType.MOVE_JEWEL.ToString());
			OutputMoveFood = World.NewExternalActionChunk(ClarionAgentActionType.MOVE_FOOD.ToString());



			// DECLARAÇÃO DO THREAD RESPONSÁVEL (RunThread) POR INICIAR O CICLO COGNITIVO:
			runThread = new Thread(RunThread);
			Console.WriteLine("Agent started");

		}
		#endregion Constuctor


		// ########################  MÉTODOS ##########################
		#region Public Methods
		// CHAMADO DA CLASSE MAINCLASS.CS - START RunThread, O CICLO COGNITIVO
		public void Run() {                
			Console.WriteLine ("Running ...\n");
			// Setup Agent to run
			if (runThread != null && !runThread.IsAlive) {             
				Console.WriteLine ("Setup agent infrastructure  ...\n");
				SetupAgentInfraStructure(); // CONFIGURA OS MODULOS CLARION QUE SERÃO UTILIZADOS NA SIMULAÇÃO.
				if (OnNewVisualSensorialInformation != null) {                 
					// Start Simulation Thread  
					Console.Out.WriteLine ("STARTING RUNTHREAD!!!!!!!!!!!!!");
					runThread.Start(null);
				}
			}
		}



		// ABORTAR SIMULAÇÃO:
		/// <param name="deleteAgent">If true beyond abort the current simulation it will die the agent.</param>
		public void Abort(Boolean deleteAgent) {
			Console.WriteLine ("Aborting ...\n");
			if (runThread != null && runThread.IsAlive) {             
				runThread.Abort();
			}
			if (CurrentAgent != null && deleteAgent) {             
				CurrentAgent.Die();
			}
		}
		#endregion Public Methods



		#region Setup Agent Methods
		// CONFIGURAR A INFRAESTRUTURA - MODULOS CLARION UTILIZADOS, ACS (BOTTOM UP -> CONHECIMENTO) E MS (OBJETIVOS INTERNOS)
		/// Setup agent infra structure (ACS, NACS, MS and MCS)
		private void SetupAgentInfraStructure() {        
			// Setup the ACS Subsystem
			Console.WriteLine ("Setup ACS ....... \n");
			SetupACS();                    
			//Setup the MCS and MS Subsystems
			// MS-OFF  	Console.WriteLine ("Setup MS ....... \n");
			// MS-OFF  SetupMS();                    
		}


		//###################################################################### SETUP ACS - ACTION CENTERED SUBSYSTEM  #####################################################################################
		#region ACS

		private void SetupACS() {

			//##############################################
			// Create Colission Wall Rule
			SupportCalculator avoidCollisionWallSupportCalculator = FixedRuleDelegateToAvoidColissionWall;
			FixedRule ruleAvoidCollisionWall = AgentInitializer.InitializeActionRule(CurrentAgent, FixedRule.Factory, OutputRotateClockwise, avoidCollisionWallSupportCalculator);

			// Commit this rule to Agent (in the ACS)
			CurrentAgent.Commit(ruleAvoidCollisionWall);
			//##############################################

			//##############################################
			// Create Colission To Go Ahead  - MUITO GENÉRICO
			SupportCalculator goAheadSupportCalculator = FixedRuleDelegateToGoAhead;
			FixedRule ruleGoAhead = AgentInitializer.InitializeActionRule(CurrentAgent, FixedRule.Factory, OutputGoAhead, goAheadSupportCalculator);

			// Commit this rule to Agent (in the ACS)
			CurrentAgent.Commit(ruleGoAhead);  
			//#############################################

			//#############################################
			// Create ROTATE - NOTHING NEW
			SupportCalculator rotateSupportCalculator = FixedRuleDelegateRotate;
			FixedRule ruleRotate = AgentInitializer.InitializeActionRule(CurrentAgent, FixedRule.Factory, OutputRotateClockwise, rotateSupportCalculator);

			// Commit this rule to Agent (in the ACS)
			CurrentAgent.Commit(ruleRotate);


			//#############################################
			// Create get jewel
			SupportCalculator getJewelSupportCalculator = FixedRuleDelegateToGetJewel;
			FixedRule ruleGetJewel = AgentInitializer.InitializeActionRule(CurrentAgent, FixedRule.Factory, OutputGetJewel, getJewelSupportCalculator);

			// Commit this rule to Agent (in the ACS)
			CurrentAgent.Commit(ruleGetJewel);
			//#############################################


			//#############################################
			// Create eat food
			SupportCalculator eatFoodSupportCalculator = FixedRuleDelegateToEatFood;
			FixedRule ruleEatFood = AgentInitializer.InitializeActionRule(CurrentAgent, FixedRule.Factory, OutputEatFood, eatFoodSupportCalculator);

			// Commit this rule to Agent (in the ACS)
			CurrentAgent.Commit(ruleEatFood);
			//#############################################



			//#############################################
			// Create move jewel
			SupportCalculator moveJewelSupportCalculator = FixedRuleDelegateToMoveJewel;
			FixedRule ruleMoveJewel = AgentInitializer.InitializeActionRule(CurrentAgent, FixedRule.Factory, OutputMoveJewel, moveJewelSupportCalculator);

			// Commit this rule to Agent (in the ACS)
			CurrentAgent.Commit(ruleMoveJewel);
			//#############################################


			//#############################################
			// Create move food
			SupportCalculator moveFoodSupportCalculator = FixedRuleDelegateToMoveFood;
			FixedRule ruleMoveFood = AgentInitializer.InitializeActionRule(CurrentAgent, FixedRule.Factory, OutputMoveFood, moveFoodSupportCalculator);

			// Commit this rule to Agent (in the ACS)
			CurrentAgent.Commit(ruleMoveFood);
			//#############################################


			// Disable Rule Refinement
			CurrentAgent.ACS.Parameters.PERFORM_RER_REFINEMENT = false;
			// The selectio type will be probabilistic
			CurrentAgent.ACS.Parameters.LEVEL_SELECTION_METHOD = ActionCenteredSubsystem.LevelSelectionMethods.STOCHASTIC;
			// The selection of the action will be fixed (not variable) i.e. only the statement defined above.
			CurrentAgent.ACS.Parameters.LEVEL_SELECTION_OPTION = ActionCenteredSubsystem.LevelSelectionOptions.FIXED;

			// Define Probabilistic values
			CurrentAgent.ACS.Parameters.FIXED_FR_LEVEL_SELECTION_MEASURE = 1;
			CurrentAgent.ACS.Parameters.FIXED_IRL_LEVEL_SELECTION_MEASURE = 0;
			CurrentAgent.ACS.Parameters.FIXED_BL_LEVEL_SELECTION_MEASURE = 0;
			CurrentAgent.ACS.Parameters.FIXED_RER_LEVEL_SELECTION_MEASURE = 0;
		}
		#endregion ACS





		//###################################################################### SETUP MS - MOTIVATION SYSTEM (REACTION)  #####################################################################################
		/* // MS-OFF  

		#region MS

		private void SetupMS() {                    
			// Goal Chunk and add inputs and output to QBPNet
			//Console.WriteLine ("First step ... MS ... \n");			
			GoalAvoidWall = World.NewGoalChunk("GoalAvoidWall");	
			GoalGoAhead = World.NewGoalChunk("GoalGoAhead");
			SimplifiedQBPNetwork net = AgentInitializer.InitializeImplicitDecisionNetwork(CurrentAgent, SimplifiedQBPNetwork.Factory);
			net.Input.Add(InputWallAhead); 
			net.Input.Add(GoalAvoidWall); 
			net.Input.Add(InputWallFarAway); 			
			net.Input.Add(GoalGoAhead); 			
			net.Output.Add(OutputRotateClockwise); 			
			net.Output.Add(OutputGoAhead); 			

			// Learning rate, agent commit with QBPNet and rule refinament for action rule   
			net.Parameters.LEARNING_RATE = 1;   
			CurrentAgent.Commit(net); 
			RefineableActionRule.GlobalParameters.INFORMATION_GAIN_OPTION = RefineableActionRule.IGOptions.PERFECT;

			GoalStructureUpdateActionChunk setAvoidWall = World.NewGoalStructureUpdateActionChunk();
			setAvoidWall.Add(GoalStructure.RecognizedActions.SET_RESET, GoalAvoidWall);
			GoalStructureUpdateActionChunk setGoAhead = World.NewGoalStructureUpdateActionChunk();
			setGoAhead.Add(GoalStructure.RecognizedActions.SET_RESET, GoalGoAhead);


			// MS Module: Drives and Drive Equation: physical danger drive (bis drive). Also, drive equation. 
			AvoidingPhysicalDangerDrive wallDrive = AgentInitializer.InitializeDrive(CurrentAgent, AvoidingPhysicalDangerDrive.Factory, .7, (DeficitChangeProcessor)Goals_DeficitChange); 
			DriveEquation wallEquation = AgentInitializer.InitializeDriveComponent(wallDrive, DriveEquation.Factory);
			wallDrive.Commit(wallEquation);
			CurrentAgent.Commit(wallDrive);

			// MS Module: Drives and Drive Equation: autonomy drive (bas drive). Also, drive equation. 
			//AutonomyDrive walkDrive = AgentInitializer.InitializeDrive(CurrentAgent, AutonomyDrive.Factory, .5); 
			CuriosityDrive walkDrive = AgentInitializer.InitializeDrive(CurrentAgent, CuriosityDrive.Factory, .5, (DeficitChangeProcessor)Goals_DeficitChange); 
			DriveEquation walkEquation = AgentInitializer.InitializeDriveComponent(walkDrive, DriveEquation.Factory);
			walkDrive.Commit(walkEquation);
			CurrentAgent.Commit(walkDrive);


			// MCS Module: Goal selection module and Goal Selection: combine goal strengths         
			GoalSelectionModule gsm = AgentInitializer.InitializeMetaCognitiveModule(CurrentAgent, GoalSelectionModule.Factory);
			GoalSelectionEquation gse = AgentInitializer.InitializeMetaCognitiveDecisionNetwork(gsm, GoalSelectionEquation.Factory);

			// Make goal recommendations for the goal structure based  
			gse.Input.Add(wallDrive.GetDriveStrength());    
			gse.Input.Add(walkDrive.GetDriveStrength());    

			// Goal Selection Equation: provide information to GoalSelectionModule
			GoalStructureUpdateActionChunk goalActWall = World.NewGoalStructureUpdateActionChunk();
			GoalStructureUpdateActionChunk goalActGo = World.NewGoalStructureUpdateActionChunk();

			// update the goals in MS subsystem
			goalActWall.Add(GoalStructure.RecognizedActions.SET_RESET, GoalAvoidWall); 
			goalActGo.Add(GoalStructure.RecognizedActions.SET_RESET, GoalGoAhead); 

			//Add output, set relevance and commit
			gse.Output.Add(goalActWall); 
			gse.Output.Add(goalActGo); 		
			//###### DECIDIR SE VAI USAR SETRELEVANCE OU NÃO

			//   gse.SetRelevance(goalActWall, wallDrive, 1); 
			//   gse.SetRelevance(goalActGo, wallDrive, 0); 
			//   gse.SetRelevance(goalActGo, walkDrive, 1); 
			//   gse.SetRelevance(goalActWall, walkDrive, 0); 
			gsm.Commit(gse);		
			CurrentAgent.Commit(gsm);			

			CurrentAgent.MS.Parameters.CURRENT_GOAL_ACTIVATION_OPTION = MotivationalSubsystem.CurrentGoalActivationOptions.FULL;			
		}
		#endregion MS



		#region Deficit Change
		// BASEADO NO EXEMPLO HelloWorldFull - DETERMINAÇÃO DO DEFICIT_CHANGE_RATE PARA UTILIZAÇÃO NO SUBSISTEMA MOTIVACIONAL

		private double Goals_DeficitChange(ActivationCollection si, Drive target) {

			var cg = ((SensoryInformation)si).AffiliatedAgent.CurrentGoal;
			if (cg != null) 
			{
				if ((cg == GoalAvoidWall && target is AvoidingPhysicalDangerDrive) ||
				    (cg == GoalGoAhead && target is CuriosityDrive))
					target.Parameters.DEFICIT_CHANGE_RATE = .999;
				else
					target.Parameters.DEFICIT_CHANGE_RATE = 1.001;
			}
			return target.Deficit * target.Parameters.DEFICIT_CHANGE_RATE;
		}
		#endregion Deficit Change

      // MS-OFF  
	  */


		//################################################  PERCEPÇÃO DE INFORMAÇÕES DE ENTRADA:

		/// Make the agent perception. In other words, translate the information that came from sensors to a new type that the agent can understand
		/// <param name="sensorialInformation">The information that came from server</param>
		/// <returns>The perceived information</returns>
		/// 
		private SensoryInformation MakePerceptionFromSensorialInput(IList<Thing> sensorialInformation) {        
			// New sensory information
			SensoryInformation si = World.NewSensoryInformation (CurrentAgent);
			Creature creature = (Creature) sensorialInformation.Where(item => (item.CategoryId == Thing.CATEGORY_CREATURE)).First();


			// WALL AHEAD - AVOID DANGER:

		
			Boolean wallAhead = (sensorialInformation.Where (item => (item.CategoryId == Thing.CATEGORY_BRICK && item.DistanceToCreature <= DIST_MIN_WALL)).Any ()||creature.HasCollided||creature.IsOccluded);
			 
			if (wallAhead) {
				si.Add (InputWallAhead, CurrentAgent.Parameters.MAX_ACTIVATION);
				si.Add (InputJewelAhead, CurrentAgent.Parameters.MIN_ACTIVATION);
				si.Add (InputFoodAhead, CurrentAgent.Parameters.MIN_ACTIVATION);
				si.Add (InputJewelFarAway, CurrentAgent.Parameters.MIN_ACTIVATION);
				si.Add (InputFoodFarAway, CurrentAgent.Parameters.MIN_ACTIVATION);

				// UTILIZANDO O DRIVE DE MOTIVAÇÃO:
				// MS-OFF  si[typeof(AvoidingPhysicalDangerDrive), AvoidingPhysicalDangerDrive.MetaInfoReservations.STIMULUS] = 1;
				// MS-OFF  si[typeof(CuriosityDrive), CuriosityDrive.MetaInfoReservations.STIMULUS] = 0;

				return si; 


				// CASO NÃO HAJA URGÊNCIA OU PERIGO, ESTABELEÇA VALORES DE PRIORIDADES PARA AS AÇÕES:
			} else {
				si.Add (InputWallAhead, CurrentAgent.Parameters.MIN_ACTIVATION);

				// UTILIZANDO O DRIVE DE MOTIVAÇÃO - ERROR - NULL POINTER EXCEPTION!!!! :
				// MS-OFF  si[typeof(AvoidingPhysicalDangerDrive), AvoidingPhysicalDangerDrive.MetaInfoReservations.STIMULUS] = 0;
				// MS-OFF  si[typeof(CuriosityDrive), CuriosityDrive.MetaInfoReservations.STIMULUS] = 1;


				// JEWEL AHEAD:
				Boolean jewelAhead = sensorialInformation.Where (item => (item.CategoryId == Thing.CATEGORY_JEWEL && item.DistanceToCreature <= 30)).Any ();
				double jewelAheadActivationValue = jewelAhead ? CurrentAgent.Parameters.MAX_ACTIVATION : CurrentAgent.Parameters.MIN_ACTIVATION;
				si.Add (InputJewelAhead, jewelAheadActivationValue);

				// FOOD AHEAD:
				Boolean foodAhead = sensorialInformation.Where (item => ((item.CategoryId == Thing.CATEGORY_FOOD || item.CategoryId == Thing.CATEGORY_NPFOOD || item.CategoryId == Thing.CATEGORY_PFOOD) && item.DistanceToCreature <= 30)).Any ();
				double foodAheadActivationValue = foodAhead ? CurrentAgent.Parameters.MAX_ACTIVATION : CurrentAgent.Parameters.MIN_ACTIVATION;
				si.Add (InputFoodAhead, foodAheadActivationValue);

				// FOOD FAR AWAY
				Boolean foodFarAway = sensorialInformation.Where (item => ((item.CategoryId == Thing.CATEGORY_FOOD || item.CategoryId == Thing.CATEGORY_NPFOOD || item.CategoryId == Thing.CATEGORY_PFOOD) && item.DistanceToCreature > 30)).Any ();
				double foodFarAwayActivationValue = (foodFarAway && !foodAhead && !jewelAhead) ? CurrentAgent.Parameters.MAX_ACTIVATION : CurrentAgent.Parameters.MIN_ACTIVATION;
				si.Add (InputFoodFarAway, foodFarAwayActivationValue);

				//  JEWEL FAR AWAY
				Boolean jewelFarAway = sensorialInformation.Where (item => (item.CategoryId == Thing.CATEGORY_JEWEL && creature.inLeaflet(item.Material.Color) && item.DistanceToCreature > 30)).Any ();
				double jewelFarAwayActivationValue = (jewelFarAway && !foodAhead && !jewelAhead) ? CurrentAgent.Parameters.MAX_ACTIVATION : CurrentAgent.Parameters.MIN_ACTIVATION;
				si.Add (InputJewelFarAway, jewelFarAwayActivationValue);

				 //Console.WriteLine("creature.IsOccluded creature.IsOccluded creature.IsOccluded");
				 //Console.WriteLine("creature.HasCollided  creature.HasCollided  creature.HasCollided");



				return si;

			} 

			/*
			//#############################   LOGICA 2    #############################

			// UTILIZANDO O DRIVE DE MOTIVAÇÃO - ERROR - NULL POINTER EXCEPTION!!!! :
			// MS-OFF  si[typeof(AvoidingPhysicalDangerDrive), AvoidingPhysicalDangerDrive.MetaInfoReservations.STIMULUS] = 0;
			// MS-OFF  si[typeof(CuriosityDrive), CuriosityDrive.MetaInfoReservations.STIMULUS] = 1;

			IEnumerable<Thing> sorted = sensorialInformation.Where(item => (item.CategoryId != Thing.CATEGORY_CREATURE && item.CategoryId != Thing.CATEGORY_BRICK));
			if (sorted.Any ()) {
				// JEWEL AHEAD:
				Boolean jewelAhead = sensorialInformation.Where (item => (item.CategoryId == Thing.CATEGORY_JEWEL && item.DistanceToCreature <= 30)).Any ();
				double jewelAheadActivationValue = jewelAhead ? CurrentAgent.Parameters.MAX_ACTIVATION : CurrentAgent.Parameters.MIN_ACTIVATION;
				si.Add (InputJewelAhead, jewelAheadActivationValue);

				// FOOD AHEAD:
				Boolean foodAhead = sensorialInformation.Where (item => ((item.CategoryId == Thing.CATEGORY_FOOD || item.CategoryId == Thing.CATEGORY_NPFOOD || item.CategoryId == Thing.CATEGORY_PFOOD) && item.DistanceToCreature <= 30)).Any ();
				double foodAheadActivationValue = foodAhead ? CurrentAgent.Parameters.MAX_ACTIVATION : CurrentAgent.Parameters.MIN_ACTIVATION;
				si.Add (InputFoodAhead, foodAheadActivationValue);

				// FOOD FAR AWAY
				Boolean foodFarAway = sensorialInformation.Where (item => ((item.CategoryId == Thing.CATEGORY_FOOD || item.CategoryId == Thing.CATEGORY_NPFOOD || item.CategoryId == Thing.CATEGORY_PFOOD) && item.DistanceToCreature > 30)).Any ();
				double foodFarAwayActivationValue = (foodFarAway && !foodAhead && !jewelAhead) ? CurrentAgent.Parameters.MAX_ACTIVATION : CurrentAgent.Parameters.MIN_ACTIVATION;
				si.Add (InputFoodFarAway, foodFarAwayActivationValue);

				//  JEWEL FAR AWAY
				Boolean jewelFarAway = sensorialInformation.Where (item => (item.CategoryId == Thing.CATEGORY_JEWEL && creature.inLeaflet (item.Material.Color) && item.DistanceToCreature > 30)).Any ();
				double jewelFarAwayActivationValue = (jewelFarAway && !foodAhead && !jewelAhead) ? CurrentAgent.Parameters.MAX_ACTIVATION : CurrentAgent.Parameters.MIN_ACTIVATION;
				si.Add (InputJewelFarAway, jewelFarAwayActivationValue);

				Boolean wallAhead = sensorialInformation.Where (item => (item.CategoryId == Thing.CATEGORY_BRICK && item.DistanceToCreature <= DIST_MIN_WALL)).Any ();

				if (!jewelAhead && !foodAhead && !foodFarAway && !jewelFarAway && wallAhead) {
					si.Add (InputWallAhead, CurrentAgent.Parameters.MAX_ACTIVATION);
				} else {
					si.Add (InputWallAhead, CurrentAgent.Parameters.MIN_ACTIVATION);
				}
			}
			else {
				si.Add (InputJewelAhead, CurrentAgent.Parameters.MIN_ACTIVATION);
				si.Add (InputFoodAhead, CurrentAgent.Parameters.MIN_ACTIVATION);
				si.Add (InputFoodFarAway, CurrentAgent.Parameters.MIN_ACTIVATION);
				si.Add (InputJewelFarAway, CurrentAgent.Parameters.MIN_ACTIVATION);
				si.Add (InputWallAhead, CurrentAgent.Parameters.MAX_ACTIVATION);
			}

			return si;
                            */
		}

		#endregion




		#region Delegate Methods
		#region Fixed Rules



		// ############################################  REGRAS FIXAS: #####################################################

		//###############################  AvoidColissionWall
		private double FixedRuleDelegateToAvoidColissionWall(ActivationCollection currentInput, Rule target) {        
			// See partial match threshold to verify what are the rules available for action selection

			return ((currentInput.Contains(InputWallAhead, CurrentAgent.Parameters.MAX_ACTIVATION))) ? 1.0 : 0.0;
		}


		//###############################  GoAhead  (MUITO GENÉRICO)
		private double FixedRuleDelegateToGoAhead(ActivationCollection currentInput, Rule target) {        
			// Here we will make the logic to go ahead
			                                                      //return ((currentInput.Contains(InputWallAhead, CurrentAgent.Parameters.MIN_ACTIVATION))) ? 0.7 : 0.0;

			if ((currentInput.Contains (InputWallAhead, CurrentAgent.Parameters.MAX_ACTIVATION) && 
			    currentInput.Contains (InputJewelAhead, CurrentAgent.Parameters.MIN_ACTIVATION) && 
			    currentInput.Contains (InputFoodAhead, CurrentAgent.Parameters.MIN_ACTIVATION) &&
			    currentInput.Contains (InputJewelFarAway, CurrentAgent.Parameters.MIN_ACTIVATION) &&
			     currentInput.Contains (InputFoodFarAway, CurrentAgent.Parameters.MIN_ACTIVATION))||(rotateStuck >= 30)) {
				getJewelStuck = 0;
				rotateStuck = 0;
				return 0.8;
			} 
			else {
				return 0.0;
			}
		}


		//############################### Rotate
		private double FixedRuleDelegateRotate (ActivationCollection currentInput, Rule target)
		{
			// Here we will make the logic to rotate
			if ((currentInput.Contains (InputWallAhead, CurrentAgent.Parameters.MIN_ACTIVATION) && 
			    currentInput.Contains (InputJewelAhead, CurrentAgent.Parameters.MIN_ACTIVATION) && 
			    currentInput.Contains (InputFoodAhead, CurrentAgent.Parameters.MIN_ACTIVATION) &&
			    currentInput.Contains (InputJewelFarAway, CurrentAgent.Parameters.MIN_ACTIVATION) &&
			     currentInput.Contains (InputFoodFarAway, CurrentAgent.Parameters.MIN_ACTIVATION))||(getJewelStuck >= 5)) {
				getJewelStuck = 0;
				rotateStuck = rotateStuck + 1;
				return 1.0;
			} 
			else {
				rotateStuck = 0;
				return 0.0;
			}

		}


		//###############################  GetJewel
		private double FixedRuleDelegateToGetJewel(ActivationCollection currentInput, Rule target)
		{

			// Here we will make the logic to go ahead
			if (currentInput.Contains (InputWallAhead, CurrentAgent.Parameters.MIN_ACTIVATION) &&                                                                                 //currentInput.Contains (InputWallAhead, CurrentAgent.Parameters.MIN_ACTIVATION) && 
			    currentInput.Contains (InputJewelAhead, CurrentAgent.Parameters.MAX_ACTIVATION)){
				getJewelStuck = getJewelStuck + 1;
				rotateStuck = 0;
				return 1.0;
			} 
			else {
				getJewelStuck = 0;
				return 0.0;
			}
		}



		//###############################  EatFood
		private double FixedRuleDelegateToEatFood (ActivationCollection currentInput, Rule target)
		{
			// Here we will make the logic to go ahead
			if (                                                                                //currentInput.Contains (InputWallAhead, CurrentAgent.Parameters.MIN_ACTIVATION) && 
			    currentInput.Contains (InputJewelAhead, CurrentAgent.Parameters.MIN_ACTIVATION) && 
			    currentInput.Contains (InputFoodAhead, CurrentAgent.Parameters.MAX_ACTIVATION) ){
				getJewelStuck = 0;
				rotateStuck = 0;
				return 1.0;
			} else {
				return 0.0;
			}        
		}



		//###############################  MoveJewel
		private double FixedRuleDelegateToMoveJewel (ActivationCollection currentInput, Rule target)
		{

			if (currentInput.Contains (InputWallAhead, CurrentAgent.Parameters.MIN_ACTIVATION) && 
			    currentInput.Contains (InputJewelAhead, CurrentAgent.Parameters.MIN_ACTIVATION) && 
			    currentInput.Contains (InputFoodAhead, CurrentAgent.Parameters.MIN_ACTIVATION) &&
			    currentInput.Contains (InputJewelFarAway, CurrentAgent.Parameters.MAX_ACTIVATION)) {
				getJewelStuck = 0;
				rotateStuck = 0;
				return 1.0;
			} else {
				return 0.0;
			}
		}




		//###############################  MoveFood
		private double FixedRuleDelegateToMoveFood (ActivationCollection currentInput, Rule target)
		{
			// Here we will make the logic to go ahead
			// Here we will make the logic to go ahead
			if (currentInput.Contains (InputWallAhead, CurrentAgent.Parameters.MIN_ACTIVATION) && 
			    currentInput.Contains (InputJewelAhead, CurrentAgent.Parameters.MIN_ACTIVATION) && 
			    currentInput.Contains (InputFoodAhead, CurrentAgent.Parameters.MIN_ACTIVATION) &&
			    currentInput.Contains (InputJewelFarAway, CurrentAgent.Parameters.MIN_ACTIVATION) &&
			    currentInput.Contains (InputFoodFarAway, CurrentAgent.Parameters.MAX_ACTIVATION)) {
				getJewelStuck = 0;
				rotateStuck = 0;
				return 1.0;
			} else {
				return 0.0;
			}
		}



		#endregion Fixed Rules




		#region Run Thread Method
		//############################### CLICLO COGNITIVO - iniciado na MainClass.cs com agent.Run()

		private void RunThread(object obj) {  

			Console.WriteLine("Starting Cognitive Cycle ... press CTRL-C to finish !\n");



			// Cognitive Cycle starts here getting sensorial information
			while (CurrentCognitiveCycle != MaxNumberOfCognitiveCycles) {   

				                                                                                      //Console.Out.WriteLine ("while (CurrentCognitiveCycle != MaxNumberOfCognitiveCycles) - CurrentCognitiveCycle:" +CurrentCognitiveCycle);  //DEBUG

				// Get current sensorial information                    
				// recebe a resposta do método delegado na MainClass:
				IList<Thing> sensorialInformation = OnNewVisualSensorialInformation();   

				// Make the perception
				SensoryInformation si = MakePerceptionFromSensorialInput(sensorialInformation);

				//Perceive the sensory information
				CurrentAgent.Perceive(si);

				//Choose an action
				ExternalActionChunk chosen = CurrentAgent.GetChosenExternalAction(si);
				                                                                                     //Console.Out.WriteLine ("ExternalActionChunk chosen = CurrentAgent.GetChosenExternalAction(si) - chosen" +chosen);   //DEBUG
				// Get the selected action
				String actionLabel = chosen.LabelAsIComparable.ToString();
				ClarionAgentActionType actionType = (ClarionAgentActionType)Enum.Parse(typeof(ClarionAgentActionType), actionLabel, true);

				Console.WriteLine("actionType actionTypeactionTypeactionTypeactionTypeactionTypeactionType :" + actionType);
				                                                                                     //Console.Out.WriteLine ("if (OnNewExternalActionSelected != null) ENTRADA");   //DEBUG	
				//#################### Call the output event handler  - CRIAR UMA CLASSE AVALIADORA MS & ACS!!!!
				if (OnNewExternalActionSelected != null) { 

					                                                                                 //Console.Out.WriteLine ("if (OnNewExternalActionSelected != null) SAIDA");   //DEBUG
					//#################### EMERGÊNCIA: EVITAR BATER NA PAREDE - SISTEMA REATIVO - MOTIVATION SYSTEM.

				/*	// MS-OFF 

					if (CurrentAgent.CurrentGoal == GoalAvoidWall) { 
						Console.Out.WriteLine ("(CurrentAgent.CurrentGoal == GoalAvoidWall) ");   //DEBUG

						if (si[InputWallAhead] == CurrentAgent.Parameters.MAX_ACTIVATION && actionType == ClarionAgentActionType.ROTATE) {
							//Console.WriteLine("Avoiding wall ............................ \n");
							Console.WriteLine("Wall ahead .. avoiding physical danger drive: ... " + si + "Action type: " + actionType + " ...... \n");

							CurrentAgent.ReceiveFeedback(si, 1.0); // TREINANDO O SYSTEMA MS BOTTOM.  
							//OnNewExternalActionSelected(actionType); 
						} 

						else {  
							if (si [InputWallAhead] == CurrentAgent.Parameters.MIN_ACTIVATION) {
								CurrentAgent.ReceiveFeedback (si, 0.0);  // TREINANDO O SYSTEMA MS BOTTOM.
							}
							//OnNewExternalActionSelected(actionType); 
						}	


					}


					//#################### NENHUMA EMERGÊNCIA - SYSTEMA REATIVO OFF (TRANQUILO) - COMANDO ACS.

					else { 		
						if (CurrentAgent.CurrentGoal == GoalGoAhead)  { 
							Console.Out.WriteLine ("(CurrentAgent.CurrentGoal == GoalGoAhead) ");   //DEBUG

							if (si[InputWallFarAway] == CurrentAgent.Parameters.MAX_ACTIVATION && !(actionType == ClarionAgentActionType.ROTATE)) {

								Console.WriteLine("Wall far away .. goal: ser curioso, andar, comer, pegar jóias ... " + si + "Action type: " + actionType + " ...... \n");
								CurrentAgent.ReceiveFeedback(si, 1.0);   
								//OnNewExternalActionSelected(actionType); 
							} 
							else {
								//OnNewExternalActionSelected(actionType);  
								Console.WriteLine("Wall far away .. feedback 0.0 .... " + si + " ....... \n");
								CurrentAgent.ReceiveFeedback(si, 0.0);
								//OnNewExternalActionSelected(actionType); 
							}
						}		
					}  */ // MS-OFF  

					OnNewExternalActionSelected(actionType);
				}
				// Increment the number of cognitive cycles
				CurrentCognitiveCycle++;

				//Wait to the agent accomplish his job
				if (TimeBetweenCognitiveCycles > 0) {                
					Thread.Sleep(TimeBetweenCognitiveCycles);
				}
			}
		}
		//############################### FIM - CLICLO COGNITIVO

		#endregion Run Thread Method
		#endregion Delegate Methods
	}
}
