using System;
using System.CodeDom;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Data;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using DevExpress.Data.Linq;
using DevExpress.Utils.Serializing;
using DevExpress.XtraBars;
using DevExpress.XtraBars.Docking2010.Customization;
using DevExpress.XtraEditors;
using DevExpress.XtraTreeList.Columns;
using DevExpress.XtraTreeList.Nodes;
using PXP.Crawler.API.Designers.Controls.Forms;
using PXP.Crawler.API.Model;
using NodePinDirPXP.Crawler.API.Utils;
using PXP.Crawler.API.Utils;

namespace PXP.Crawler.API.Designers.Controls
{
    public partial class ProgramDesignerControl : UserControl
    {
        #region Types

        private class PasteDrawControl : Control
        {
            public PasteDrawControl()
            {
                SetStyle(ControlStyles.SupportsTransparentBackColor, false);
                SetStyle(ControlStyles.Opaque, true);
                UpdateStyles();
            }

            /*protected override void OnPaintBackground(PaintEventArgs pevent)
            {
                
            }*/
        }

        #endregion

        #region Variables

        internal Program program;
        private Point popupMenuPoint = new Point();
        private Point mouseDownPoint = new Point();
        private Point mdScrollPos = new Point();
        private Point prevScrollPos = new Point();
        internal List<Node> selectedNodes = new List<Node>();
        private bool doRectSelect = false;
        private Rectangle selRect = new Rectangle();
        private Pen selPen;

        internal NodeDesignerPin connSourcePin = null;
        internal NodeDesignerPin connTargetPin = null;

        public bool pasteAvailable = false;

        private Point pastePoint = new Point();
        NodeConnectionProperty hConnOver = null;
        bool srcHandOver = false;
        bool targHandOver = false;
        Point mdHandPoint = new Point();

        public bool editMoveEnabled = false;

        #endregion

        #region Constructors

        public ProgramDesignerControl()
        {
            InitializeComponent();
            SetStyle(ControlStyles.Selectable, true);
            SetStyle(ControlStyles.StandardClick, true);

            UpdateStyles();
        }

        public ProgramDesignerControl(Program program)
        {
            InitializeComponent();

            programNodesControl.program = program;
            
            SetStyle(ControlStyles.Selectable, true);
            SetStyle(ControlStyles.StandardClick, true);

            UpdateStyles();

            selPen = new Pen(Color.White, 1);
            selPen.DashStyle = DashStyle.Dot;

            this.program = program;
            foreach (Node node in program.nodes.Values)
            {
                Rectangle ddBounds = node.designerData.bounds;
                node.CreateDesigner();
                ((NodeDesigner) node.designer).programDesignerControl = this;
                programNodesControl.Controls.Add(node.designer.control);
                node.designerData.bounds = ddBounds;
                node.designer.control.Location = node.designerData.bounds.Location;
                node.designer.control.Size = node.designerData.bounds.Size;
            }

            programNodesControl.UpdatePreview();
            programNodesControl.overlayControl.Paint += nodesOverlayControl_Paint;

            programNodesControl.Invalidate();

            foreach (Node node in program.nodes.Values)
            {
                if (node.designer.control is NodeDesignerControl)
                {
                    NodeDesignerControl ndc = (NodeDesignerControl)node.designer.control;
                    ndc.UpdatePinDesigners();
                }
            }
        }

        protected override void Dispose(bool disposing)
        {
            programNodesControl.AutoScrollPosition = new Point(0, 0);

            program.DestroyDesigner();
            

            if (selPen != null)
            {
                selPen.Dispose();
                selPen = null;
            }

            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #endregion

        #region Events

        public event EventHandler SelectionChanged;

        private void OnSelectionChanged()
        {
            cutBarButtonItem.Enabled = selectedNodes.Count > 0;
            copyBarButtonItem.Enabled = cutBarButtonItem.Enabled;

            CheckClipboardData();

            if (SelectionChanged != null)
                SelectionChanged(this, EventArgs.Empty);
        }

        #endregion

        #region Methods

        public void NodeSelectionChanged()
        {
            deleteBarButtonItem.Enabled = selectedNodes.Count > 0;
            OnSelectionChanged();
        }

        private void DeleteSelection(bool prompt = true)
        {
            if (selectedNodes.Count > 0)
            {
                bool doDelete = true;
                if (prompt)
                    doDelete = XtraMessageBox.Show("Are you sure you want to delete selected nodes?", "Program",
                        MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) ==
                               DialogResult.Yes;
                if (doDelete)
                {
                    while (selectedNodes.Count > 0)
                    {
                        Node csNode = selectedNodes[0];

                        List<string> tKeys = csNode.inPropConns.Keys.ToList();

                        foreach (string icName in tKeys)
                        {
                            csNode.DisconnectInProperty(icName);
                        }

                        tKeys = csNode.outPropConns.Keys.ToList();
                        foreach (string ocName in tKeys)
                        {
                            csNode.DisconnectOutProperty(ocName);
                        }

                        csNode.DestroyDesigner();
                        selectedNodes.RemoveAt(0);
                        csNode.program.nodes.Remove(csNode.id);
                        csNode.Dispose();
                    }

                    NodeSelectionChanged();
                }

                Invalidate(true);
            }
        }

        private List<Node> FindNodesAtPoint(Point tp)
        {            
            List<Node> fNodes = new List<Node>();

            foreach (Node cn in program.nodes.Values)
            {
                NodeDesigner nd = (NodeDesigner)cn.designer;
                NodeDesignerControl ndCont = (NodeDesignerControl)nd.Control;

                if (ndCont.Bounds.Contains(tp))
                {
                    fNodes.Add(cn);
                }
            }

            return fNodes;
        }


        private List<Node> FindNodesInRect(Rectangle rect)
        {
            List<Node> fNodes = new List<Node>();

            foreach (Node cn in program.nodes.Values)
            {
                NodeDesigner nd = (NodeDesigner)cn.designer;
                NodeDesignerControl ndCont = (NodeDesignerControl)nd.Control;

                if (rect.IntersectsWith(ndCont.Bounds))
                {
                    fNodes.Add(cn);
                }                
            }

            return fNodes;
        }

        public void DeselectAll()
        {
            while (selectedNodes.Count > 0)
            {
                Node cn = selectedNodes[0];
                
                selectedNodes.RemoveAt(0);
                if (cn.designer.control is NodeDesignerControl)
                {
                    NodeDesignerControl cnDesCont = ((NodeDesignerControl) cn.designer.control);
                    cnDesCont.isSelected = false;
                    cnDesCont.OnSelectedChanged();
                    cnDesCont.Cursor = Cursors.Hand;
                    cnDesCont.toolTipController.HideHint();
                }
            }

            NodeSelectionChanged();
        }

        #endregion

        #region Internal Events


        void nodesOverlayControl_Paint(object sender, PaintEventArgs e)
        {
            if (doRectSelect)
            {
                //Rectangle drRect = programNodesControl.overlayControl.ClientRectangle;
                //e.Graphics.DrawRectangle(Pens.Red, 10, 10, drRect.Width - 20, drRect.Height - 20);

                e.Graphics.DrawRectangle(selPen, selRect);
            }
        }

        private void addNodeBarButtonItem_ItemClick(object sender, ItemClickEventArgs e)
        {
            Node node = new Node(program);
            node.id = program.GetNewNodeID();
            node.designerData.bounds.Location = popupMenuPoint;
            program.nodes.Add(node.id, node);
            //program.currentNodeID++;
            node.CreateDesigner();
            ((NodeDesigner) node.designer).programDesignerControl = this;
            programNodesControl.Controls.Add(node.designer.control);
            DeselectAll();
            selectedNodes.Add(node);
            ((NodeDesignerControl) ((NodeDesigner) node.designer).control).isSelected = true;
            ((NodeDesignerControl) ((NodeDesigner) node.designer).control).OnSelectedChanged();
            NodeSelectionChanged();
        }

        private void xtraScrollableControl_MouseUp(object sender, MouseEventArgs e)
        {
            if (hConnOver != null)
            {
                Cursor = Cursors.Default;
                hConnOver = null;
                srcHandOver = false;
                targHandOver = false;
            }

            
            pastePoint = e.Location;
            //pastePoint = new Point(e.X - programNodesControl.AutoScrollPosition.X, e.Y - programNodesControl.AutoScrollPosition.Y);                

            CheckClipboardData();

            List<Node> fNodes;

            if (program.designer != null)
                program.designer.availableConnPins.Clear();

            programNodesControl.connsControl.Visible = true;

            if (connSourcePin != null)
            {
                fNodes = FindNodesAtPoint(e.Location);

                if (fNodes.Count > 0)
                {
                    Node oNode = fNodes[0];
                    NodeDesignerControl oNdCont = (NodeDesignerControl)oNode.designer.control;
                    oNdCont.pinOver = oNdCont.FindPinAtPoint(new Point(e.X - oNdCont.Left, e.Y - oNdCont.Top));
                    if (oNdCont.pinOver != null)
                    {
                        connTargetPin = oNdCont.pinOver;
                        if (programNodesControl.connsControl.tempConnEnabled)
                        {
                            if (connSourcePin.type == NodeDesignerPinType.Property && connTargetPin.type == NodeDesignerPinType.Property)
                            {


                                NodeConnectionProperty ncProp = new NodeConnectionProperty(program, connSourcePin.node, connTargetPin.node, connSourcePin.node.GetType().GetProperty(connSourcePin.name), connTargetPin.node.GetType().GetProperty(connTargetPin.name));
                                ncProp.sourceDesignerPin = connSourcePin;
                                ncProp.targetDesignerPin = connTargetPin;
                                ncProp.id = program.GetNewConnectionID();

                                ncProp.sourceHandlePoint = programNodesControl.connsControl.srcHandPoint;
                                ncProp.targetHandlePoint = programNodesControl.connsControl.targHandPoint;

                                

                                program.connections.Add(ncProp.id, ncProp);
                                
                                //connSourcePin.connPins.Add(connTargetPin.id, );

                                if (!connSourcePin.node.outPropConns.ContainsKey(connSourcePin.name))
                                    connSourcePin.node.outPropConns.Add(connSourcePin.name, new SortedDictionary<int, NodeConnectionProperty>());
                                

                                connSourcePin.node.outPropConns[connSourcePin.name].Add(ncProp.id, ncProp);
                                
                                connTargetPin.node.DisconnectInProperty(connTargetPin.name);
                                
                                connTargetPin.node.inPropConns.Add(connTargetPin.name, ncProp);
                                connTargetPin.node.OnInputPinConnected(connTargetPin.name);

                                ncProp.enableDesigner = ncProp.sourceDesignerPin != null && ncProp.targetDesignerPin != null;


                                ncProp.Update();
                            }
                        }
                    }
                }

                connSourcePin = null;
                connTargetPin = null;
                programNodesControl.connsControl.tempVisible = false;
                programNodesControl.connsControl.Invalidate();
            }
            
            if (doRectSelect)
            {
                fNodes = FindNodesInRect(selRect);                

                DeselectAll();


                foreach (Node cn in fNodes)
                {
                    selectedNodes.Add(cn);
                    ((NodeDesignerControl) ((NodeDesigner) cn.designer).control).isSelected = true;
                    ((NodeDesignerControl) ((NodeDesigner) cn.designer).control).OnSelectedChanged();
                }

                doRectSelect = false;

                OnSelectionChanged();

                programNodesControl.overlayControl.Visible = false;                

                programNodesControl.Invalidate(true);
            }

            if (mouseDownPoint == e.Location && e.Button == MouseButtons.Right)
            {
                popupMenuPoint = e.Location;
                popupMenu.ShowPopup(Cursor.Position);
            }

            if (e.Button == MouseButtons.Right)
            {
                prevScrollPos = programNodesControl.AutoScrollPosition;
                programNodesControl.previewVisible = true;
                programNodesControl.Invalidate(true);                
            }

            programNodesControl.UpdatePreview();
            Invalidate(true);
            
        }

        private void xtraScrollableControl_MouseDown(object sender, MouseEventArgs e)
        {
            

            CheckClipboardData();

            programNodesControl.Focus();

            if (e.Button == MouseButtons.Right)
            {
                mdScrollPos = prevScrollPos;

                mouseDownPoint = e.Location;

                programNodesControl.previewVisible = false;
                programNodesControl.Invalidate();
            }

            if (e.Button == MouseButtons.Left)
            {
                mouseDownPoint = e.Location;

                if (hConnOver != null)
                {
                    if (srcHandOver)
                    {
                        mdHandPoint = hConnOver.sourceHandlePoint;
                    }
                    else if (targHandOver)
                    {
                        mdHandPoint = hConnOver.targetHandlePoint;
                    }
                }
                else
                {
                    pastePoint = e.Location;

                    if (!ProjectDesignerControl.ctrlDown && !ProjectDesignerControl.altDown && !ProjectDesignerControl.shiftDown)
                    {

                        DeselectAll();
                        programNodesControl.Invalidate(true);
                    }                    
                }

                
                mouseDownPoint = e.Location;
            }
        }

        public void NodesControlMouseMove(MouseEventArgs e)
        {
            xtraScrollableControl_MouseMove(programNodesControl, e);
        }

        public void NodesControlMouseUp(MouseEventArgs e)
        {
            xtraScrollableControl_MouseUp(programNodesControl, e);
            if (e.Button == MouseButtons.Right)
            {
                
                popupMenu.ShowPopup(Cursor.Position);
            }
        }

        public void NodesControlMouseDown(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                popupMenu.ShowPopup(Cursor.Position);
            }
        }

        private void xtraScrollableControl_MouseMove(object sender, MouseEventArgs e)
        {
            Point mdiff = new Point(e.X - mouseDownPoint.X, e.Y - mouseDownPoint.Y);

            if (programNodesControl.connsControl.tempVisible)
            {
                programNodesControl.connsControl.tempVisible = false;                
            }

            


            if (editMoveEnabled)
            {
                
                if (e.Button == MouseButtons.Left)
                {
                    if (srcHandOver)
                    {
                        hConnOver.sourceHandlePoint = new Point(mdHandPoint.X + mdiff.X, mdHandPoint.Y + mdiff.Y);
                        programNodesControl.connsControl.Invalidate();
                        return;
                    }
                    if (targHandOver)
                    {
                        hConnOver.targetHandlePoint = new Point(mdHandPoint.X + mdiff.X, mdHandPoint.Y + mdiff.Y);
                        programNodesControl.connsControl.Invalidate();
                        return;
                    }                
                }
               
            }

            hConnOver = null;
            srcHandOver = false;
            targHandOver = false;

            if (editMoveEnabled) { 
                foreach (Node selNode in selectedNodes)
                {
                    Point mloc = new Point(e.X - programNodesControl.AutoScrollPosition.X, e.Y - programNodesControl.AutoScrollPosition.Y);
                    foreach (NodeConnectionProperty ncp in selNode.inPropConns.Values)
                    {
                        if (ncp.sourceHandleRect.Contains(mloc))
                        {
                            hConnOver = ncp;
                            srcHandOver = true;
                            break;
                        }
                        if (ncp.targetHandleRect.Contains(mloc))
                        {
                            hConnOver = ncp;
                            targHandOver = true;
                            break;
                        }
                    }
                    if (!srcHandOver && !targHandOver)
                    {
                        foreach (string opk in selNode.outPropConns.Keys)
                        {
                            SortedDictionary<int, NodeConnectionProperty> opcs = selNode.outPropConns[opk];
                            foreach (NodeConnectionProperty ncp in opcs.Values)
                            {
                                if (ncp.sourceHandleRect.Contains(mloc))
                                {
                                    hConnOver = ncp;
                                    srcHandOver = true;
                                    break;
                                }
                                if (ncp.targetHandleRect.Contains(mloc))
                                {
                                    hConnOver = ncp;
                                    targHandOver = true;
                                    break;
                                }
                            }
                        }
                    }
                }


                if (srcHandOver || targHandOver)
                {
                    Cursor = Cursors.SizeAll;
                    return;
                }
                else
                {
                    Cursor = Cursors.Default;
                }
            }

            

            if (e.Button == MouseButtons.Right)
            {
                Point scrollPos = new Point(-mdiff.X - mdScrollPos.X, -mdiff.Y - mdScrollPos.Y);
                programNodesControl.AutoScrollPosition = scrollPos;
                programNodesControl.UpdatePreview();
                //Debug.WriteLine(scrollPos);
                prevScrollPos = programNodesControl.AutoScrollPosition;
                //programNodesControl.Invalidate();
            }
            else if (e.Button == MouseButtons.Left)
            {
                if (connSourcePin != null)
                {
                    Point sp = connSourcePin.pos;                    
                    Point dp = e.Location;

                    sp.X += connSourcePin.node.designer.control.Left;
                    sp.Y += connSourcePin.node.designer.control.Top;
                    
                    
                    List<Node> fNodes = FindNodesAtPoint(e.Location);
                                        
                    if (fNodes.Count > 0)
                    {
                        Node oNode = fNodes[0];

                        NodeDesignerControl oNdCont = (NodeDesignerControl) oNode.designer.control;
                        oNdCont.pinOver = oNdCont.FindPinAtPoint(new Point(e.X - oNdCont.Left, e.Y - oNdCont.Top));
                        if (oNdCont.pinOver != null)
                        {
                            connTargetPin = oNdCont.pinOver;



                            bool connEnable = false;
                            if (connTargetPin.node != connSourcePin.node)
                            {
                                if (connTargetPin.pinType != null)
                                {
                                    if (connTargetPin.pinType.IsAssignableFrom(connSourcePin.pinType))
                                    {
                                        if ((connSourcePin.isOutput && !connTargetPin.isOutput) ||
                                                    (!connSourcePin.isOutput && connTargetPin.isOutput))
                                        {
                                            connEnable = true;
                                        }
                                    }
                                    else if (connSourcePin.type == NodeDesignerPinType.Property)
                                    {
                                        if (connTargetPin.type == NodeDesignerPinType.Property)
                                        {
                                            if (connSourcePin.pinType.FullName == connTargetPin.pinType.FullName)
                                            {
                                                if ((connSourcePin.isOutput && !connTargetPin.isOutput) ||
                                                    (!connSourcePin.isOutput && connTargetPin.isOutput))
                                                {
                                                    connEnable = true;
                                                }
                                            }
                                        }
                                    }                                    
                                }
                            }

                            string spInfo = connSourcePin.type + "(" + connSourcePin.name + ", " +
                                            connSourcePin.pinType.Name + ")";
                            string tpInfo = connTargetPin.type + "(" + connTargetPin.name + ", " +
                                            connSourcePin.pinType.Name + ")";

                            if (connEnable)
                            {
                                programNodesControl.connsControl.tempConnEnabled = true;
                                programNodesControl.connsControl.tempConnInfo = spInfo + " > " + tpInfo;
                            }
                            else
                            {                                                                
                                connTargetPin = null;
                                programNodesControl.connsControl.tempConnEnabled = false;
                                programNodesControl.connsControl.tempConnInfo = spInfo + " X " + tpInfo;
                            }

                            if (connEnable)
                            {
                                dp = connTargetPin.pos;
                                dp.X += connTargetPin.node.designer.control.Left;
                                dp.Y += connTargetPin.node.designer.control.Top;
                            }
                        }
                        else
                        {
                            string spInfo = connSourcePin.type + "(" + connSourcePin.name + ", " + connSourcePin.pinType.Name + ")";
                            programNodesControl.connsControl.tempConnInfo = spInfo;
                            programNodesControl.connsControl.tempConnEnabled = false;
                        }
                    }
                    else
                    {
                        string spInfo = connSourcePin.type + "(" + connSourcePin.name + ", " + connSourcePin.pinType.Name + ")";
                        programNodesControl.connsControl.tempConnInfo = spInfo;
                        programNodesControl.connsControl.tempConnEnabled = false;
                    }

                    programNodesControl.connsControl.Visible = true;
                    programNodesControl.connsControl.tempSourcePos = sp;
                    programNodesControl.connsControl.tempTargetPos = dp;
                    programNodesControl.connsControl.tempVisible = true;
                } else if (selectedNodes.Count == 0){

                    programNodesControl.overlayControl.Location = new Point();

                    if (!doRectSelect)
                    {
                        selRect = new Rectangle(e.Location, new Size());
                        doRectSelect = true;

                        programNodesControl.connsControl.Visible = false;
                        programNodesControl.overlayControl.Visible = true;

                        //Debug.WriteLine(programNodesControl.overlayControl.Location);

                        programNodesControl.overlayControl.DoOverlayBackUpdate();

                    }
                    if (doRectSelect)
                    {
                        if (mdiff.X > 0)
                        {
                            selRect.Width = mdiff.X;
                        }
                        else
                        {
                            selRect.X = mouseDownPoint.X + mdiff.X;
                            selRect.Width = -mdiff.X;
                        }
                        if (mdiff.Y > 0)
                        {
                            selRect.Height = mdiff.Y;
                        }
                        else
                        {
                            selRect.Y = mouseDownPoint.Y + mdiff.Y;
                            selRect.Height = -mdiff.Y;
                        }
                        programNodesControl.overlayControl.Invalidate();
                    }                                    
                }
                programNodesControl.connsControl.Invalidate();

            }
            else if (e.Button == MouseButtons.None)
            {
                foreach (Node cn in program.nodes.Values)
                {
                    NodeDesignerControl ndc = (NodeDesignerControl) cn.designer.control;
                    ndc.toolTipController.HideHint();
                }
            }
        }

        internal void programNodesControl_KeyDown(object sender, KeyEventArgs e)
        {
            if (!ProjectDesignerControl.ctrlDown && !ProjectDesignerControl.altDown && !ProjectDesignerControl.shiftDown)
            {
                if (e.KeyCode == Keys.Home)
                {
                    programNodesControl.AutoScrollPosition = new Point();
                    programNodesControl.Invalidate(true);
                }
                else if (e.KeyCode == Keys.Delete)
                {
                    DeleteSelection();
                }
            }
        }
        private void deleteBarButtonItem_ItemClick(object sender, ItemClickEventArgs e)
        {
            DeleteSelection();
        }

        #endregion

        #region Properties

        public List<Node> SelectedNodes
        {
            get
            {
                return selectedNodes;
            }
        }

        #endregion

        #region Drag

        private void programNodesControl_DragOver(object sender, DragEventArgs e)
        {
            e.Effect = DragDropEffects.None;

            bool isTreeNode = false;
            foreach (string dataFormatName in e.Data.GetFormats())
            {
                if (dataFormatName.Contains("TreeListNode"))
                {
                    isTreeNode = true;
                    break;
                }
            }
            if (isTreeNode)
            {
                TreeListNode tlNode = (TreeListNode) e.Data.GetData(typeof (TreeListNode));
                if (tlNode.TreeList.Name == "nodesTreeList")
                {
                    e.Effect = DragDropEffects.Move;

                    Point dropPoint = programNodesControl.PointToClient(new Point(e.X, e.Y));
                    dropPoint.X += programNodesControl.AutoScrollPosition.X;
                    dropPoint.Y += programNodesControl.AutoScrollPosition.Y;

                    /*List<Node> fNodes = FindNodesAtPoint(dropPoint);
                    if (fNodes.Count > 0)
                    {
                        e.Effect = DragDropEffects.None;
                    }*/

                    //Debug.WriteLine(dropPoint);
                }
            }

        }

        private void programNodesControl_DragDrop(object sender, DragEventArgs e)
        {

            bool isTreeNode = false;
            foreach (string dataFormatName in e.Data.GetFormats())
            {
                if (dataFormatName.Contains("TreeListNode"))
                {
                    isTreeNode = true;
                    break;
                }
            }
            if (isTreeNode)
            {
                TreeListNode tlNode = (TreeListNode) e.Data.GetData(typeof (TreeListNode));
                if (tlNode.TreeList.Name == "nodesTreeList")
                {
                    DeselectAll();

                    Point dropPoint = programNodesControl.PointToClient(new Point(e.X, e.Y));

                    TreeListColumn nodeInfoColumn = tlNode.TreeList.Columns["NodeInfo"];
                    NodeInfo nInfo = (NodeInfo) tlNode.GetValue(nodeInfoColumn);
                    ConstructorInfo nConstInfo = nInfo.nodeType.GetConstructor(new Type[] {typeof (Program)});
                    Node newNode = (Node) nConstInfo.Invoke(new object[] {program});
                    newNode.category = nInfo.Category;
                    newNode.id = program.GetNewNodeID();
                    program.nodes.Add(newNode.id, newNode);
                    newNode.CreateDesigner();
                    newNode.designerData.bounds.Location = dropPoint;
                    newNode.designerData.bounds.Size = new Size(nInfo.DefaultWidth, nInfo.DefaultHeight);
                    programNodesControl.Controls.Add(newNode.designer.control);
                    ((NodeDesigner) newNode.designer).programDesignerControl = this;
                    NodeDesignerControl ndCont = ((NodeDesignerControl) newNode.designer.control);
                    ndCont.isSelected = true;
                    ndCont.OnSelectedChanged();
                    ndCont.lockSize = nInfo.lockSize;
                    ndCont.lockWidth = nInfo.lockWidth;
                    ndCont.lockHeight = nInfo.lockHeight;
                    selectedNodes.Clear();
                    selectedNodes.Add(newNode);
                    NodeSelectionChanged();
                    programNodesControl.Invalidate(true);
                    newNode.designer.control.Focus();
                }
            }
        }

        #endregion

        #region Copy / Paste


        public void DoCopy()
        {
            XmlDocument xDoc = new XmlDocument();
            XmlDeclaration xDec = xDoc.CreateXmlDeclaration("1.0", "UTF-8", "yes");
            xDoc.AppendChild(xDec);

            XmlElement projDataEl = xDoc.CreateElement("project_data");
            xDoc.AppendChild(projDataEl);

            Point bbMin = new Point(int.MaxValue, int.MaxValue);
            Point bbMax = new Point(int.MinValue, int.MinValue);


            foreach (Node selNode in selectedNodes)
            {
                Rectangle bRect = selNode.designerData.bounds;
                if (bRect.X < bbMin.X)
                    bbMin.X = bRect.X;
                if (bRect.Y < bbMin.Y)
                    bbMin.Y = bRect.Y;
                if (bRect.Right > bbMax.X)
                    bbMax.X = bRect.Right;
                if (bRect.Bottom > bbMax.Y)
                    bbMax.Y = bRect.Bottom;
            }

            XmlElement nbbEl = xDoc.CreateElement("bb_data");
            projDataEl.AppendChild(nbbEl);

            nbbEl.SetAttribute("min_x", bbMin.X.ToString());
            nbbEl.SetAttribute("min_y", bbMin.Y.ToString());
            nbbEl.SetAttribute("max_x", bbMax.X.ToString());
            nbbEl.SetAttribute("max_y", bbMax.Y.ToString());

            XmlElement nodesEl = xDoc.CreateElement("nodes");
            projDataEl.AppendChild(nodesEl);



            foreach (Node selNode in selectedNodes)
            {
                selNode.PutXml(nodesEl);
            }

            SortedDictionary<int, NodeConnectionProperty> nConns = new SortedDictionary<int, NodeConnectionProperty>();
            foreach (Node selNode in selectedNodes)
            {
                foreach (NodeConnectionProperty incp in selNode.inPropConns.Values)
                {
                    if (selectedNodes.Contains(incp.source))
                    {
                        if (!nConns.ContainsKey(incp.id))
                        {
                            nConns.Add(incp.id, incp);
                        }
                    }
                }
                foreach (string opn in selNode.outPropConns.Keys)
                {
                    SortedDictionary<int, NodeConnectionProperty> ocps = selNode.outPropConns[opn];
                    foreach (NodeConnectionProperty oncp in ocps.Values)
                    {
                        if (selectedNodes.Contains(oncp.target))
                        {
                            if (!nConns.ContainsKey(oncp.id))
                            {
                                nConns.Add(oncp.id, oncp);
                            }
                        }
                    }
                }
            }

            XmlElement connsEl = xDoc.CreateElement("connections");
            projDataEl.AppendChild(connsEl);

            foreach (int cncid in nConns.Keys)
            {
                NodeConnectionProperty cncp = nConns[cncid];
                XmlElement connEl = Misc.CreateAppendXmlElement(connsEl, "connection");
                cncp.PutXml(connEl);
                connsEl.AppendChild(connEl);
            }

            Clipboard.SetData("pxc_project_data", Misc.XmlDocumentToString(xDoc));
            pasteAvailable = true;
        }

        private Bitmap pasteNodesBitmap;
        private List<NodeDesignerControl> pastedControls = new List<NodeDesignerControl>();
        private bool doPaste = false;
        private Bitmap nodesContBitmap;

        public void DoPaste(Point pastePos)
        {
            CheckClipboardData();

            if (pasteAvailable)
            {
                DeselectAll();

                string pdXmlStr = Clipboard.GetData("pxc_project_data").ToString();

                XmlDocument xDoc = Misc.XmlDocumentFromXmlString(pdXmlStr);

                XmlElement pdEl = xDoc["project_data"];

                XmlElement bbEl = pdEl["bb_data"];

                Point bbMin = new Point(Conv.StringToInt(bbEl.GetAttribute("min_x"), 0), Conv.StringToInt(bbEl.GetAttribute("min_y"), 0));
                Point bbMax = new Point(Conv.StringToInt(bbEl.GetAttribute("max_x"), 0), Conv.StringToInt(bbEl.GetAttribute("max_y"), 0));

                Rectangle bbRect = new Rectangle(bbMin, new Size(bbMax.X - bbMin.X, bbMax.Y - bbMin.Y));

                XmlElement nodesEl = pdEl["nodes"];

                SortedDictionary<int, Node> pastedNodes = new SortedDictionary<int, Node>();
                SortedDictionary<int, int> oidNodes = new SortedDictionary<int, int>();

                

                pastedControls.Clear();
                

                foreach (XmlElement nEl in nodesEl.ChildNodes)
                {
                    Node npNode = Node.CreteFromXml(program, nEl);
                    npNode.Tag = npNode.id;
                    npNode.id = program.GetNewNodeID();

                    npNode.designerData.bounds.X -= bbRect.X;
                    npNode.designerData.bounds.Y -= bbRect.Y;


                    npNode.designerData.bounds.X += pastePos.X;
                    npNode.designerData.bounds.Y += pastePos.Y;
                    
                    pastedNodes.Add(npNode.id, npNode);
                    oidNodes.Add((int)npNode.Tag, npNode.id);
                }


                foreach (int pnid in pastedNodes.Keys)
                {
                    Node cn = pastedNodes[pnid];
                    program.nodes.Add(pnid, cn);
                    cn.CreateDesigner();

                    NodeDesigner nDes = (NodeDesigner)cn.designer;
                    nDes.programDesignerControl = this;

                    programNodesControl.Controls.Add(cn.designer.control);
                    pastedControls.Add((NodeDesignerControl)cn.designer.control);

                    NodeDesignerControl ndCont = (NodeDesignerControl) nDes.Control;
                    ndCont.useAntiAlias = false;
                    ndCont.RefreshPins();
                    ndCont.UpdatePinDesigners();

                    ndCont.isSelected = true;
                }

                
                selectedNodes = pastedNodes.Values.ToList();

                SortedDictionary<int, NodeConnection> nConns = new SortedDictionary<int, NodeConnection>();

                XmlElement connsEl = pdEl["connections"];
                foreach (XmlElement connEl in connsEl.ChildNodes)
                {
                    int snid = Conv.StringToInt(Misc.GetChildElementXml(connEl, "src_nid"), -1);
                    int tnid = Conv.StringToInt(Misc.GetChildElementXml(connEl, "targ_nid"), -1);

                    connEl["src_nid"].InnerText = oidNodes[snid].ToString();
                    connEl["targ_nid"].InnerText = oidNodes[tnid].ToString();

                    connEl["id"].InnerText = program.GetNewConnectionID().ToString();

                    NodeConnection conn = NodeConnection.CreateFromXml(program, connEl);
                    if (conn != null)
                    {
                        bool addConn = true;
                        if (conn is NodeConnectionProperty)
                        {
                            if (((NodeConnectionProperty)conn).targetPropInfo == null)
                            {
                                program.connections.Remove(conn.id);
                                addConn = false;
                            }
                        }
                        if (addConn)
                        {
                            nConns.Add(conn.id, conn);
                        }
                    }
                }

                List<int> remConnIDs = new List<int>();


                bool hasTarget = false;
                foreach (NodeConnection nConn in nConns.Values)
                {
                    hasTarget = true;
                    if (nConn is NodeConnectionProperty)
                    {
                        NodeConnectionProperty npConn = (NodeConnectionProperty)nConn;

                        hasTarget = npConn.targetPropInfo != null && npConn.sourcePropInfo != null;

                        if (hasTarget)
                        {
                            if (!npConn.source.outPropConns[npConn.sourcePropInfo.Name].ContainsKey(npConn.id))
                            {
                                npConn.source.outPropConns[npConn.sourcePropInfo.Name].Add(nConn.id, (NodeConnectionProperty)nConn);
                            }

                            if (!npConn.target.inPropConns.ContainsKey(npConn.targetPropInfo.Name))
                            {
                                npConn.target.inPropConns.Add(npConn.targetPropInfo.Name, (NodeConnectionProperty)nConn);
                                npConn.target.OnInputPinConnected(npConn.targetPropInfo.Name);
                            }
                        }

                    }

                    if (hasTarget)
                    {
                        nConn.Update();
                    }
                    else
                    {
                        remConnIDs.Add(nConn.id);
                    }
                }

                foreach (int rcID in remConnIDs)
                {
                    NodeConnection nc = nConns[rcID];
                    if (nc is NodeConnectionProperty)
                    {
                        NodeConnectionProperty ncp = (NodeConnectionProperty)nc;
                        string spName = "";
                        if (ncp.sourcePropInfo != null)
                            spName = ncp.sourcePropInfo.Name;
                        if (ncp.source.outPropConns.ContainsKey(spName))
                        {
                            if (ncp.source.outPropConns[ncp.targetPropInfo.Name].ContainsKey(ncp.id))
                            {
                                ncp.source.outPropConns[ncp.targetPropInfo.Name].Remove(ncp.id);
                            }
                        }
                    }
                    program.connections.Remove(rcID);
                    nc.UpdateNodeReferences();
                }

                foreach (Node tn in pastedNodes.Values)
                {
                    ((NodeDesignerControl)tn.designer.control).RefreshPins();
                }

                OnSelectionChanged();


                Invalidate(true);
            }
        }

        public void CheckClipboardData()
        {
            pasteAvailable = Clipboard.ContainsData("pxc_project_data");
            pasteBarButtonItem.Enabled = pasteAvailable;
        }

        private void copyBarButtonItem_ItemClick(object sender, ItemClickEventArgs e)
        {
            DoCopy();            
        }

        private void pasteBarButtonItem_ItemClick(object sender, ItemClickEventArgs e)
        {
            DoPaste(pastePoint);
        }

        #endregion

        private void cutBarButtonItem_ItemClick(object sender, ItemClickEventArgs e)
        {
            DoCopy();
            DeleteSelection(false);
        }

        private void editEnabledBarCheckItem_CheckedChanged(object sender, ItemClickEventArgs e)
        {
            editMoveEnabled = editEnabledBarCheckItem.Checked;
            Invalidate(true);
        }

        private void nodeTypePickUserControl_DoHide(object sender, EventArgs e)
        {
            nodeTypePickUserControl.Visible = false;            
        }

        private Point cnPoint = new Point();

        private void programNodesControl_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (e.Clicks >= 2)
                {
                    cnPoint = e.Location;

                    nodeTypePickUserControl.Reset();
                    nodeTypePickUserControl.Location = new Point(e.X - nodeTypePickUserControl.Width / 2, e.Y - nodeTypePickUserControl.Height / 2);

                    if (nodeTypePickUserControl.Left < 0)
                        nodeTypePickUserControl.Left = 0;

                    if (nodeTypePickUserControl.Top < 0)
                        nodeTypePickUserControl.Top = 0;

                    if (nodeTypePickUserControl.Right > Width)
                        nodeTypePickUserControl.Left = Width - nodeTypePickUserControl.Width;

                    if (nodeTypePickUserControl.Bottom > programNodesControl.Height)
                        nodeTypePickUserControl.Top = programNodesControl.Height - nodeTypePickUserControl.Height;

                    nodeTypePickUserControl.Visible = true;
                    nodeTypePickUserControl.filterTextEdit.Focus();

                    return;
                }
            }
        }

        private void nodeTypePickUserControl_DoNodeSelect(object sender, EventArgs e)
        {
            nodeTypePickUserControl.Visible = false;
            NodeInfo nInfo = nodeTypePickUserControl.SelectedNodeType;
            if (nInfo != null)
            {
                ConstructorInfo nConstInfo = nInfo.nodeType.GetConstructor(new Type[] { typeof(Program) });
                Node newNode = (Node)nConstInfo.Invoke(new object[] { program });
                newNode.category = nInfo.Category;
                newNode.id = program.GetNewNodeID();
                program.nodes.Add(newNode.id, newNode);
                newNode.CreateDesigner();
                Point npos = cnPoint;
                    //nodeTypePickUserControl.Location;
                //npos.X += nodeTypePickUserControl.Width / 2;
                cnPoint.X -= nInfo.DefaultWidth / 2;
                cnPoint.Y -= nInfo.DefaultHeight / 2;

                if (cnPoint.X < 0)
                    cnPoint.X = 0;
                if (cnPoint.Y < 0)
                    cnPoint.Y = 0;
                


                newNode.designerData.bounds.Location = cnPoint;                

                newNode.designerData.bounds.Size = new Size(nInfo.DefaultWidth, nInfo.DefaultHeight);
                programNodesControl.Controls.Add(newNode.designer.control);
                ((NodeDesigner)newNode.designer).programDesignerControl = this;
                NodeDesignerControl ndCont = ((NodeDesignerControl)newNode.designer.control);
                ndCont.isSelected = true;
                ndCont.OnSelectedChanged();
                ndCont.lockSize = nInfo.lockSize;
                ndCont.lockWidth = nInfo.lockWidth;
                ndCont.lockHeight = nInfo.lockHeight;
                selectedNodes.Clear();
                selectedNodes.Add(newNode);
                NodeSelectionChanged();
                programNodesControl.Invalidate(true);
                newNode.designer.control.Focus();
            }
        }

        private void debugBarButtonItem_ItemClick(object sender, ItemClickEventArgs e)
        {

        }



    }
}
