using System;
using Clarion;
using Clarion.Framework;
using Clarion.Framework.Core;
using Clarion.Framework.Templates;
using System.Collections.Generic;
using System.Drawing;

namespace FixedRulesGUI
{
	public class Agente
	{
		#region Private Members
		private Clarion.Framework.Agent Andy;
		private const int numElementos = 9;	
		
		enum Elements {Empty, Dirt, Fire, Wall, Window, Door, Family, Thief, Robot };
		
		private DimensionValuePair[,] dvMatrix;
		
		private ExternalActionChunk moveTo;
		private ExternalActionChunk pourWater;
		private ExternalActionChunk clean;
		private ExternalActionChunk goAway;		
		private ExternalActionChunk turnOnAlarm;
		//private ExternalActionChunk alarmOff;
		#endregion
		
		#region Public Members
		public int desPoss;
		public List<int> possibleDest;
		public int pastAgentPos;
		#endregion
		
		#region Private Methods
		/// <summary>
		/// Setups the Action Centered Subsystem.
		/// </summary>
		private void SetupACS()
		{			
			// Create moveTo
			SupportCalculator moveToSupportCalculator = FixedRuleDelegateToMoveTo;
			FixedRule ruleMoveTo = AgentInitializer.InitializeActionRule(Andy, FixedRule.Factory, moveTo, moveToSupportCalculator);
			
			// Commit this rule to Agent (in the ACS)
            Andy.Commit(ruleMoveTo);
			
			// Create pourWather
			SupportCalculator pourWaterSupportCalculator = FixedRuleDelegateToPourWater;
			FixedRule rulePourWater = AgentInitializer.InitializeActionRule(Andy, FixedRule.Factory, pourWater, pourWaterSupportCalculator);
			
			// Commit this rule to Agent (in the ACS)
            Andy.Commit(rulePourWater);
			
			// Create clean
			SupportCalculator cleanSupportCalculator = FixedRuleDelegateToClean;
			FixedRule ruleClean = AgentInitializer.InitializeActionRule(Andy, FixedRule.Factory, clean, cleanSupportCalculator);
			
			// Commit this rule to Agent (in the ACS)
            Andy.Commit(ruleClean);
			
			// Create goAway
			SupportCalculator goAwaySupportCalculator = FixedRuleDelegateToGoAway;
			FixedRule ruleGoAway = AgentInitializer.InitializeActionRule(Andy, FixedRule.Factory, goAway, goAwaySupportCalculator);
			
			// Commit this rule to Agent (in the ACS)
            Andy.Commit(ruleGoAway);
			
			// Create turnOnAlarm
			SupportCalculator turnOnAlarmSupportCalculator = FixedRuleDelegateToTurnOnAlarm;
			FixedRule ruleTurnOnAlarm = AgentInitializer.InitializeActionRule(Andy, FixedRule.Factory, turnOnAlarm, turnOnAlarmSupportCalculator);
			
			// Commit this rule to Agent (in the ACS)
            Andy.Commit(ruleTurnOnAlarm);
			
			/// ACS PARAMETERS ///
			
			// Disable Rule Refinement
            Andy.ACS.Parameters.PERFORM_RER_REFINEMENT = false;

            // The selectio type will be probabilistic
            Andy.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.
            Andy.ACS.Parameters.LEVEL_SELECTION_OPTION = ActionCenteredSubsystem.LevelSelectionOptions.FIXED;

            // Define Probabilistic values
            Andy.ACS.Parameters.FIXED_FR_LEVEL_SELECTION_MEASURE = 1;
            Andy.ACS.Parameters.FIXED_IRL_LEVEL_SELECTION_MEASURE = 0;
            Andy.ACS.Parameters.FIXED_BL_LEVEL_SELECTION_MEASURE = 0;
            Andy.ACS.Parameters.FIXED_RER_LEVEL_SELECTION_MEASURE = 0;			
		}
		
		/// <summary>
		/// Gets the sensor information.
		/// </summary>
		/// <returns>
		/// The sensor information.
		/// </returns>
		private SensoryInformation GetSensorInformation(int[] sensor_info)
		{
			// New sensory information
			SensoryInformation si = World.NewSensoryInformation(Andy);
			
			for(int i = 0; i < 8; i++)
			{
				switch (sensor_info[i]) 
				{
				case 0:
					si.Add(dvMatrix[0,i],Andy.Parameters.MAX_ACTIVATION);
					si.Add(dvMatrix[1,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[2,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[3,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[4,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[5,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[6,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[7,i],Andy.Parameters.MIN_ACTIVATION);
					break;
				case 1:
					si.Add(dvMatrix[0,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[1,i],Andy.Parameters.MAX_ACTIVATION);
					si.Add(dvMatrix[2,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[3,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[4,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[5,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[6,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[7,i],Andy.Parameters.MIN_ACTIVATION);
					break;
				case 2:
					si.Add(dvMatrix[0,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[1,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[2,i],Andy.Parameters.MAX_ACTIVATION);
					si.Add(dvMatrix[3,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[4,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[5,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[6,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[7,i],Andy.Parameters.MIN_ACTIVATION);
					break;
				case 3:
					si.Add(dvMatrix[0,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[1,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[2,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[3,i],Andy.Parameters.MAX_ACTIVATION);
					si.Add(dvMatrix[4,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[5,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[6,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[7,i],Andy.Parameters.MIN_ACTIVATION);					
					break;
				case 4:
					si.Add(dvMatrix[0,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[1,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[2,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[3,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[4,i],Andy.Parameters.MAX_ACTIVATION);
					si.Add(dvMatrix[5,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[6,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[7,i],Andy.Parameters.MIN_ACTIVATION);
					break;
				case 5:
					si.Add(dvMatrix[0,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[1,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[2,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[3,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[4,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[5,i],Andy.Parameters.MAX_ACTIVATION);
					si.Add(dvMatrix[6,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[7,i],Andy.Parameters.MIN_ACTIVATION);					
					break;
				case 6:
					si.Add(dvMatrix[0,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[1,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[2,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[3,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[4,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[5,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[6,i],Andy.Parameters.MAX_ACTIVATION);
					si.Add(dvMatrix[7,i],Andy.Parameters.MIN_ACTIVATION);					
					break;
				case 7:
					si.Add(dvMatrix[0,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[1,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[2,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[3,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[4,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[5,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[6,i],Andy.Parameters.MIN_ACTIVATION);
					si.Add(dvMatrix[7,i],Andy.Parameters.MAX_ACTIVATION);					
					break;
				default:
					break;
				}
				
			}
			
			return si;
		}
		#endregion
		
		#region Fixed Rules Delegates

        private double FixedRuleDelegateToMoveTo(ActivationCollection currentInput, Rule target)
        {
			// If exist Dirt or Fire or Family or Thief then don't use the MoveTo action			
            for(int i = 0; i < 8; i++)
			{
				if(currentInput.Contains(dvMatrix[(int)Elements.Dirt,i], Andy.Parameters.MAX_ACTIVATION) ||
				   currentInput.Contains(dvMatrix[(int)Elements.Fire,i], Andy.Parameters.MAX_ACTIVATION) ||
				   currentInput.Contains(dvMatrix[(int)Elements.Family,i], Andy.Parameters.MAX_ACTIVATION) ||
				   currentInput.Contains(dvMatrix[(int)Elements.Thief,i], Andy.Parameters.MAX_ACTIVATION))
					return 0.0;
			}
			
			// If there is no wall in the current cell, then add that cell into the possible destinations
			for(int i = 0; i < 8; i++)
			{
				if(currentInput.Contains(dvMatrix[(int)Elements.Empty,i], Andy.Parameters.MAX_ACTIVATION) && (i != pastAgentPos))
				   possibleDest.Add(i);				   
			}
			return 1.0;			
        }
		
		private double FixedRuleDelegateToPourWater(ActivationCollection currentInput, Rule target)
        {
			// If there is a cell with fire then pourWater
			
            for(int i = 0; i < 8; i++)
			{
				if(currentInput.Contains(dvMatrix[(int)Elements.Fire,i], Andy.Parameters.MAX_ACTIVATION))
				{
					desPoss = i;
					return 1.0;
				}					
			}			
			
			return 0.0;
        }
		
		private double FixedRuleDelegateToClean(ActivationCollection currentInput, Rule target)
        {
			// If there is no cell with fire and any with thief or family, and there is a cell with dirt, clean it
			
            for(int i = 0; i < 8; i++)
			{
				if(currentInput.Contains(dvMatrix[(int)Elements.Fire,i], Andy.Parameters.MAX_ACTIVATION) ||
				   currentInput.Contains(dvMatrix[(int)Elements.Thief,i], Andy.Parameters.MAX_ACTIVATION) ||
				   currentInput.Contains(dvMatrix[(int)Elements.Family,i], Andy.Parameters.MAX_ACTIVATION) )
					return 0.0;
			}
			
			for(int i = 0; i < 8; i++)
			{
				if(currentInput.Contains(dvMatrix[(int)Elements.Dirt,i], Andy.Parameters.MAX_ACTIVATION))
				{
					desPoss = i;
					return 1.0;
				}
			}
			
			return 0.0;			
		}
		
		private double FixedRuleDelegateToGoAway(ActivationCollection currentInput, Rule target)
        {
			// If there is no fire and there is a family, then goWay
			for(int i = 0; i < 8; i++)
			{
				if(currentInput.Contains(dvMatrix[(int)Elements.Fire,i], Andy.Parameters.MAX_ACTIVATION))								
					return 0.0;
			}
			
			for(int i = 0; i < 8; i++)
			{
				if(currentInput.Contains(dvMatrix[(int)Elements.Family,i], Andy.Parameters.MAX_ACTIVATION))	
				{
					desPoss = i;
					return 1.0;
				}					
			}
			return 0.0;
		}
		
		private double FixedRuleDelegateToTurnOnAlarm(ActivationCollection currentInput, Rule target)
        {
			// If there is no fire and there is a thief, then Turn On the Alarm
			for(int i = 0; i < 8; i++)
			{
				if(currentInput.Contains(dvMatrix[(int)Elements.Fire,i], Andy.Parameters.MAX_ACTIVATION))								
					return 0.0;
			}
			
			for(int i = 0; i < 8; i++)
			{
				if(currentInput.Contains(dvMatrix[(int)Elements.Thief,i], Andy.Parameters.MAX_ACTIVATION))	
				{
					desPoss = i;
					return 1.0;
				}					
			}
			return 0.0;
		}
		#endregion
		
		#region Public Methods
		/// <summary>
		/// Initializes a new instance of the <see cref="FixedRulesGUI.Agente"/> class.
		/// </summary>
		public Agente ()
		{
			dvMatrix = new DimensionValuePair[numElementos,8];
			possibleDest = new List<int>();
			desPoss = 0;
			pastAgentPos = 0;
						
			String aux = "";			
			
			for(int i = 0; i < 8; i++)
			{
				for(int j=0; j < numElementos; j++)
				{
					aux = "Cell"+i.ToString();
					dvMatrix[j,i] = World.NewDimensionValuePair(aux, j);
				}				
			}
			
			moveTo = World.NewExternalActionChunk("moveTo");
			pourWater = World.NewExternalActionChunk("pourWater");
			clean = World.NewExternalActionChunk("clean");			
			goAway = World.NewExternalActionChunk("goAway");			
			turnOnAlarm = World.NewExternalActionChunk("turnOnAlarm");			
			
			Andy = World.NewAgent("Andy");
			
			SetupACS();
		}
		
		/// <summary>
		/// Start a new cognitive cicle.
		/// </summary>
		/// <param name='sensor_info'>
		/// Sensor_info.
		/// </param>
		public string NewCognitiveCicle(int[] sensor_info)
		{
			possibleDest.Clear();
			desPoss = 0;			
			
			// Make the perception
            SensoryInformation si = GetSensorInformation(sensor_info);
			
			//Perceive the sensory information
			Andy.Perceive(si);
			
			//Choose an action            
			ExternalActionChunk chosen = Andy.GetChosenExternalAction(si);
			
			// Get the selected action            
			return chosen.LabelAsIComparable.ToString();
		}
		
		/// <summary>
		/// Ends the agent.
		/// </summary>
		public void EndAgent()
		{
			//Kill the agent to end the task            
            Andy.Die();
            Console.WriteLine("\nAndy is Dead");
		}
		
		#endregion
	}
}

