using System;
using System.Globalization;
using System.Windows;
using System.Windows.Media;
using System.Xml;
using Machinations.GUI;

namespace Machinations.FromFlash.graph
{
    /// <summary>
    /// ancestor of connections classes, has startelement and endelement plus points
    /// handles the stuff about being a connection
    /// </summary>
    public abstract class GraphConnection : GraphElement
    {
        protected GraphElement m_start;
        protected GraphElement m_end;
        private PointCollection m_points;
        private int m_startId;
        private int m_endId;

        public event Action<IObjectView<GraphElement>> EndConnected;
        public event Action<IObjectView<GraphElement>> StartConnected;


        protected GraphConnection()
        {
            m_points = new PointCollection();
        }


        public GraphElement Start
        {
            get { return m_start; }
            set
            {
                m_start = value;         
            }
        }

        public GraphElement End
        {
            get { return m_end; }
            set
            {
                m_end = value;
            }
        }

        public PointCollection Points
        {
            get { return m_points; }
        }

        public virtual ConnectionClass ConnectionClass => ConnectionClass.None;

        public int StartId
        {
            get { return m_startId; }
            set { m_startId = value; }
        }

        public int EndId
        {
            get { return m_endId; }
            set { m_endId = value; }
        }

        public Point GetMiddlePoint()
        {
            double halfLength = GetTotalLength() / 2;
            int i = 1;
            double pointDistance = GeometryUtils.PointDistance(Points[i - 1], Points[i]);
            while (halfLength - pointDistance > 1)
            {
                halfLength -= pointDistance;
                i++;
                pointDistance = GeometryUtils.PointDistance(Points[i - 1], Points[i]);
            }
            Vector direction = new Vector(Points[i].X - Points[i - 1].X,
                Points[i].Y - Points[i - 1].Y);
            direction.X = direction.X / pointDistance * halfLength;
            direction.Y = direction.Y / pointDistance * halfLength;
            var perpendicular = new Vector(direction.Y, - direction.X);
            perpendicular.Normalize();
            Point middlePoint = new Point(Points[i - 1].X + direction.X + 10*perpendicular.X,
                Points[i - 1].Y + direction.Y + 10*perpendicular.Y);
            return middlePoint;
        }
        private double GetTotalLength()
        {
            double length = 0;
            for (int i = 1; i < Points.Count; i++)
            {
                length += GeometryUtils.PointDistance(Points[i - 1], Points[i]);
            }
            return length;
        }

        public override XmlElement GenerateXml(XmlDocument document, XmlElement element = null)
		{
		    if (element == null)
		    {
		        element = document.CreateElement("connection");
            }
		    element = base.GenerateXml(document, element);
            element.SetAttribute("type", ConnectionClass.ToString());
			if (m_start != null && m_start.Id >= 0)
			{
			    element.SetAttribute("start", m_start.Id + "");
			} else {
                element.SetAttribute("start", "-1");
            }
            if (m_end != null && m_end.Id >= 0)
            {
                element.SetAttribute("end", m_end.Id+ "");
            } else {
                element.SetAttribute("end", "-1");
            }
		    var start = m_start == null ? 0 : 1;
            var end = m_end == null ? m_points.Count :m_points.Count - 1;
		    
		    for (int i = start; i < end; i++)          //bez prvniho a posledniho
		    {
		        Point point = Points[i];
                XmlElement pointElement = (XmlElement)element.AppendChild(document.CreateElement("point"));
                pointElement.SetAttribute("x", "" + Math.Round(point.X));
                pointElement.SetAttribute("y", "" + Math.Round(point.Y));
            }
            return element;
		}
		
		public override void ReadXml(XmlElement element)
		{
		    base.ReadXml(element);
		    m_startId = int.Parse(element.GetAttribute("start"));
			m_endId = int.Parse(element.GetAttribute("end"));

		    int min = 0;
		    int max = element.ChildNodes.Count;

            for (int i = min; i < max; i++)
            {
                double x = double.Parse(((XmlElement)element.ChildNodes[i]).GetAttribute("x"), CultureInfo.InvariantCulture);
                double y = double.Parse(((XmlElement)element.ChildNodes[i]).GetAttribute("y"), CultureInfo.InvariantCulture);
                m_points.Add(new Point(x,y));
            }	
		}

        public void EndConnectedInvoke(IObjectView<GraphElement> elementView)
        {
            EndConnected?.Invoke(elementView);
        }

        public void StartConnectedInvoke(IObjectView<GraphElement> elementView)
        {
            StartConnected?.Invoke(elementView);
        }

        public override void CopyShift()
        {
            PointCollection points = new PointCollection();
            foreach (var point in m_points)
            {
                points.Add(new Point(point.X + 20, point.Y + 20));
            }
            m_points = points;
        }

        public override Point GetPosition()
        {
            return GetMiddlePoint();
        }

        public void ForceChangeInhibition(bool inhibited)
        {
            if (m_inhibited != inhibited)
            {
                m_inhibited = inhibited;
                InhibitedChanged?.Invoke();
            }
        }
    }
}
