using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using DevExpress.Utils;
using DevExpress.Utils.About;
using DevExpress.Utils.Crypt;
using DevExpress.Utils.Drawing.Helpers;
using DevExpress.XtraBars.ColorPick.Popup;
using DevExpress.XtraBars.Ribbon;
using PXP.Crawler.API.Model;
using PXP.Crawler.API.Utils;

namespace PXP.Crawler.API.Designers.Controls
{
    public class NodeDesignerControl : DesignerControl
    {
        #region Variables

        private Pen borderPen;
        private Pen borderSelPen;
        internal Node node;
        internal bool hasNode = false;
        internal GraphicsPath cPath;
        internal GraphicsPath selPath;
        internal GraphicsPath inPath;

        private Rectangle topRect;
        private Rectangle bottRect;
        private Rectangle leftRect;
        private Rectangle rightRect;


        protected internal bool topOver = false;
        protected internal bool bottOver = false;
        protected internal bool leftOver = false;
        protected internal bool rightOver = false;

        private Point borderMouseDownPos = new Point();
        private bool borderMouseDown = false;
        private Rectangle borderMouseDownRect = new Rectangle();
        private Point mouseDownPos = new Point();

        internal bool isSelected = false;

        private StringFormat nameStringFormat = new StringFormat(StringFormatFlags.NoWrap)
        {
            Alignment = StringAlignment.Center,
            LineAlignment = StringAlignment.Center,
            Trimming = StringTrimming.EllipsisCharacter
        };

        private Brush textBrush;
        private Brush textShBrush;
        private Brush backBrush;
        private Brush backSelBrush;

        private Font infoFont = new Font("Consolas", 10, GraphicsUnit.Pixel);

        internal bool lockSize = false;
        internal bool lockWidth = false;
        internal bool lockHeight = false;

        public List<NodeDesignerPin> pinsInput = new List<NodeDesignerPin>();
        public List<NodeDesignerPin> pinsOutput = new List<NodeDesignerPin>();
        internal ToolTipController toolTipController;
        private IContainer components;
        internal NodeDesignerPin pinOver = null;
        internal NodeDesignerPin mouseDownPin = null;

        internal bool mouseLeftDown = false;
        protected internal bool showTitle = true;
        protected internal bool titleTop = false;
        protected internal Point titlePositionAdjust = new Point();

        internal bool useAntiAlias = true;


        #endregion

        #region Constructor / Destructor

        public NodeDesignerControl()
        {
            Init();
        }

        public NodeDesignerControl(Node node)
        {
            this.node = node;
            hasNode = this.node != null;
            Init();
        }

        protected override void Dispose(bool disposing)
        {
            ClearPins();

            borderPen.Dispose();
            borderSelPen.Dispose();
            backBrush.Dispose();
            backSelBrush.Dispose();
            infoFont.Dispose();
            cPath.Dispose();
            inPath.Dispose();
            selPath.Dispose();
            if (textBrush != null)
            {
                textBrush.Dispose();
                textBrush = null;
            }
            if (textShBrush != null)
            {
                textShBrush.Dispose();
                textShBrush = null;
            }
            nameStringFormat.Dispose();
            base.Dispose(disposing);
        }

        #endregion

        #region Container

        #endregion

        #region Methods

        public void DoSelect()
        {
            ProgramDesignerControl prDesCont = ((NodeDesigner)node.designer).programDesignerControl;
            prDesCont.DeselectAll();
            prDesCont.selectedNodes.Add(node);
            isSelected = true;
            OnSelectedChanged();
            prDesCont.NodeSelectionChanged();
            prDesCont.Invalidate(true);
        }

        /*protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= 0x20;
                return cp;
            }
        }*/

        protected virtual void Init()
        {
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.StandardClick, true);
            SetStyle(ControlStyles.Selectable, false);
            SetStyle(ControlStyles.ResizeRedraw, true);
            SetStyle(ControlStyles.Opaque, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            //SetStyle(ControlStyles.SupportsTransparentBackColor, true);

            UpdateStyles();

            InitializeComponent();

            this.Cursor = System.Windows.Forms.Cursors.Hand;

            borderPen = new Pen(Brushes.Black, 1);
            borderPen.Alignment = PenAlignment.Outset;

            borderSelPen = new Pen(Color.FromArgb(200, Color.White), 1);
            borderSelPen.Alignment = PenAlignment.Center;
            borderSelPen.DashStyle = DashStyle.Solid;

            textBrush = new SolidBrush(ForeColor);
            textShBrush = new SolidBrush(Color.FromArgb(150, 0, 0, 0));

            //BackColor = Color.Transparent;

            NodeInfo nInfo = node.GetType().GetCustomAttribute<NodeInfo>();
            if (nInfo != null)
            {
                lockSize = nInfo.lockSize;
                lockWidth = nInfo.lockWidth;
                lockHeight = nInfo.lockHeight;
            }

            UpdateGraphics();

            RefreshPins();
        }

        public NodeDesignerPin FindPinAtPoint(Point testPoint)
        {
            foreach (NodeDesignerPin niPin in pinsInput)
            {
                if (niPin.pinRect.Contains(testPoint))
                {
                    return niPin;
                }
            }
            if (pinOver == null)
            {
                foreach (NodeDesignerPin noPin in pinsOutput)
                {
                    if (noPin.pinRect.Contains(testPoint))
                    {
                        return noPin;
                    }
                }
            }
            return null;
        }

        public NodeDesignerPin FindOutDesignerPin(string name)
        {
            foreach (NodeDesignerPin ndPin in pinsOutput)
            {
                if (ndPin.name == name)
                    return ndPin;
            }
            return null;
        }

        public NodeDesignerPin FindInDesignerPin(string name)
        {
            foreach (NodeDesignerPin ndPin in pinsInput)
            {
                if (ndPin.name == name)
                    return ndPin;
            }
            return null;
        }

        private void DrawPins(Graphics g)
        {
            if (!Visible)
                return;

            foreach (NodeDesignerPin niPin in pinsInput)
            {
                niPin.Draw(g);
            }

            foreach (NodeDesignerPin noPin in pinsOutput)
            {
                noPin.Draw(g);
            }
        }

        private void UpdatePinPositions()
        {

            float xs = ClientRectangle.X;// + node.designerData.cornerRadius / 2;
            float xe = ClientRectangle.Right;// - node.designerData.cornerRadius / 2;
            
            float xl = xe - xs;

            float pxd = xl / (float)(pinsInput.Count + 1);
            float hl = xl * 0.5f;
            float xc = xs + hl;
            float cx = xc;

            for (int ipi = 0; ipi < pinsInput.Count; ipi++)
            {
                cx = xs + hl - (pxd * (pinsInput.Count - 1) * 0.5f) + (pxd * ipi);
                NodeDesignerPin niPin = pinsInput[ipi];niPin.pos.X = (int)cx;
                niPin.pos.Y = ClientRectangle.Y + 4;
                niPin.UpdatePinBounds();
            }

            pxd = xl / (float)(pinsOutput.Count + 1);
            hl = xl * 0.5f;
            xc = xs + hl;
            cx = xc;

            for (int ipi = 0; ipi < pinsOutput.Count; ipi++)
            {
                cx = xs + hl - (pxd * (pinsOutput.Count - 1) * 0.5f) + (pxd * ipi);
                NodeDesignerPin noPin = pinsOutput[ipi];
                noPin.pos.X = (int)cx;
                noPin.pos.Y = ClientRectangle.Bottom - 5;
                noPin.UpdatePinBounds();
            }
        }

        public void UpdatePinDesigners()
        {

            foreach (NodeConnection nConn in node.program.FindConnectionByNode(node))
            {
                if (nConn is NodeConnectionProperty)
                {
                    NodeConnectionProperty npConn = (NodeConnectionProperty)nConn;
                    if (npConn.source == node)
                    {
                        npConn.sourceDesignerPin = FindOutDesignerPin(npConn.sourcePropInfo.Name);
                        if (npConn.targetDesignerPin == null)
                        {
                            if (npConn.target.designer != null)
                            {
                                //if (npConn.enableDesigner)
                                    npConn.targetDesignerPin = ((NodeDesignerControl) npConn.target.designer.control).FindInDesignerPin(npConn.targetPropInfo.Name);
                            }
                        }
                    }
                    else if (npConn.target == node)
                    {
                        //if (npConn.enableDesigner)
                            npConn.targetDesignerPin = FindInDesignerPin(npConn.targetPropInfo.Name);
                    }
                    npConn.enableDesigner = npConn.sourceDesignerPin != null && npConn.targetDesignerPin != null;
                }
            }
        }

        internal void ClearPins()
        {
            if (node.program.designer != null)
            {
                foreach (NodeDesignerPin ndp in pinsOutput)
                {
                    node.program.designer.outPins[ndp.pinType.GUID].Remove(ndp.id);
                }
                foreach (NodeDesignerPin ndp in pinsInput)
                {
                    node.program.designer.inPins[ndp.pinType.GUID].Remove(ndp.id);
                }                
            }

            pinsInput.Clear();
            pinsOutput.Clear();
        }

        private void ProcNodePropertyPin(NodePropertyPin nPropertyPin, PropertyInfo cpi, bool createStates)
        {
            bool isIn = false;
            bool isOut = false;

            if (nPropertyPin.direction == NodePinDirection.Auto)
            {
                isIn = cpi.CanWrite;
                isOut = cpi.CanRead;
            }
            else
            {
                isIn = nPropertyPin.direction == NodePinDirection.Input;
                isOut = nPropertyPin.direction == NodePinDirection.Output;
            }

            if (isIn)
            {

                NodeDesignerPin niPin = new NodeDesignerPin(this);
                niPin.id = node.program.designer.GetNewDesignerPinID();
                niPin.name = cpi.Name;
                niPin.info = "<b>Type: </b>" + cpi.PropertyType.Name.Replace(cpi.PropertyType.Namespace, "");
                niPin.type = NodeDesignerPinType.Property;
                niPin.pinType = cpi.PropertyType;
                pinsInput.Add(niPin);
                node.program.designer.AddPin(niPin.pinType, niPin);

                if (createStates)
                {
                    int ind = node.designerData.pinStates.Count;
                    object propValue = node.GetType().GetProperty(niPin.name).GetValue(node);
                    if (propValue == null)
                    {
                        propValue = "";
                    }
                    node.designerData.pinStates.Add(ind, new DesignerData.PinState()
                    {
                        index = ind,
                        Name = niPin.name,
                        Value = WebUtility.HtmlDecode(propValue.ToString())
                    });
                }

            }
            if (isOut)
            {
                NodeDesignerPin noPin = new NodeDesignerPin(this);
                noPin.id = node.program.designer.GetNewDesignerPinID(); noPin.isOutput = true;
                noPin.name = cpi.Name;
                noPin.info = "<b>Type: </b> " + cpi.PropertyType.Name.Replace(cpi.PropertyType.Namespace, "");
                noPin.type = NodeDesignerPinType.Property;
                noPin.pinType = cpi.PropertyType;
                pinsOutput.Add(noPin);
                node.program.designer.AddPin(noPin.pinType, noPin);
            }


        }

        public void RefreshPins()
        {
            ClearPins();

            bool noPinStates = node.designerData.pinStates.Count == 0;

            if (node is IPins)
            {
                IPins ip = (IPins) node;

                List<NodePin> pins = ip.Pins;
            }

            foreach (PropertyInfo cpi in node.GetType().GetProperties())
            {
                NodePropertyPin nPropertyPin = cpi.GetCustomAttribute<NodePropertyPin>(false);
                if (nPropertyPin != null)
                {
                    ProcNodePropertyPin(nPropertyPin, cpi, !noPinStates);
                }                
                
            }



            UpdatePinDesigners();


            UpdatePinPositions();

            Invalidate();
        }

        private GraphicsPath CreateRoundGraphicsPath(Rectangle rect, int borderSize, int radius)
        {
            int cradius = Misc.LimitValue(radius, 0, rect.Width / 2);

            Rectangle r = new Rectangle(rect.X, rect.Y, rect.Width - 1, rect.Height - 1);

            int bs = borderSize;
            r.X += bs;
            r.Y += bs;
            r.Width -= bs * 2;
            r.Height -= bs * 2;

            Rectangle tlc = new Rectangle(r.Left, r.Top, 2 * cradius,
                                  2 * cradius);
            Rectangle trc = tlc;
            trc.X = r.Right - 2 * cradius;
            Rectangle blc = tlc;
            blc.Y = r.Bottom - 2 * cradius;
            Rectangle brc = blc;
            brc.X = r.Right - 2 * cradius;

            Point[] n = new Point[] {
                new Point(tlc.Left, tlc.Bottom), tlc.Location, 
                new Point(tlc.Right, tlc.Top), trc.Location, 
                new Point(trc.Right, trc.Top), 
                new Point(trc.Right, trc.Bottom),
                new Point(brc.Right, brc.Top), 
                new Point(brc.Right, brc.Bottom), 
                new Point(brc.Left, brc.Bottom), 
                new Point(blc.Right, blc.Bottom), 
                new Point(blc.Left, blc.Bottom), blc.Location
            };

            GraphicsPath tPath = new GraphicsPath();
            tPath.StartFigure();
            tPath.AddArc(tlc, 180, 90);
            tPath.AddLine(n[2], n[3]);
            tPath.AddArc(trc, 270, 90);
            tPath.AddLine(n[5], n[6]);
            tPath.AddArc(brc, 0, 90);
            tPath.AddLine(n[8], n[9]);
            tPath.AddArc(blc, 90, 90);
            tPath.AddLine(n[11], n[0]);
            tPath.CloseFigure();

            return tPath;
        }

        #endregion

        #region Events

        public event EventHandler SelectedChanged;

        protected virtual internal void OnSelectedChanged()
        {
            if (SelectedChanged != null)
                SelectedChanged(this, EventArgs.Empty);
        }

        #endregion

        #region Properties

        [Browsable(false)]
        public bool IsSelected
        {
            get { return isSelected; }
        }

        [Browsable(false)]
        public bool ShowTitle
        {
            get { return showTitle; }
            set
            {
                showTitle = value;
                Invalidate();
            }
        }

        [Browsable(false)]
        public Point TitlePositionAdjust
        {
            get { return titlePositionAdjust; }
            set
            {
                titlePositionAdjust = value;
                Invalidate();
            }
        }

        #endregion

        #region Overrides

        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);
            if (node.designer != null)
            {
                ProgramDesignerControl prDesCont = ((NodeDesigner)node.designer).programDesignerControl;
                prDesCont.programNodesControl_KeyDown(prDesCont, new KeyEventArgs(e.KeyData));                
            }
        }

        protected override void OnLeave(EventArgs e)
        {
            toolTipController.HideHint();

            base.OnLeave(e);
        }


        public override Color ForeColor
        {
            get
            {
                return base.ForeColor;
            }
            set
            {
                base.ForeColor = value;
                if (textBrush != null)
                {
                    textBrush.Dispose();
                }
                textBrush = new SolidBrush(ForeColor);
                Invalidate();
            }
        }



        protected void InvalidateEx()
        {
            if (Parent == null)
                return;

            Rectangle rc = new Rectangle(this.Location, this.Size);

            Parent.Invalidate(rc, true);
        }

        private void UpdateGraphics()
        {
            cPath = CreateRoundGraphicsPath(new Rectangle(0, 0, node.designerData.bounds.Width, node.designerData.bounds.Height), 3, node.designerData.cornerRadius);
            selPath = CreateRoundGraphicsPath(new Rectangle(0, 0, node.designerData.bounds.Width, node.designerData.bounds.Height), 5, node.designerData.cornerRadius - 1);
            inPath = CreateRoundGraphicsPath(new Rectangle(0, 0, node.designerData.bounds.Width, node.designerData.bounds.Height), 10, node.designerData.cornerRadius - 2);

            Rectangle br = node.designerData.bounds;
            int rad = node.designerData.cornerRadius;

            int bs = rad;
            if (rad < 3)
                bs = 8 - rad;

            topRect = new Rectangle(0, 0, br.Width, rad + bs);
            bottRect = new Rectangle(0, br.Height - rad - bs, br.Width, rad + bs);
            leftRect = new Rectangle(0, 0, rad + bs, br.Height);
            rightRect = new Rectangle(br.Width - rad - bs, 0, rad + bs, br.Height);

            UpdatePinPositions();
        }

        protected override void OnLocationChanged(EventArgs e)
        {
            node.designerData.bounds.Location = Location;
            base.OnLocationChanged(e);
            UpdateGraphics();
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            node.designerData.bounds.Size = Size;
            base.OnSizeChanged(e);
            UpdateGraphics();
            //Debug.WriteLine("Node (" + node.Name + ") Size: " + Size.ToString());
        }

        public override Color BackColor
        {
            get { return base.BackColor; }
            set
            {
                base.BackColor = value;

                if (backBrush != null)
                {
                    backBrush.Dispose();
                    backSelBrush.Dispose();
                }

                backBrush = new SolidBrush(BackColor);
                
                float cf = 1.2f;
                backSelBrush = new SolidBrush(Color.FromArgb(Misc.LimitValue((int)(BackColor.R * cf), 0, 255), Misc.LimitValue((int)(BackColor.G * cf),0, 255), Misc.LimitValue((int)(BackColor.B * cf), 0, 255)));
                

                Invalidate();
            }
        }


        protected override void OnParentChanged(EventArgs e)
        {
            base.OnParentChanged(e);
            if (Parent != null)
            {
                Location = node.designerData.bounds.Location;
                Size = node.designerData.bounds.Size;
            }
        }

        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
            //pevent.Graphics.Clear(Color.Transparent);
            //base.OnPaintBackground(pevent);
            //pevent.Graphics.Clear(Parent.BackColor);
            //pevent.Graphics.Clear(Color.Transparent);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            e.Graphics.Clear(Parent.BackColor);
            //e.Graphics.Clear(Color.Transparent);

            //e.Graphics.Clear(Color.Transparent);
            //if (useAntiAlias)
                e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

            //e.Graphics.FillPath(BackColor, cPath);


            if (isSelected)
            {
                e.Graphics.FillPath(backSelBrush, cPath);


                /*borderSelPen.Width = 1;
                borderSelPen.Alignment = PenAlignment.Center;
                borderSelPen.Color = Color.FromArgb(40, Color.White);
                borderSelPen.DashStyle = DashStyle.Dash;
                e.Graphics.DrawPath(borderSelPen, inPath);*/

                borderSelPen.Width = 1;
                borderSelPen.Alignment = PenAlignment.Outset;
                borderSelPen.Color = Color.FromArgb(60, Color.White);
                borderSelPen.DashStyle = DashStyle.Solid;
                e.Graphics.DrawPath(borderSelPen, selPath);

                e.Graphics.DrawPath(borderPen, cPath);

            }
            else
            {
                e.Graphics.FillPath(backBrush, cPath);

                e.Graphics.DrawPath(borderPen, cPath);
            }




            Rectangle tClRect = ClientRectangle;
            Rectangle tClSrRect = ClientRectangle;
            if (showTitle)
            {

                tClSrRect.Inflate(-3, -3);

                string title = node.Name;

                if (node.description.Length > 0)
                {
                    title += " (" + node.description + ")";
                }

                if (titleTop)
                {
                    if (isSelected)
                    {
                        //tClSrRect.Inflate(-12, -9);
                    }
                    else
                    {
                        //tClSrRect.Inflate(-12, -6);                        
                    }

                    tClSrRect.Inflate(-12, -6);

                    nameStringFormat.LineAlignment = StringAlignment.Near;
                    nameStringFormat.Alignment = StringAlignment.Near;
                }
                else
                {
                    nameStringFormat.LineAlignment = StringAlignment.Center;
                    nameStringFormat.Alignment = StringAlignment.Center;                    
                }


                tClSrRect.X += titlePositionAdjust.X;
                tClSrRect.Y += titlePositionAdjust.Y;


                tClRect = tClSrRect;


                tClRect.X -= 1;
                tClRect.Y += 1;
                e.Graphics.DrawString(title, Font, textShBrush, tClRect, nameStringFormat);
                tClRect = tClSrRect;
                tClRect.X += 1;
                tClRect.Y += 1;
                e.Graphics.DrawString(title, Font, textShBrush, tClRect, nameStringFormat);
                tClRect = tClSrRect;
                tClRect.X -= 1;
                tClRect.Y += 0;
                e.Graphics.DrawString(title, Font, textShBrush, tClRect, nameStringFormat);
                tClRect = tClSrRect;
                tClRect.X += 1;
                tClRect.Y += 0;
                e.Graphics.DrawString(title, Font, textShBrush, tClRect, nameStringFormat);
                tClRect = tClSrRect;
                tClRect.X -= 1;
                tClRect.Y -= 1;
                e.Graphics.DrawString(title, Font, textShBrush, tClRect, nameStringFormat);
                tClRect = tClSrRect;
                tClRect.X += 1;
                tClRect.Y -= 1;
                e.Graphics.DrawString(title, Font, textShBrush, tClRect, nameStringFormat);

                e.Graphics.DrawString(title, Font, textBrush, tClSrRect, nameStringFormat);
            }



            if (ProjectDesignerControl.EnableDebug)
            {
                string diStr = "";

                if (titleTop)
                {
                    diStr += "ID: " + node.id.ToString();
                    diStr += ", Pos: " + node.designerData.bounds.Location.X + "; " + node.designerData.bounds.Location.Y;
                    diStr += ", Size: " + node.designerData.bounds.Size.Width + "x" + node.designerData.bounds.Size.Height;
                }
                else
                {
                    diStr += "ID:\t" + node.id.ToString();
                    diStr += "\nPos:\t" + node.designerData.bounds.Location.X + "; " + node.designerData.bounds.Location.Y;
                    diStr += "\nSize:\t" + node.designerData.bounds.Size.Width + "x" + node.designerData.bounds.Size.Height;                    
                }

                Point ddp = new Point(25, 10);

                nameStringFormat.Alignment = StringAlignment.Far;
                nameStringFormat.LineAlignment = StringAlignment.Near;
                Size isSize = Size.Truncate(e.Graphics.MeasureString(diStr, infoFont));

                ddp.X = tClRect.Width - isSize.Width;


                e.Graphics.DrawString(diStr, infoFont, Brushes.Black, ddp.X + 1, ddp.Y + 1);
                e.Graphics.DrawString(diStr, infoFont, Brushes.Black, ddp.X - 1, ddp.Y - 1);
                e.Graphics.DrawString(diStr, infoFont, Brushes.Black, ddp.X - 1, ddp.Y + 1);
                e.Graphics.DrawString(diStr, infoFont, Brushes.Black, ddp.X + 1, ddp.Y - 1);
                e.Graphics.DrawString(diStr, infoFont, Brushes.White, ddp.X, ddp.Y);
            }


            //e.Graphics.DrawString(node.designerData.bounds.Location.ToString() + "\n" + node.designerData.bounds.Size.ToString(), infoFont, Brushes.White, 5, 5);

            DrawPins(e.Graphics);



            //e.Graphics.DrawRectangle(Pens.Red, topRect);
            //e.Graphics.DrawRectangle(Pens.Red, leftRect);
        }

        #endregion

        #region Mouse

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            NodeDesignerPin prevPinOver = pinOver;
            pinOver = null;


            if (e.Button == MouseButtons.None || e.Button == MouseButtons.Left)
            {
                pinOver = FindPinAtPoint(e.Location);

            }

            if (e.Button == MouseButtons.None)
            {
                topOver = false;
                bottOver = false;
                leftOver = false;
                rightOver = false;
            }

            if (prevPinOver != pinOver)
            {
                if (pinOver != null)
                {
                    if (!borderMouseDown && e.Button != MouseButtons.Left)
                    {
                        Cursor = Cursors.Hand;
                        Point pPos = PointToScreen(pinOver.pos);
                        string tt = pinOver.info;
                        if (pinOver.type == NodeDesignerPinType.Property)
                        {
                            //if (pinOver.node.)
                            //{
                            PropertyInfo poPropInfo = pinOver.node.GetType().GetProperty(pinOver.name);
                            if (poPropInfo.CanRead)
                            {
                                object pval = poPropInfo.GetValue(pinOver.node);                                
                                if (pval != null)
                                {
                                    if (pval.GetType().GetInterface("IList") != null)
                                    {
                                        IList il = (IList) pval;
                                        if (il.Count > 0)
                                        {
                                           Type[] geArgs = il.GetType().GetGenericArguments();
                                            if (geArgs.Length > 0)
                                            {
                                                tt += "\n<b>Type: </b> List(" + il.GetType().GetGenericArguments().Single().Name + "\nCount: " + il.Count;
                                            }}
                                        else
                                        {
                                            tt += "\n<b>Value: </b> Empty List";

                                        }                                        

                                    } else
                                    {
                                        tt += "\n<b>Value: </b>" + pval.ToString().Replace(poPropInfo.PropertyType.Namespace, "").TrimStart('.');                                        
                                    }
                                }
                                else
                                {
                                    tt += "\n<b>Value: </b>Null";
                                }                                
                            }
                            //}
                        }
                        toolTipController.ShowHint(tt, pinOver.name, pPos);
                    }
                }
                else
                {
                    toolTipController.HideHint();
                }
            }

            ProgramDesignerControl pdCont = ((NodeDesigner)node.designer).programDesignerControl;
            if (e.Button == MouseButtons.Left && pdCont.connSourcePin != null && pdCont.connSourcePin != pinOver)
            {
                Point mPos = e.Location;
                mPos.X += Left;
                mPos.Y += Top;
                pdCont.NodesControlMouseMove(new MouseEventArgs(e.Button, e.Clicks, mPos.X, mPos.Y, e.Delta));
                return;
            }

            if (!isSelected)
                return;

            if (e.Button == MouseButtons.None && !lockSize && pinOver == null)
            {
                bool inCont = inPath.IsVisible(e.Location);
                bool inBord = false;

                if (!inCont)
                {
                    inBord = cPath.IsVisible(e.Location);
                }

                if (inCont)
                {
                    this.Cursor = Cursors.SizeAll;
                }
                else if (inBord)
                {
                    topOver = topRect.Contains(e.Location) && !lockHeight;
                    bottOver = bottRect.Contains(e.Location) && !lockHeight;
                    leftOver = leftRect.Contains(e.Location) && !lockWidth;
                    rightOver = rightRect.Contains(e.Location) && !lockWidth;

                    if (topOver && leftOver)
                    {
                        this.Cursor = Cursors.SizeNWSE;
                    }
                    else if (topOver && rightOver)
                    {
                        this.Cursor = Cursors.SizeNESW;
                    }
                    else if (bottOver && leftOver)
                    {
                        this.Cursor = Cursors.SizeNESW;
                    }
                    else if (bottOver && rightOver)
                    {
                        this.Cursor = Cursors.SizeNWSE;
                    }
                    else if (topOver || bottOver)
                    {
                        this.Cursor = Cursors.SizeNS;
                    }
                    else if (leftOver || rightOver)
                    {
                        this.Cursor = Cursors.SizeWE;
                    }
                    else
                    {
                        this.Cursor = Cursors.Default;
                    }
                }
                else
                {
                    this.Cursor = Cursors.Default;
                }
            }
            else if (e.Button == MouseButtons.Left && pinOver == null)
            {
                if (borderMouseDown && !lockSize)
                {
                    Point mpDiff = new Point(borderMouseDownPos.X - Cursor.Position.X,
                        borderMouseDownPos.Y - Cursor.Position.Y);

                    ProgramDesignerControl prDesCont = ((NodeDesigner)node.designer).programDesignerControl;
                    foreach (Node selNode in prDesCont.selectedNodes)
                    {
                        NodeDesignerControl ndCont = (NodeDesignerControl)((NodeDesigner)selNode.designer).control;
                        if (!ndCont.lockSize)
                        {
                            if (topOver && !ndCont.lockHeight)
                            {
                                selNode.designerData.bounds.Y = ndCont.borderMouseDownRect.Y - mpDiff.Y;
                                selNode.designerData.bounds.Height = ndCont.borderMouseDownRect.Height + mpDiff.Y;
                            }
                            if (bottOver && !ndCont.lockHeight)
                            {
                                selNode.designerData.bounds.Height = ndCont.borderMouseDownRect.Height - mpDiff.Y;
                            }
                            if (leftOver && !ndCont.lockWidth)
                            {
                                selNode.designerData.bounds.X = ndCont.borderMouseDownRect.X - mpDiff.X;
                                selNode.designerData.bounds.Width = ndCont.borderMouseDownRect.Width + mpDiff.X;
                            }
                            if (rightOver && !ndCont.lockWidth)
                            {
                                selNode.designerData.bounds.Width = ndCont.borderMouseDownRect.Width - mpDiff.X;
                            }

                            if (selNode.designerData.bounds.X < 15)
                                selNode.designerData.bounds.X = 15;
                            if (selNode.designerData.bounds.Y < 10)
                                selNode.designerData.bounds.Y = 10;

                            int minWidth = selNode.designerData.cornerRadius * 3 + 10 + Misc.LargetValue(pinsInput.Count, pinsOutput.Count) * 10;

                            if (selNode.designerData.bounds.Width < minWidth)
                                selNode.designerData.bounds.Width = minWidth;
                            if (selNode.designerData.bounds.Height < selNode.designerData.cornerRadius * 3 + 20)
                                selNode.designerData.bounds.Height = selNode.designerData.cornerRadius * 3 + 20;

                            if (!node.designerSizing)
                            {
                                node.designerSizing = true;
                                node.OnDesignerBeginSizing();
                            }

                            if (node is NodeContainer)
                            {
                                NodeContainer nCont = (NodeContainer) node;
                                nCont.nodeDesigner.nodeDesignerControl.desContainer.Visible = false;
                            }

                            ndCont.Location = selNode.designerData.bounds.Location;
                            ndCont.Size = selNode.designerData.bounds.Size;

                            ndCont.Invalidate();
                            
                        }
                        //ndCont.Parent.Invalidate(true);
                    }

                    //((NodeDesigner)node.designer).programDesignerControl.programNodesControl.UpdatePreview();
                }
                else
                {
                    if (isSelected)
                    {
                        Point cmpDiff = new Point(mouseDownPos.X - Cursor.Position.X, mouseDownPos.Y - Cursor.Position.Y);

                        node.designerData.bounds.X = borderMouseDownRect.X - cmpDiff.X;
                        node.designerData.bounds.Y = borderMouseDownRect.Y - cmpDiff.Y;

                        if (node.designerData.bounds.X < 13)
                            node.designerData.bounds.X = 13;

                        if (node.designerData.bounds.Y < 10)
                            node.designerData.bounds.Y = 10;


                        Location = node.designerData.bounds.Location;

                        ProgramDesignerControl prDesCont = ((NodeDesigner)node.designer).programDesignerControl;
                        foreach (Node selNode in prDesCont.selectedNodes)
                        {
                            if (selNode != node)
                            {
                                //if (selNode.designer is NodeDesigner)
                                //{
                                NodeDesignerControl ndCont =
                                    (NodeDesignerControl)((NodeDesigner)selNode.designer).control;

                                selNode.designerData.bounds.X = ndCont.borderMouseDownRect.X - cmpDiff.X;
                                selNode.designerData.bounds.Y = ndCont.borderMouseDownRect.Y - cmpDiff.Y;

                                if (selNode.designerData.bounds.X < 13)
                                    selNode.designerData.bounds.X = 13;

                                if (selNode.designerData.bounds.Y < 10)
                                    selNode.designerData.bounds.Y = 10;

                                ndCont.Location = selNode.designerData.bounds.Location;
                                //indCont.Invalidate();
                                //}
                            }
                        }

                        ((NodeDesigner)node.designer).programDesignerControl.programNodesControl.UpdatePreview();

                        //ProgramDesignerControl prDesCont = ((NodeDesigner) node.designer).programDesignerControl;
                        //prDesCont.Invalidate();
                    }
                }

                if (isSelected)
                {
                    ((NodeDesigner)node.designer).programDesignerControl.programNodesControl.connsControl.Invalidate();
                }
            }

            //cPath.IsVisible()
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);            

            borderMouseDownRect = node.designerData.bounds;
            borderMouseDownPos = Cursor.Position;

            mouseLeftDown = e.Button == MouseButtons.Left;

            ProgramDesignerControl prDesCont = ((NodeDesigner)node.designer).programDesignerControl;
            
            if (pinOver == null)
                prDesCont.NodesControlMouseDown(e);

            foreach (Node selNode in prDesCont.selectedNodes)
            {
                if (selNode != node)
                {
                    NodeDesignerControl ndCont = (NodeDesignerControl)((NodeDesigner)selNode.designer).control;
                    ndCont.borderMouseDownRect = selNode.designerData.bounds;
                    ndCont.borderMouseDownPos = Cursor.Position;
                    ndCont.mouseDownPos = Cursor.Position;
                    ndCont.borderMouseDown = false;
                }
            }
            mouseDownPos = Cursor.Position;
            mouseDownPin = null;

            if (e.Button == MouseButtons.Left)
            {
                toolTipController.HideHint();

                if (pinOver != null)
                {
                    mouseDownPin = pinOver;
                    borderMouseDown = false;

                    node.program.designer.UpdateAvailableConnPins(pinOver);                                       

                    ProgramDesignerControl pdCont = ((NodeDesigner)node.designer).programDesignerControl;

                    pdCont.Invalidate(true);

                    Cursor = Cursors.Cross;
                    if (pdCont.connSourcePin == null)
                    {
                        pdCont.connSourcePin = mouseDownPin;

                        if (pdCont.connSourcePin.pinType != null)
                        {
                            string spInfo = pdCont.connSourcePin.type + "(" + pdCont.connSourcePin.name + ", " +
                                            pdCont.connSourcePin.pinType.Name + ")";

                            pdCont.programNodesControl.connsControl.tempConnInfo = spInfo;
                        }
                        else
                        {
                            string spInfo = pdCont.connSourcePin.type + "(" + pdCont.connSourcePin.name + ")";

                            pdCont.programNodesControl.connsControl.tempConnInfo = spInfo;
                        }
                        pdCont.programNodesControl.connsControl.tempConnEnabled = false;
                    }
                }
                else if (isSelected)
                {
                    if (topOver | bottOver | leftOver | rightOver)
                    {
                        borderMouseDown = true;
                    }
                }
                else
                {
                    Focus();
                }
            }
            else if (e.Button == MouseButtons.Right)
            {
                if (pinOver != null)
                {
                    if (pinOver.isOutput)
                    {
                        node.DisconnectOutProperty(pinOver.name);
                    }
                    else
                    {
                        node.DisconnectInProperty(pinOver.name);                        
                    }
                    ProgramDesignerControl pdCont = ((NodeDesigner)node.designer).programDesignerControl;
                    pdCont.Invalidate(true);
                }
            }

            Invalidate();
        }

        protected override void OnMouseUp(MouseEventArgs e){
            base.OnMouseUp(e);
            borderMouseDown = false;

            node.designerSizing = false;
            node.OnDesignerEndSizing();

            if (node is NodeContainer)
            {
                NodeContainer nCont = (NodeContainer)node;
                nCont.nodeDesigner.nodeDesignerControl.desContainer.Visible = true;
            }

            if (e.Button == MouseButtons.Left)
            {

                mouseLeftDown = false;

                ProgramDesignerControl progDesCont = ((NodeDesigner)node.designer).programDesignerControl;
                if (progDesCont.connSourcePin != null)
                {
                    Point mPos = e.Location;
                    mPos.X += Left;
                    mPos.Y += Top;
                    progDesCont.NodesControlMouseUp(new MouseEventArgs(e.Button, e.Clicks, mPos.X, mPos.Y, e.Delta));
                    progDesCont.connSourcePin = null;
                }

                if (isSelected)
                {
                    if (ProjectDesignerControl.ctrlDown && !ProjectDesignerControl.altDown &&
                        !ProjectDesignerControl.shiftDown)
                    {
                        progDesCont.selectedNodes.Remove(node);
                        isSelected = false;
                        OnSelectedChanged();
                        toolTipController.HideHint();
                        progDesCont.NodeSelectionChanged();

                        progDesCont.Invalidate(true);
                    }
                }
                else
                {
                    if (cPath.IsVisible(e.Location))
                    {

                        if (!ProjectDesignerControl.ctrlDown && !ProjectDesignerControl.altDown &&
                            !ProjectDesignerControl.shiftDown)
                        {
                            progDesCont.DeselectAll();
                        }

                        progDesCont.selectedNodes.Add(node);
                        isSelected = true;
                        OnSelectedChanged();
                        progDesCont.NodeSelectionChanged();

                        progDesCont.Invalidate(true);

                    }
                }

                ProgramDesignerControl prDesCont = ((NodeDesigner)node.designer).programDesignerControl;
                foreach (Node selNode in prDesCont.selectedNodes)
                {
                    if (selNode != node)
                    {
                        NodeDesignerControl ndCont = (NodeDesignerControl)((NodeDesigner)selNode.designer).control;
                        ndCont.borderMouseDownRect = selNode.designerData.bounds;
                        ndCont.borderMouseDownPos = Cursor.Position;
                        ndCont.mouseDownPos = Cursor.Position;
                        ndCont.borderMouseDown = false;
                    }
                }
            }
        }

        #endregion

        #region Designer

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.toolTipController = new DevExpress.Utils.ToolTipController(this.components);
            this.SuspendLayout();
            // 
            // toolTipController
            // 
            this.toolTipController.AllowHtmlText = true;
            this.toolTipController.ShowShadow = false;
            this.toolTipController.ToolTipStyle = DevExpress.Utils.ToolTipStyle.Windows7;
            this.toolTipController.ToolTipType = DevExpress.Utils.ToolTipType.SuperTip;
            // 
            // NodeDesignerControl
            // 
            this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(130)))), ((int)(((byte)(130)))), ((int)(((byte)(130)))));
            this.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
            this.ForeColor = System.Drawing.Color.White;
            this.ResumeLayout(false);

        }

        #endregion
    }
}