using System;
using System.Xml;
using Machinations.Description;
using Machinations.FromFlash.graph;
using Machinations.UserComponents;

namespace Machinations.FromFlash.machinations.model
{
    /// <summary>
    /// connection, can affect other elements,trigger, change capacity or number of resources or inhibit
    /// </summary>
    public class StateConnection : MachinationsConnection
    {
        private int m_state;
		private bool m_alreadyTriggered;
        private bool m_forwardOnly;

        //TODO: Fix a bug with state connections (activators) activating pools!

        public StateConnection()
		{
		    Label.Text = "+1";
		    m_forwardOnly = false;
		}

        public override ConnectionClass ConnectionClass => ConnectionClass.StateConnection;

        public int State
        {
            get { return m_state; }
            set
            {
                if (End is Register)
                {
                    if (Start is Pool)
                    {
                        value = ((Pool) Start).ResourceCount;
                    }
                    else if (Start is Register) value = ((Register) Start).Value;
                    else value = 0;
                }
                int previous = m_state;
                int interval = Label.IntervalType == LabelType.FixedValue? Label.Interval : 1;
                m_state = value;
                var change = (m_state/interval - previous/interval);
                if (ForwardOnly && change < 0) return;
                if(Label.Type == LabelType.DynamicDice)
                    for (int i = 0; i < change; i++)
                    {
                        Label.GenerateNewValue();
                        SetState(Label.Value);
                    }
                else
                    SetState(change * Label.Value);
            }
        }

        public bool ForwardOnly
        {
            get { return m_forwardOnly || Label.Type == LabelType.DynamicDice; }
            set
            {
                if (m_forwardOnly != value)
                {
                    m_forwardOnly = value;
                    UpdateDescription();
                }
            }
        }

        public override void UpdateDescription()
        {
            bool forwardOnly = m_forwardOnly || Label.Type == LabelType.DynamicDice;
            DescriptionChange(NodesDescription.ForwardOnlyOrder,
                forwardOnly ? NodesDescription.ForwardOnlyDescription : "");
            base.UpdateDescription();
        }

        protected void SetState(double changeValue)
        {
            switch (Label.Type)
            {
                case LabelType.ChangeMultiplier:
                    (End as MachinationsConnection)?.Label.ModifyMultiplier(Convert.ToInt32(changeValue));
                    break;
                case LabelType.ChangeCapacity:
                    (End as Pool)?.ModifyCapacity(Convert.ToInt32(changeValue));
                    break;
                case LabelType.ChangeInterval:
                    if (End is MachinationsConnection) ((MachinationsConnection) End).Label.ModifyInterval(Convert.ToInt32(changeValue));
                    break;
                case LabelType.FixedValue:
                case LabelType.CalculatedValue:
                case LabelType.ChangeProbability:
                case LabelType.ChangeValue:
                case LabelType.DynamicDice:
                    if (End is MachinationsConnection)
                        ((MachinationsConnection)End).Label.Modify(changeValue);
                    else if (End is Register) ((Register) End).CalculateValue();
                    else if (End is Pool) ((Pool) End).Modify(Convert.ToInt32(changeValue));
                    else if (End is Gate) ((Gate) End).Modify(Convert.ToInt32(changeValue));
                    else (End as Delay)?.Modify(Convert.ToInt32(changeValue));
                    break;
                case LabelType.Greater:
                case LabelType.GreaterOrEqual:
                case LabelType.EqualTo:
                case LabelType.Less:
                case LabelType.LessOrEqual:
                case LabelType.NotEqualTo:
                case LabelType.Range:
                    Inhibited = !Label.CheckCondition(m_state);
                    break;
            }
        }

        public void Fire()
        {
			if ((Label.Type == LabelType.Trigger || Start is Gate) && !m_alreadyTriggered)
            {
				DoFire();
			}
        }

        public void ReverseFire()
        {
			if ((Label.Type == LabelType.ReverseTrigger) && !m_alreadyTriggered)
            {
				DoFire();
			}
		}

        public override void Update(double time) 
		{
			m_alreadyTriggered = false;
			base.Update(time);
		}

        public override void Prepare(bool doEvents) 
		{
			base.Prepare(doEvents);
            m_state = 0;
		    var startPool = Start as Pool;
		    if (startPool != null)
            {
				m_state = startPool.ResourceCount;
				if (((MachinationsGraph) m_graph).ColorCoding )
                {
					if (Color != startPool.Color)           //co kdyz je cerna?   
                    {
                        m_state = startPool.ResourceColorCount(Color);
                    }
                }
			}
			if (Start is Register) m_state = ((Register) Start).Value;
            ChangeState(0);
		}

        public void ChangeState(int delta)
        {
			State = m_state + delta;            //setter ma funkci
            CheckInhibition();
		}

        public void ChangeModifier(double delta)
        {
			if (Start is Pool) {
				int c = Convert.ToInt32((Start as Pool).ResourceCount * delta);
                m_state += c;
				int interval = 1;
				if (Label.IntervalType == LabelType.FixedValue) {
					interval = Label.Interval;
				}

				if (End is MachinationsConnection && interval!=1) (End as MachinationsConnection).Label.Modify(c);
				else if (End is MachinationsConnection) (End as MachinationsConnection).Label.Modify(c);
				else if (End is Pool) (End as Pool).Modify(c);
				else if (End is Gate) (End as Gate).Modify(c);
				else if (End is Register) {
					State = m_state;
					(End as Register).CalculateValue();
				}
			}
			//checkInhibition();
		}


        public void ResetState()
        {
			if (Start is Pool)
            {
				State = (Start as Pool).Resources.Count;
			} else if (Start is Register)
            {
				m_state = 0;
				if (End is MachinationsConnection) (End as MachinationsConnection).Label.Reset();
				State = (Start as Register).Value;
			}
		}

        public bool IsSetter()
        {
			switch (Label.Type) {
				case LabelType.FixedValue:
				case LabelType.CalculatedValue:
				case LabelType.ChangeInterval:
				case LabelType.ChangeProbability:
				case LabelType.ChangeValue:
					return true;
			}
			return false;
		}

        public bool IsActivator()
        {
			return (Label.IsCondition());
		}


        public void CheckInhibition()
        {
			if (Start is MachinationsNode && (Label.Type == LabelType.Trigger || Start is Gate))
            {
				Inhibited = (Start as MachinationsNode).Inhibited;   
			}
		}

        public override string ToMMString() 
		{
			if (End is GraphConnection) //it is a hyperedge!
				return "";
				
			string expression = Label.Type.ToString();
			
			switch (Label.Type)
			{
				case LabelType.ChangeValue:
					return "";
				case LabelType.Trigger:
					expression = "*";
					break;
			}
			
			return GetMMName() + ": " + Start?.GetMMName() + " ." + expression + ".> " + End?.GetMMName();

        }

        public override XmlElement GenerateXml(XmlDocument document, XmlElement element = null)
        {
            element = base.GenerateXml(document, element);
            element.SetAttribute("forwardOnly", m_forwardOnly.ToString());
            return element;
        }

        public override void ReadXml(XmlElement element)
        {
            base.ReadXml(element);
            if (element.HasAttribute("forwardOnly"))
                m_forwardOnly = bool.Parse(element.GetAttribute("forwardOnly"));
        }

        private void DoFire()
        {
            m_alreadyTriggered = true;
            if (End is MachinationsNode && (!(End as MachinationsNode).Inhibited || (End is EndCondition))) (End as MachinationsNode).Fire();
            if (End is ResourceConnection) ((ResourceConnection) End).Fire();
            if (End is CardInstance) ((CardInstance) End).TakeCard();
            Firing = Math.Min(((MachinationsGraph) m_graph).FireInterval * 0.75, 0.5);
            if (DoEvents) OnChanged(GraphEvent.ElementChange);
        }
    }

    public class MergedStateConnection : StateConnection, IMergedConnection, IEquatable<MergedStateConnection>
    {
        private readonly MergedConnectionKey m_mergedConnectionKey;

        public MergedStateConnection(MergedConnectionKey mcc) 
        {
            m_mergedConnectionKey = mcc;
        }

        public MergedConnectionKey MergedConnectionKey
        {
            get { return m_mergedConnectionKey; }
        }

        protected override void InhibitedChangeForReal()
        {
            base.InhibitedChangeForReal();
            m_mergedConnectionKey.InnerConnection.ForceChangeInhibition(Inhibited);
            m_mergedConnectionKey.OuterConnection.ForceChangeInhibition(Inhibited);
        }

        public bool Equals(MergedStateConnection other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return m_mergedConnectionKey.Equals(other.m_mergedConnectionKey);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != this.GetType()) return false;
            return Equals((MergedStateConnection) obj);
        }

        public override int GetHashCode()
        {
            return m_mergedConnectionKey.GetHashCode();
        }
    }
}
