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 System.Drawing;
using Clarion.Plugins;

namespace RER
{
	public class Agente
	{
		#region Private Members
		private Clarion.Framework.Agent Andy;
		private const int numElementos = 4;	
		
		//static string agentFile = "Agent.xml";
		
		// Training stuffs
		//private const int NumberTrials = 10000;
				
		public int CorrectCounter = 0;
		
		enum Elements {Empty, Dirt, Fire, Wall};
		
		private DimensionValuePair[,] dvMatrix;
		
		private ExternalActionChunk[] moveTo;
		private ExternalActionChunk[] pourWater;
		private ExternalActionChunk[] clean;
		
		//private Random r;
				
		private ExternalActionChunk chosen;
		private SensoryInformation si;
			
		private int posi = 0;
		private	int val = 0;
			
		private	string aux = "";
		private	string action = "";				
		
		#endregion
		
		#region Private Methods
		private void SetupAgent()
		{
			
			SimplifiedQBPNetwork net = AgentInitializer.InitializeImplicitDecisionNetwork(Andy, SimplifiedQBPNetwork.Factory);
		
			for(int i = 0; i < 8; i++)
			{
				for(int j=0; j < numElementos; j++)
				{
					net.Input.Add(dvMatrix[j,i]);
            		net.Input.Add(dvMatrix[j,i]);					
				}				
			}
			
			for(int i = 0; i < 8; i++)			
				net.Output.Add(moveTo[i]);
			
			for(int i = 0; i < 8; i++)			
				net.Output.Add(pourWater[i]);
			
			for(int i = 0; i < 8; i++)			
				net.Output.Add(clean[i]);
			
			net.Parameters.LEARNING_RATE = 2;
            net.Parameters.MOMENTUM = .01;
            	
			Andy.Commit(net);
			
			//RefineableActionRule.GlobalParameters.GENERALIZATION_THRESHOLD_1 = -.01;
			//RefineableActionRule.GlobalParameters.SPECIALIZATION_THRESHOLD_1 = -.4;
			//Andy.ACS.Parameters.PERFORM_RER_REFINEMENT = false;
			
			Andy.ACS.Parameters.PERFORM_RER_REFINEMENT = false;
            Andy.ACS.Parameters.SELECTION_TEMPERATURE = .01;
			
			//Running the training
            Console.WriteLine("Running the training");
            //Console.SetOut(sw);
			
			posi = 0;
			val = 0;			
			aux = "";
			action = "";			
			CorrectCounter = 0;			
			
		}
		
		private SensoryInformation PreProcessing(int[] sensor_input)
		{
			// New sensory information
			SensoryInformation si = World.NewSensoryInformation(Andy);
			
			for(int i = 0; i < 8; i++)
			{
				switch (sensor_input[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);					
					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);					
					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);
					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);					
					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);
					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);					
					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);					
					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);					
					break;
				default:
					break;
				}
				
			}
			
			return si;
		}
		
		public int NumberTrials;
		#endregion
		
		#region Public Methods
		public void ResetAgent()
		{
			//Kill the agent to end the task            
            Andy.Die();
            Console.WriteLine("\nAndy is Dead");
			
			Andy = World.NewAgent("Andy");	
			
			SetupAgent();
		}
		
		public void EndAgent()
		{
			//Kill the agent to end the task            
            Andy.Die();
            Console.WriteLine("\nAndy is Dead");			
		}
		
		public string Train(int[] sensor_input)
		{
			string output = "";
			
			val = 0;
			posi = 1;
			
			for(int i = 0 ; i < 8 ; i++)
			{
				if(sensor_input[i] != 0)
				{
					val = sensor_input[i];
					posi= i;
				}
			}
									
			si = PreProcessing(sensor_input);
			
			//Perceive the sensory information
			Andy.Perceive(si);
			
			//Choose an action
            chosen = Andy.GetChosenExternalAction(si);				
			action = chosen.LabelAsIComparable.ToString();
									
			//Deliver appropriate feedback to the agent
			switch (val) 
			{
				
			case (int)Elements.Empty:
				
				aux = "moveTo_" + posi.ToString();
				
				if(action.Contains("moveTo"))
				{
					output = "\nIn: " + aux + " Out: " + action + "		Andy was correct !!";
					CorrectCounter++;
					Andy.ReceiveFeedback(si, 1.0);					
				}
				else
				{
					output = "\nIn: " + aux + " Out: " + action + "		Andy was incorrect !!";					
					Andy.ReceiveFeedback(si, 0.0);
				}
				break;
				
			case (int)Elements.Dirt:
				
				aux = "clean_"+posi.ToString();
				
				if(action.Equals(aux))
				{
					output = "\nIn: " + aux + " Out: " + action + "		Andy was correct !!";
					CorrectCounter++;
					Andy.ReceiveFeedback(si, 1.0);
				}
				else
				{
					output = "\nIn: " + aux + " Out: " + action + "		Andy was incorrect !!";
					Andy.ReceiveFeedback(si, 0.0);
				}
				
				break;
				
			case (int)Elements.Fire:
				
				aux = "pourWater_"+posi.ToString();
				
				if(action.Equals(aux))
				{
					output = "\nIn: " + aux + " Out: " + action + "		Andy was correct !!";
					CorrectCounter++;
					Andy.ReceiveFeedback(si, 1.0);
				}
				else
				{
					output = "\nIn: " + aux + " Out: " + action + "		Andy was incorrect !!";
					Andy.ReceiveFeedback(si, 0.0);
				}
				break;
				
			case (int)Elements.Wall:
								
				aux = "Wall_" + posi.ToString();
				
				if(action.Contains("moveTo"))
				{					
					if((posi == 0) || (posi == 1) || (posi == 2))
					{
						if(action.Equals("moveTo_0") || action.Equals("moveTo_1") || action.Equals("moveTo_2"))
						{
							output = "\nIn: " + aux + " Out: " + action + "		Andy was incorrect !!";
							Andy.ReceiveFeedback(si, 0.0);
						}
						else
						{							
							output = "\nIn: " + aux + " Out: " + action + "		Andy was correct !!";
							CorrectCounter++;
							Andy.ReceiveFeedback(si, 1.0);							
						}
					}
					
					if((posi == 3) || (posi == 4))
					{
						
						if(action.Equals("moveTo_2") || action.Equals("moveTo_3") || action.Equals("moveTo_4"))
						{
							output = "\nIn: " + aux + " Out: " + action + "		Andy was incorrect !!";
							Andy.ReceiveFeedback(si, 0.0);
						}
						else
						{
							output = "\nIn: " + aux + " Out: " + action + "		Andy was correct !!";
							CorrectCounter++;
							Andy.ReceiveFeedback(si, 1.0);							
						}
					}
					
					if((posi == 5) || (posi == 6))
					{
						
						if(action.Equals("moveTo_4") || action.Equals("moveTo_5") || action.Equals("moveTo_6"))
						{
							output = "\nIn: " + aux + " Out: " + action + "		Andy was incorrect !!";	
							Andy.ReceiveFeedback(si, 0.0);
						}
						else
						{
							output = "\nIn: " + aux + " Out: " + action + "		Andy was correct !!";
							CorrectCounter++;
							Andy.ReceiveFeedback(si, 1.0);							
						}						
					}
					
					if(posi == 7)
					{
						
						if(action.Equals("moveTo_0") || action.Equals("moveTo_7") || action.Equals("moveTo_6") )
						{
							output = "\nIn: " + aux + " Out: " + action + "		Andy was incorrect !!";
							Andy.ReceiveFeedback(si, 0.0);
						}
						else
						{
							output = "\nIn: " + aux + " Out: " + action + "		Andy was correct !!";
							CorrectCounter++;
							Andy.ReceiveFeedback(si, 1.0);
						}					
					}					
				}
				else
				{
					output = "\nIn: " + aux + " Out: " + action + "		Andy was incorrect !!";
					Andy.ReceiveFeedback(si, 0.0);
				}
				
				break;				
			}	
			
			return output;
		}
		
		public string GetTrainResults()
		{
			string report = "";
			
			report = "======================";
			report += "\nReporting Results:";
			report += "\n======================";
			//report += "\nAndy got " + CorrectCounter + " correct out of " + NumberTrials + " trials (" + (int)Math.Round(((double)CorrectCounter / (double)NumberTrials) * 100) + "%)";
			report += "\nAt the end of the task, Andy had learned the following rules:";
			
			foreach (var i in Andy.GetInternals(Agent.InternalContainers.ACTION_RULES))
				report += "\n" + i;
                
			
			return report;
			
			//Report Results

            /*Console.WriteLine("Reporting Results for the Simple Hello World Task");
            Console.WriteLine("John got " + CorrectCounter + " correct out of " + NumberTrials + " trials (" +
                (int)Math.Round(((double)CorrectCounter / (double)NumberTrials) * 100) + "%)");

            Console.WriteLine("At the end of the task, John had learned the following rules:");
            foreach (var i in Andy.GetInternals(Agent.InternalContainers.ACTION_RULES))
                Console.WriteLine(i);

            sw.Close();
            Console.SetOut(orig);
            Console.CursorLeft = 0;
            Console.WriteLine("100% Complete..");*/
			
		}
		
		public string NewCognitiveCicle(int[] sensor_info)
		{
			// Make the perception
            si = PreProcessing(sensor_info);
			
			Andy.Perceive(si);
			
			//Choose an action            
			chosen = Andy.GetChosenExternalAction(si);
			// Get the selected action
            return chosen.LabelAsIComparable.ToString();
		}
		
		public void SaveAgent()
		{
			//Console.WriteLine("Serializing Andy");
            //SerializationPlugin.Serialize(Andy, agentFile);
			//SerializationPlugin.SerializeWorld(agentFile);
		}
		
		public bool LoadAgent()
		{
			/*if (File.Exists(agentFile))
            {
                Console.WriteLine("Deserializing Andy");
                Andy = SerializationPlugin.DeserializeWorldObject<Agent>(agentFile);
				return true;
            }
			else
				return false;*/
			
			return false;
		}
		
		#endregion
		
		/// <summary>
		/// Initializes a new instance of the <see cref="RER.Agente"/> class.
		/// </summary>
		public Agente ()
		{			
			dvMatrix = new DimensionValuePair[numElementos,8];
			moveTo = new ExternalActionChunk[8];
			pourWater = new ExternalActionChunk[8];
			clean = new ExternalActionChunk[8];
			
			//r = new Random(DateTime.Now.Millisecond);	
			
			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);
				}				
			}
			
			aux = "";
			
			for(int i = 0; i < 8; i++)
			{
				aux = "moveTo_"+i.ToString();
				moveTo[i] = World.NewExternalActionChunk(aux);
				
				aux = "pourWater_"+i.ToString();
				pourWater[i] = World.NewExternalActionChunk(aux);
				
				aux = "clean_"+i.ToString();
				clean[i] = World.NewExternalActionChunk(aux);
			}			
			
			Andy = World.NewAgent("Andy");				
			SetupAgent();
		}		
		
	}
}


