using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using Machinations.FromFlash.graph;

namespace Machinations.FromFlash.machinations.model
{
    /// <summary>
    /// machinations node, can count some basic formulas and bz results affect other nodes
    /// </summary>
    public class Register : MachinationsNode
    {
        private int m_value;
		private bool m_calculated;
		public int MinValue;
		public int MaxValue;
		private int m_startValue;
		public int ValueStep;
		public const int Limit = 9999;
		public int Interaction;
		public bool HasTriggers;
		public bool IsTriggered;

        private List<string> m_postFix;

        private List<string> m_values;

        private bool m_expression;

        public Register()
        {
            Size = 32;
            Value = 0;
            m_calculated = true;
            MinValue = -Limit;
            MaxValue = Limit;
            ValueStep = 1;
            m_startValue = 0;
            m_values = new List<string>();
            Actions = 0;
            for (int i = 0; i < 26; i++) m_values.Add("0");
        }

        public int Value
        {
            get
            {
                if (!m_calculated)
                {
                    m_calculated = true;
                    CalculateValue();
                }
                return m_value;
            }
            set
            {
                if (m_value != value)
                {
                    m_value = value;        //asi   

                    //if (m_doEvents) OnChanged(GraphEvent.ElementChange);
                }
            } 
        }

        public int StartValue
        {
            get { return m_startValue; }
            set
            {
                m_startValue = value;
                if (ActivationMode == ActivationMode.Interactive) Value = m_startValue;

            }
        }

        public override NodeClass NodeClass => NodeClass.Register;

        public override void Fire() 
		{
			if (HasTriggers && IsTriggered && Interaction==0) {
				double r = Convert.ToInt32((RandomUtils.NextDouble() * 100));
				for (int i = 0; i < m_outputs.Count; i++)
                {
                    StateConnection connection = m_outputs[i] as StateConnection;
					if (connection != null && connection.Label.Type ==  LabelType.Trigger)
                    {
                        if (r < Value)
                            connection.Fire();
                    }
					if (connection != null && connection.Label.Type ==  LabelType.ReverseTrigger)
                    {
                        if (r >= Value)
                            (m_outputs[i] as StateConnection).ReverseFire();
                    }
                }
            }
			
			//implement changes
			if (Interaction!=0) {
				if (m_doEvents) OnChanged(GraphEvent.ElementChange);
				int previous = Value;
				Value = Math.Min(Math.Max(Value + Interaction, MinValue), MaxValue);
                Interaction = 0;

                CalculateValue();
                for (int i = 0; i< m_outputs.Count; i++) {
					if (m_outputs[i] is StateConnection) {
						(m_outputs[i] as StateConnection).ChangeState(Value-previous);
					}
				}				
			}
			base.Fire();
		}

        public override void AutoFire() 
		{
			base.AutoFire();
		    if (HasTriggers && !IsTriggered)
		    {
		        int r = Convert.ToInt32(RandomUtils.NextDouble() * 100);
		        for (int i = 0; i < m_outputs.Count; i++)
		        {
		            StateConnection connection = m_outputs[i] as StateConnection;
		            if (connection != null)
		            {
		                if (connection.Label.Type ==  LabelType.Trigger)
		                {
		                    if (r < Value)
		                        connection.Fire();
		                }
		                else if (connection.Label.Type ==  LabelType.ReverseTrigger)
		                {
		                    if (r >= Value)
		                        connection.ReverseFire();
		                }
		            }
		        }
		    }
		}
        
		
	 //   public override MyVector GetConnection(MyVector towards) 
		//{
		//	MyVector d = m_position.Clone();
		//	MyVector u = towards.Subtract(m_position);
		//	u.Normalize();
		//	MyVector p = MathUtil.GetSquareOutlinePoint(u, 0.5 * Size + Thickness + 1);
		//	d.IncrementBy(p);
		//	return d;			
		//}
		
		public void CalculateValue()
        {
			if (ActivationMode == ActivationMode.Interactive) {
				m_calculated = true;
				return;
			}
			int newValue = 0;
			if (m_expression)
			{
			    foreach (GraphConnection t in m_inputs)
			    {
			        StateConnection connection = t as StateConnection;
			        if (connection != null )
			        {
			            int j = connection.Label.Text[0] - 97;
			            if (j >= 0 && j<m_values.Count) m_values[j] = connection.State + "";
			        }
			    }
			    newValue = Convert.ToInt32(MachinationsRegisterExpression.Evaluate(m_postFix, m_values));
			}
			else if (m_caption.ToLower() == "max")
            {
				//find the maximum value
				newValue = int.MinValue;
				for (int i = 0; i < m_inputs.Count; i++)
                {
                    StateConnection connection = m_inputs[i] as StateConnection;
					if (connection != null)
                    {
						if (newValue < (m_inputs[i] as StateConnection).State)
						{
						    newValue = (m_inputs[i] as StateConnection).State;
						}
					}
				}
			} else if (m_caption.ToLower() == "min")
            {
				//find the maximum value
                newValue =
                    m_inputs.OfType<StateConnection>()
                        .Select(t => t.State)
                        .Concat(new[] {int.MaxValue})
                        .Min();
            } else
            {
				//no m_caption simply add all inputs;
				for (int i = 0; i < m_inputs.Count; i++)
                {
                    StateConnection connection = m_inputs[i] as StateConnection;
					if (connection != null)
					{
					    newValue += Convert.ToInt32(((StateConnection) m_inputs[i]).State*((StateConnection) m_inputs[i]).Label.Value);
					}
				}		
				if (m_caption.ToLower() == "actions" && (m_graph as MachinationsGraph).TimeMode == TimeMode.TurnBased)
                {
					(m_graph as MachinationsGraph).ActionsPerTurn = newValue;
				}
			}
			
			newValue = Math.Min(Math.Max(newValue, MinValue), MaxValue);
            if (newValue != m_value)
            {
                int delta = newValue - m_value;
				m_value = newValue;
				if (m_doEvents) OnChanged(GraphEvent.ElementChange);
				for (int i = 0; i < m_outputs.Count; i++)
                {
					if (m_outputs[i] is StateConnection)
                    {
						((StateConnection) m_outputs[i]).ChangeState(delta);
					}
				}
			}
            m_calculated = true;
        }
		
		public override void Prepare(bool doEvents) 
		{
		    m_calculated = false;
		    base.Prepare(doEvents);
			/*if (stateInputCount == 0) {
				this.activationMode = MODE_INTERACTIVE;
			} else {
				this.activationMode = MODE_PASSIVE;
			}*/
			m_expression = false;
			if (ActivationMode != ActivationMode.Interactive && m_caption != "" && m_caption != "max" && m_caption != "min")
            {
                if (!MachinationsRegisterExpression.IsVariable(m_caption)
                    && m_caption.IndexOf('+') < 0 
                    && m_caption.IndexOf('-') < 0 
                    && m_caption.IndexOf('%') < 0
                    && m_caption.IndexOf('*') < 0
                    && m_caption.IndexOf('/') < 0
                    && m_caption.IndexOf('D') < 0
                    && m_caption.IndexOf('(') < 0
                    && m_caption.IndexOf(')') < 0)
                {
                    m_expression = false;
                }
                else {
                    m_postFix = MachinationsRegisterExpression.ToPostFix(m_caption);
                    m_expression = true;
                    for (int i = 0; i < m_values.Count; i++) m_values[i] = "0";
                    CalculateValue();
                }
            }
			if (ActivationMode == ActivationMode.Interactive)
            {
                m_calculated = true;
                Value = StartValue;
                if (doEvents) OnChanged(GraphEvent.ElementChange);
            } else if (!m_calculated)
            {
                Value = 0;
            }
            HasTriggers = false;
			for (int i = 0; i < m_outputs.Count; i++)
            {
                StateConnection connection = m_outputs[i] as StateConnection;
                if (connection != null 
                    && (connection.Label.Type ==  LabelType.Trigger 
                        || connection.Label.Type ==  LabelType.ReverseTrigger))
                {
                    HasTriggers = true;
                    break;
                }
            }

            IsTriggered = false;
			for (int i = 0; i < m_inputs.Count; i++)
			{
			    StateConnection connection = m_inputs[i] as StateConnection;
                if (connection != null
                    && (connection.Label.Type ==  LabelType.Trigger 
                        || connection.Label.Type ==  LabelType.ReverseTrigger))
                {
                    IsTriggered = true;
                    break;
                }
            }
        }

        public void PrepareCalculated()
        {
			if (ActivationMode == ActivationMode.Interactive) return;
			if (!m_calculated)
            {
                Value = 0;
            }
			for (int i = 0; i < m_outputs.Count; i++) {
                if (m_outputs[i] is StateConnection)
                {
                    (m_outputs[i] as StateConnection).ResetState();
                }
            }
            CalculateValue();
        }

        public override void Stop() 
		{
			base.Stop();
			if (ActivationMode == ActivationMode.Interactive)
            {
				Value = StartValue;
			} else
			{
			    Value = 0;
                for (var i = 0; i < m_values.Count; i++)
                {
                    m_values[i] = "0";
                }
			    m_postFix = null;
			    //m_doEvents = false;
			    m_calculated = true;
			    m_expression = false;

			}
            if (m_doEvents) OnChanged(GraphEvent.ElementChange);
		}
		
		public override XmlElement GenerateXml(XmlDocument document, XmlElement element = null) 
		{
			element = base.GenerateXml(document, element);
			element.SetAttribute("min", MinValue + "");
			element.SetAttribute("max", MaxValue + "");
			element.SetAttribute("start", m_startValue + "");
			element.SetAttribute("step", ValueStep + "");
			return element;
		}
		
		public override void ReadXml(XmlElement element) 
		{
			base.ReadXml(element);
			MinValue = int.Parse(element.GetAttribute("min"));
			MaxValue = int.Parse(element.GetAttribute("max"));
			m_startValue = int.Parse(element.GetAttribute("start"));
			ValueStep = int.Parse(element.GetAttribute("step"));
            Actions = 0;
        }
    }
}
