using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Xml;
using Machinations.FromFlash.graph;
using Machinations.FromFlash.machinations.model;

namespace Machinations.UserComponents
{
    public abstract class InOutBase : GraphNode
    {
        protected readonly List<MachinationsConnection> m_connections;
        protected bool m_input;
        protected ConnectionClass m_conClass;
        protected string m_name;
        protected IUserComponentSchema m_userComponent;
        private int m_inOutNumber;

        protected InOutBase()
        {
            m_connections = new List<MachinationsConnection>();
            m_name = "";
        }

        protected InOutBase(IUserComponentSchema userComponent, Point position, int inOutNumber):this()
        {
            m_userComponent = userComponent;
            m_position = position;
            m_inOutNumber = inOutNumber;
        }

        public abstract string InOutType { get; }
        protected abstract bool Inner { get; }

        public bool Input
        {
            get { return m_input; }
        }

        public ConnectionClass ConClass
        {
            get { return m_conClass; }
        }

        public int InOutNumber
        {
            get { return m_inOutNumber; }
        }

        public IUserComponentSchema UserComponent
        {
            get { return m_userComponent; }
            set { m_userComponent = value; }
        }

        public string Name
        {
            get { return m_name; }
            set { m_name = value; }
        }

        
        public override void AddInput(MachinationsConnection connection)
        {
            if (CheckIfConsistent(connection.ConnectionClass, false))
            {
                m_input = !Inner;
                m_conClass = connection.ConnectionClass;
               m_connections.Add(connection);
            }
            else throw new NotImplementedException("Wrong type/direction of connection.");
        }

        public override void AddOutput(MachinationsConnection connection)
        {
            if (CheckIfConsistent(connection.ConnectionClass, true))
            {
                m_input = Inner;
                m_conClass = connection.ConnectionClass;
                m_connections.Add(connection);
            }
            else throw new NotImplementedException();
        }

        public override void RemoveInput(MachinationsConnection connection)
        {
            m_connections.Remove(connection);
        }

        public override void RemoveOutput(MachinationsConnection connection)
        {
            RemoveInput(connection);
        }

        public override void Dispose()
        {
            base.Dispose();
            m_connections.ForEach(RemoveSelfFromConnection);
            m_connections.Clear();
            //m_userComponent.InAndOuts.Remove(this);
        }

        public override XmlElement GenerateXml(XmlDocument document, XmlElement element = null)
        {
            if (element == null)            //rozlisit komponentu a outer graph
            {
                element = document.CreateElement(InOutType);
            }
            element.SetAttribute("UcPosition", m_inOutNumber.ToString());
            element.SetAttribute("Name", m_name);
            return base.GenerateXml(document, element);
        }

        public override void ReadXml(XmlElement element)
        {
            if (element.HasAttribute("Name"))
                m_name = element.GetAttribute("Name");
            m_inOutNumber = int.Parse(element.GetAttribute("UcPosition"));
            base.ReadXml(element);
        }

        public virtual void UpdateFields(InOutBase source)
        {
            Position = source.Position;
            Name = source.Name;
        }

        public bool IsSimilar(OuterInOut other)
        {
            return Inner == other.Inner && Input == other.Input && (ConClass == other.ConClass || ConClass == ConnectionClass.None);
        }


        private void RemoveSelfFromConnection(GraphConnection connection)
        {
            if (connection.Start == this)
            {
                connection.Start = null;
                connection.StartId = -1;
            }

            if (connection.End == this)
            {
                connection.End = null;
                connection.EndId = -1;
            }
                
        }

        private bool ClassChecked(ConnectionClass c)
        {
            if (m_connections.Any())
            {
                return m_conClass == c;
            }
            return true;
        }

        protected abstract bool DirectionChecked(bool directionIn);

        private bool CheckIfConsistent(ConnectionClass connectionClass, bool directionIn)
        {
            return DirectionChecked(directionIn) && ClassChecked(connectionClass);
        }
    }
}
