using System;
using System.Collections.Generic;
using Machinations.FromFlash.graph;
using Machinations.FromFlash.utils;

namespace Machinations.FromFlash.machinations.model
{
    /// <summary>
    /// can decode and apply if then rules of artifitial players
    /// </summary>
    public class APInstruction
    {
        private List<string> m_condition;
        private List<string> m_parameters;
        private string m_command;
        private int m_counter;
        private int m_actionsOfCommand;
        private ArtificialPlayer m_artifitialPlayer;

        public APInstruction(ArtificialPlayer artifitialPlayer, string instruction)
        {
            //instruction = instruction.toLowerCase();
            m_artifitialPlayer = artifitialPlayer;
            m_command = StringUtils.GetCommand(instruction);
            instruction = instruction.Substring(m_command.Length);
            m_command = m_command.Trim();
            //trace("comm", command, instruction);
            switch (m_command.ToLower())
            {
                case "if":
                    string con = StringUtils.GetCondition(instruction);
                    instruction = instruction.Substring(con.Length);
                    con = con.Trim();
                    //trace("con1:", con, instruction);
                    m_condition = MachinationsScriptExpression.ToPostFix(con);
                    m_command = StringUtils.GetCommand(instruction);
                    instruction = instruction.Substring(m_command.Length);
                    m_command = m_command.Trim();
                    break;
            }
            m_parameters = StringUtils.GetParameters(instruction);
            for (int i = 0; i < m_parameters.Count; i++)
            {
                m_parameters[i] = m_parameters[i].Trim();
            }
            m_counter = 0;
            m_actionsOfCommand = 0;
        }

        public double GetVariable(string variable)
        {
            MachinationsNode node;
            MachinationsGraph graph = m_artifitialPlayer.Graph as MachinationsGraph;
            if (graph.ColorCoding)
            {
				node = graph .FindNodeByCaptionAndColor(variable, m_artifitialPlayer.Color);
            }
            else
            {
				node = graph.FindNodeByCaption(variable);
            }
			if (node is Pool)
            {
				return (node as Pool).ResourceCount;
			}
			if (node is Register)
            {
				return (node as Register).Value;
			}
			switch (variable.ToLower()) {
				case "pregen0":
					return m_artifitialPlayer.PregeneratedRandom[0];
				case "pregen1":
					return m_artifitialPlayer.PregeneratedRandom[1];
				case "pregen2":
					return m_artifitialPlayer.PregeneratedRandom[2];
				case "pregen3":
					return m_artifitialPlayer.PregeneratedRandom[3];
				case "pregen4":
					return m_artifitialPlayer.PregeneratedRandom[4];
				case "pregen5":
					return m_artifitialPlayer.PregeneratedRandom[5];
				case "pregen6":
					return m_artifitialPlayer.PregeneratedRandom[6];
				case "pregen7":
					return m_artifitialPlayer.PregeneratedRandom[7];
				case "pregen8":
					return m_artifitialPlayer.PregeneratedRandom[8];
				case "pregen9":
					return m_artifitialPlayer.PregeneratedRandom[9];
				case "random":
					return RandomUtils.NextDouble();
				case "actions":
					return m_artifitialPlayer.ActionsExecuted;
				case "steps":
					return graph.Step;
				case "actionsofcommand":
					return m_actionsOfCommand;
				case "actionsperstep":
					return m_artifitialPlayer.ActionsPerStep;
				default:
					string s = "Cannot find pool, register or variable| labeled '" + variable + "'";
					if (graph.ColorCoding)
                    {
						s += " (color " + m_artifitialPlayer.Color.ToNiceString().ToLower()+")";
					}
                    
                    m_artifitialPlayer.Graph.OnChanged(GraphEvent.GraphWarning, s);
					//(ArtificialPlayer.graph as MachinationsGraph).end("Error");
					return 0;
			}
			
		}
		
		public bool Activate()
        {
			if (m_condition != null)
            {
				if (MachinationsScriptExpression.Evaluate(m_condition, this) == 0)
                    return false;
            }
            m_actionsOfCommand++;
			switch (m_command.ToLower()) {
				case "fire":
                    CommandFire();
					return true;
				case "fireall":
                    CommandFireAll();
					return true;
				case "firesequence":
				case "do":
                    CommandFireSequence();
					return true;
				case "firerandom":
				case "choose":
                    CommandFireRandom();
					return true;
				case "increase":
                    ChangeRegister(1);
					return true;
				case "decrease":
                    ChangeRegister(-1);
					return true;
				case "endturn":
                    EndTurn();
					return true;
				case "stopdiagram":
                    StopDiagram();
					return true;
				case "activate":
                    ActivateAp();
					return true;
				case "deactivate":
                    m_artifitialPlayer.Deactivate();
					return true;
			}
			return false;
		}
		
		private void ClickNode(MachinationsNode node)
        {
			if (node == null) return;
			node.Click();
			node.SetFiring();
		}
		
		private void FireNode(string caption)
        {
			MachinationsGraph graph = m_artifitialPlayer.Graph as MachinationsGraph;
			if (graph == null) return;
            var nodes = graph.ColorCoding
                ? graph.FindAllNodesByCaptionAndColor(caption, m_artifitialPlayer.Color)
                : graph.FindAllNodesByCaption(caption);
			if (nodes.Count < 1)
            {
				string message = "Cannot find node '" + caption + "'";
				if (graph.ColorCoding)
                {
					message += "|(color " + m_artifitialPlayer.Color.ToNiceString().ToLower()+")";
				}
                graph.OnChanged(GraphEvent.GraphError, message);
				//trace("WARNING: " + s);
				//graph.OnChanged(GraphEvent.GraphError, null, s));
				//graph.end("Error");
			}
			nodes.ForEach(ClickNode);
		}
		
		private void CommandFire()
		{
			if (m_parameters.Count > 0)
            {
                FireNode(m_parameters[0]);
			}
		}
		
		private void CommandFireAll()
		{
			m_parameters.ForEach(FireNode);
		}
		
		private void CommandFireSequence()
		{
			if (m_parameters.Count > 0) {

                FireNode(m_parameters[m_counter]);
                m_counter++;
				m_counter %= m_parameters.Count;
			}
		}
		
		private void CommandFireRandom()
		{
			if (m_parameters.Count > 0) {
				int r = new Random().Next(m_parameters.Count);
                FireNode(m_parameters[r]);
			}
		}
		
		private void ChangeRegister(int d)
		{
		    MachinationsGraph graph = m_artifitialPlayer.Graph as MachinationsGraph;
			if (m_parameters.Count > 0 && graph != null) {
				Register register = graph.FindNodeByCaptionAndColor(m_parameters[0], m_artifitialPlayer.Color) as Register;
				if (register != null && register.ActivationMode == ActivationMode.Interactive)
                {
                    register.Interaction = d * register.ValueStep;
                    register.Fire();
				}
			}
		}
		
		private void EndTurn()
		{
			MachinationsGraph graph = m_artifitialPlayer.Graph as MachinationsGraph;
			if (graph != null && graph.ActionsPerTurn > 0)
            {
				graph.ActionsThisTurn = graph.ActionsPerTurn;
			}
		}
		
		private void StopDiagram()
		{
			MachinationsGraph graph = m_artifitialPlayer.Graph as MachinationsGraph;
			if (graph != null && graph.Running) {
			    graph.End(m_parameters.Count > 0 ? m_parameters[0] : m_artifitialPlayer.Caption);
			}
		}
		
		private void ActivateAp()
		{
			MachinationsGraph graph = m_artifitialPlayer.Graph as MachinationsGraph;
			if (m_parameters.Count > 0 && graph != null) {
				MachinationsNode node = graph.FindNodeByCaptionAndColor(m_parameters[m_counter], m_artifitialPlayer.Color);
				m_counter++;
				m_counter %= m_parameters.Count;
			    (node as ArtificialPlayer)?.Activate();
			}
            m_artifitialPlayer.Deactivate();
		}

    }
}
