using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using DevExpress.Utils.Drawing;
using DevExpress.XtraPrinting;
using PXP.Crawler.API.Designers;
using PXP.Crawler.API.Designers.Controls;
using PXP.Crawler.API.Utils;

namespace PXP.Crawler.API.Model
{
    public enum NodeConnectionType : int
    {
        Basic = 0,
        PropertyProperty
    }

    public class NodeConnection
    {
        internal Program program;

        internal int id = -1;
        public Node source;
        public Node target;

        public bool enableDesigner = false;

        public NodeDesignerPin sourceDesignerPin;
        public NodeDesignerPin targetDesignerPin;

        internal NodeConnectionType type = NodeConnectionType.Basic;

        internal Point sourceHandlePoint = new Point(0, 50);
        internal Point targetHandlePoint = new Point(0, -50);
        
        internal Rectangle sourceHandleRect = new Rectangle(0, 0, 12, 12);
        internal Rectangle targetHandleRect = new Rectangle(0, 0, 12, 12);

        private ProgramDesignerControl progDesCont;

        public NodeConnection(Program program, Node sourceNode, Node targetNode)
        {
            this.program = program;
            source = sourceNode;
            target = targetNode;

        }

        public int ID
        {
            get { return id; }
            set { id = value; }
        }

        public virtual void Update()
        {
            
        }

        private Point sourcePoint = Point.Empty;
        private Point targetPoint;
        private Point tsCPoint = Point.Empty;
        private Point ttCPoint;

        public void Draw(Graphics g, Pen backPen, Pen forePen, Brush sHandleBrush, Brush tHandleBrush, Point adjPos, bool updateCurve = true)
        {
            if (!enableDesigner)
                return;

            ProgramDesignerControl pdCont = ((NodeDesigner) sourceDesignerPin.node.designer).programDesignerControl;
            if (updateCurve | sourcePoint == Point.Empty)
            {

                if (progDesCont == null)
                    progDesCont = ((NodeDesigner)sourceDesignerPin.designerControl.node.designer).programDesignerControl;


                sourcePoint = sourceDesignerPin.pos;
                sourcePoint.X += sourceDesignerPin.node.designer.control.Left - pdCont.programNodesControl.AutoScrollPosition.X;
                sourcePoint.Y += sourceDesignerPin.node.designer.control.Top - pdCont.programNodesControl.AutoScrollPosition.Y;
                //sourcePoint.X += sourceDesignerPin.node.designer.control.Left - adjPos.X;
                //sourcePoint.Y += sourceDesignerPin.node.designer.control.Top - adjPos.Y;
                
                targetPoint = targetDesignerPin.pos;
                targetPoint.X += targetDesignerPin.node.designer.control.Left - pdCont.programNodesControl.AutoScrollPosition.X;
                targetPoint.Y += targetDesignerPin.node.designer.control.Top - pdCont.programNodesControl.AutoScrollPosition.Y;
                //targetPoint.X += targetDesignerPin.node.designer.control.Left - adjPos.X;
                //targetPoint.Y += targetDesignerPin.node.designer.control.Top - adjPos.Y;
                updateCurve = true;
            }

            Point sPnt = sourcePoint;
            Point tPnt = targetPoint;

            sPnt.X -= adjPos.X;
            sPnt.Y -= adjPos.Y;
            tPnt.X -= adjPos.X;
            tPnt.Y -= adjPos.Y;

            if (sourceHandlePoint == Point.Empty || targetHandlePoint == Point.Empty)
            {
                int yDiff = Math.Abs(sPnt.Y - tPnt.Y) / 2;
                if (sPnt.Y < tPnt.Y)
                {
                    sourceHandlePoint = new Point(0, yDiff);
                    targetHandlePoint = new Point(0, -yDiff);
                }
                else
                {
                    sourceHandlePoint = new Point(0, -yDiff);
                    targetHandlePoint = new Point(0, yDiff);
                }
            }

            if (updateCurve | tsCPoint == Point.Empty)
            {
                tsCPoint = sPnt;
                ttCPoint = tPnt;

                tsCPoint.X += sourceHandlePoint.X;
                tsCPoint.Y += sourceHandlePoint.Y;
                ttCPoint.X += targetHandlePoint.X;
                ttCPoint.Y += targetHandlePoint.Y;

                /*int yDiff = Math.Abs(sPnt.Y - tPnt.Y) / 2;
                if (sPnt.Y < tPnt.Y)
                {
                    tsCPoint.Y += yDiff;
                    ttCPoint.Y -= yDiff;
                }
                else
                {
                    tsCPoint.Y -= yDiff;
                    ttCPoint.Y += yDiff;
                }   */
            }
            

            g.DrawBezier(backPen, sPnt, tsCPoint, ttCPoint, tPnt);            
            g.DrawBezier(forePen, sPnt, tsCPoint, ttCPoint, tPnt);

            if (ProjectDesignerControl.EnableDebug)
            {
                string connInfo = "ID: " + id.ToString();

                PointF bp = Misc.BezierPoint(0.5f, sPnt, tsCPoint, ttCPoint, tPnt);
                SizeF infoSizeF = g.MeasureString(connInfo, source.designer.control.Font);
                bp.X -= infoSizeF.Width * 0.5f;

                g.DrawString(connInfo, source.designer.control.Font, Brushes.Black, new PointF(bp.X + 1, bp.Y + 1));
                g.DrawString(connInfo, source.designer.control.Font, Brushes.White, bp);
            }



            if (progDesCont.editMoveEnabled)
            {
                bool sSel = ((NodeDesignerControl)source.designer.control).isSelected;
                bool tSel = ((NodeDesignerControl)target.designer.control).isSelected;
                if (sSel | tSel)
                {
                    sourceHandleRect.Location = sPnt;
                    sourceHandleRect.X += sourceHandlePoint.X;
                    sourceHandleRect.Y += sourceHandlePoint.Y;

                    targetHandleRect.Location = tPnt;
                    targetHandleRect.X += targetHandlePoint.X;
                    targetHandleRect.Y += targetHandlePoint.Y;

                    //g.FillRectangle(handleBrush, sourceHandleRect);
                    //g.FillRectangle(handleBrush, targetHandleRect);

                    g.FillEllipse(sHandleBrush, targetHandleRect);
                    g.FillEllipse(tHandleBrush, sourceHandleRect);
                    g.DrawEllipse(Pens.Black, targetHandleRect);
                    g.DrawEllipse(Pens.DarkBlue, sourceHandleRect);
                    
                    //g.DrawRectangle(Pens.DimGray, sourceHandleRect);
                    //g.DrawRectangle(Pens.DimGray, targetHandleRect);
                }    
            }
            

        }

        public void PutXml(XmlNode parent)
        {
            XmlElement connEl = Misc.CreateAppendXmlElement(parent, "conn");

            PutXml(connEl);
        }

        protected internal virtual void PutXml(XmlElement connElement)
        {            
            Misc.CreateAppendXmlElement(connElement, "id", id.ToString());
            Misc.CreateAppendXmlElement(connElement, "type", ((int) type).ToString());
            Misc.CreateAppendXmlElement(connElement, "src_nid", source.id.ToString());
            Misc.CreateAppendXmlElement(connElement, "targ_nid", target.id.ToString());

            XmlElement shEl = Misc.CreateAppendXmlElement(connElement, "src_hand");
            shEl.SetAttribute("x", sourceHandlePoint.X.ToString());
            shEl.SetAttribute("y", sourceHandlePoint.Y.ToString());
            XmlElement thEl = Misc.CreateAppendXmlElement(connElement, "targ_hand");
            thEl.SetAttribute("x", targetHandlePoint.X.ToString());
            thEl.SetAttribute("y", targetHandlePoint.Y.ToString());
        }

        public static NodeConnection CreateFromXml(Program prog, XmlElement connEl)
        {
            NodeConnection conn = null;

            int cid = Conv.StringToInt(Misc.GetChildElementXml(connEl, "id"), -1);
            NodeConnectionType nctype = (NodeConnectionType)Conv.StringToInt(Misc.GetChildElementXml(connEl, "type"), 0);
            Node srcNode = prog.FindNodeByID(Conv.StringToInt(Misc.GetChildElementXml(connEl, "src_nid"), -1));
            Node targNode = prog.FindNodeByID(Conv.StringToInt(Misc.GetChildElementXml(connEl, "targ_nid"), -1));

            if (cid != -1 & srcNode != null & targNode != null)
            {
                switch (nctype)
                {
                    case NodeConnectionType.PropertyProperty:

                        string spname = Misc.GetChildElementXml(connEl, "src_pname");
                        string tpname = Misc.GetChildElementXml(connEl, "targ_pname");

                        Point shPoint = Point.Empty;
                        Point thPoint = Point.Empty;

                        XmlElement shEl = connEl["src_hand"];
                        if (shEl != null)
                            shPoint = new Point(Conv.StringToInt(shEl.GetAttribute("x"), 0), Conv.StringToInt(shEl.GetAttribute("y"), 50));
                        
                        XmlElement thEl = connEl["targ_hand"];
                        if (thEl != null)
                            thPoint = new Point(Conv.StringToInt(thEl.GetAttribute("x"), 0), Conv.StringToInt(thEl.GetAttribute("y"), 50));

                        NodeConnectionProperty pconn = new NodeConnectionProperty(prog, srcNode, targNode, srcNode.GetType().GetProperty(spname), targNode.GetType().GetProperty(tpname));
                        if (pconn.targetPropInfo == null)
                        {
                            conn = null;
                        }
                        else
                        {
                            if (!pconn.target.inPropConns.ContainsKey(pconn.targetPropInfo.Name))
                            {
                                pconn.target.inPropConns.Add(pconn.targetPropInfo.Name, pconn);
                                pconn.target.OnInputPinConnected(pconn.targetPropInfo.Name);
                            }                            
                        }
                        pconn.sourceHandlePoint = shPoint;
                        pconn.targetHandlePoint = thPoint;
                        conn = pconn;
                        break;
                }

                if (conn != null)
                {
                    conn.id = cid;
                    prog.connections.Add(conn.id, conn);
                    conn.UpdateNodeReferences();                    
                }
            }

            return conn;
        }

        public virtual void UpdateNodeReferences()
        {
        }
    }
}

