using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
using System.ComponentModel;
/*
MIT License

Copyright (c) 2021 DebugST@crystal_lz

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
 */
/*
 * create: 2021-01-28
 * modify: 2021-03-02
 * Author: Crystal_lz
 * blog: http://st233.com
 * Gitee: https://gitee.com/DebugST
 * Github: https://github.com/DebugST
 */
namespace ST.Library.UI.NodeEditor
{
    /// <summary>
    /// STNode节点属性编辑器
    /// </summary>
    public class STNodePropertyGrid : Control
    {
        #region properties ==========

        private STNode _STNode;
        /// <summary>
        /// 当前显示的节点
        /// </summary>
        [Browsable(false)]
        public STNode STNode {
            get { return _STNode; }
        }

        private Color _ItemHoverColor = Color.FromArgb(50, 125, 125, 125);
        /// <summary>
        /// 获取或设置属性选项被鼠标悬停时候背景色
        /// </summary>
        [Description("获取或设置属性选项被鼠标悬停时候背景色")]
        public Color ItemHoverColor {
            get { return _ItemHoverColor; }
            set { _ItemHoverColor = value; }
        }

        private Color _ItemSelectedColor = Color.DodgerBlue;
        /// <summary>
        /// 获取或设置属性选项被选中时候背景色 当AutoColor被设置时此属性不能被设置
        /// </summary>
        [Description("获取或设置属性选项被选中时候背景色 当AutoColor被设置时此属性不能被设置")]
        public Color ItemSelectedColor {
            get { return _ItemSelectedColor; }
            set {
                if (this._AutoColor) return;
                if (value == _ItemSelectedColor) return;
                _ItemSelectedColor = value;
                this.Invalidate();
            }
        }

        private Color _ItemValueBackColor = Color.FromArgb(255, 80, 80, 80);
        /// <summary>
        /// 获取或设置属性选项值背景色
        /// </summary>
        [Description("获取或设置属性选项值背景色")]
        public Color ItemValueBackColor {
            get { return _ItemValueBackColor; }
            set {
                _ItemValueBackColor = value;
                this.Invalidate();
            }
        }

        private Color _TitleColor = Color.FromArgb(127, 0, 0, 0);
        /// <summary>
        /// 获取或设置默认标题背景色
        /// </summary>
        [Description("获取或设置默认标题背景色")]
        public Color TitleColor {
            get { return _TitleColor; }
            set {
                _TitleColor = value;
                if (!this._ShowTitle) return;
                this.Invalidate(m_rect_title);
            }
        }

        private Color _ErrorColor = Color.FromArgb(200, Color.Brown);
        /// <summary>
        /// 获取或设置属性设置错误时候提示信息背景色
        /// </summary>
        [Description("获取或设置属性设置错误时候提示信息背景色")]
        public Color ErrorColor {
            get { return _ErrorColor; }
            set { _ErrorColor = value; }
        }

        private Color _DescriptionColor = Color.FromArgb(200, Color.DarkGoldenrod);
        /// <summary>
        /// 获取或设置属性描述信息背景色
        /// </summary>
        [Description("获取或设置属性描述信息背景色")]
        public Color DescriptionColor {
            get { return _DescriptionColor; }
            set { _DescriptionColor = value; }
        }

        private bool _ShowTitle = true;
        /// <summary>
        /// 获取或设置是否显示节点标题
        /// </summary>
        [Description("获取或设置是否显示节点标题")]
        public bool ShowTitle {
            get { return _ShowTitle; }
            set {
                _ShowTitle = value;
                this.SetItemRectangle();
                this.Invalidate();
            }
        }

        private bool _AutoColor = true;
        /// <summary>
        /// 获取或设置是否根据STNode自动设置控件高亮颜色
        /// </summary>
        [Description("获取或设置是否根据STNode自动设置控件高亮颜色")]
        public bool AutoTitleColor {
            get { return _AutoColor; }
            set { _AutoColor = value; }
        }

        private bool _InfoFirstOnDraw;
        /// <summary>
        /// 获取或当节点被设置时候 是否优先绘制信息面板
        /// </summary>
        [Description("获取或设置当节点被设置时候 是否优先绘制信息面板"), DefaultValue(false)]
        public bool InfoFirstOnDraw {
            get { return _InfoFirstOnDraw; }
            set { _InfoFirstOnDraw = value; }
        }

        private bool _ReadOnlyModel;
        /// <summary>
        /// 获取或设置当前属性编辑器是否处于只读模式
        /// </summary>
        [Description("获取或设置当前属性编辑器是否处于只读模式"), DefaultValue(false)]
        public bool ReadOnlyModel {
            get { return _ReadOnlyModel; }
            set { _ReadOnlyModel = value; }
        }

        /// <summary>
        /// 获取当前滚动条高度
        /// </summary>
        [Description("获取当前滚动条高度")]
        public int ScrollOffset { get { return m_nOffsetY; } }

        #endregion

        #region protected fields ==========

        /// <summary>
        /// 作者链接地址区域
        /// </summary>
        protected Rectangle m_rect_link;
        /// <summary>
        /// 查看帮助按钮区域
        /// </summary>
        protected Rectangle m_rect_help;
        /// <summary>
        /// 编辑器标题区域
        /// </summary>
        protected Rectangle m_rect_title;
        /// <summary>
        /// 面板切换按钮区域
        /// </summary>
        protected Rectangle m_rect_switch;

        /// <summary>
        /// 控件在绘制过程中使用的垂直滚动偏移
        /// </summary>
        protected int m_nOffsetY;
        /// <summary>
        /// 保存的信息面板垂直滚动偏移
        /// </summary>
        protected int m_nInfoOffsetY;
        /// <summary>
        /// 保存的属性面板垂直滚动偏移
        /// </summary>
        protected int m_nPropertyOffsetY;

        /// <summary>
        /// 控件在绘制过程中使用的绘图区域总高度
        /// </summary>
        protected int m_nVHeight;
        /// <summary>
        /// 保存的信息面板需要的总高度
        /// </summary>
        protected int m_nInfoVHeight;
        /// <summary>
        /// 保存的属性面板需要的总高度
        /// </summary>
        protected int m_nPropertyVHeight;
        /// <summary>
        /// 信息面板中Key显示需要的水平宽度
        /// </summary>
        protected int m_nInfoLeft;

        #endregion

        private Type m_type;
        private string[] m_KeysString = new string[] { "作者", "邮箱", "链接", "查看帮助" };

        private int m_nTitleHeight = 20;
        private int m_item_height = 30;
        private Color m_clr_item_1 = Color.FromArgb(10, 0, 0, 0);
        private Color m_clr_item_2 = Color.FromArgb(10, 255, 255, 255);
        //所有属性列表保存在此List中
        private List<STNodePropertyItem> m_lst_item = new List<STNodePropertyItem>();

        private STNodePropertyItem m_item_hover;        //当前被鼠标悬停的选项
        private STNodePropertyItem m_item_hover_value;  //当前值区域被鼠标悬停的选项
        private STNodePropertyItem m_item_down_value;   //当前值区域被鼠标点击的选项
        private STNodePropertyItem m_item_selected;     //当前选中的选项
        private STNodeAttribute m_node_attribute;       //节点参数信息
        private bool m_b_hover_switch;                  //是否鼠标悬停在面板切换按钮上
        private bool m_b_current_draw_info;             //当前绘制的时候是信息面板

        private Point m_pt_move;                        //鼠标在控件上的实时坐标
        private Point m_pt_down;                        //上次鼠标在控件上点下的坐标
        private string m_str_err;                       //当被设置时 绘制错误信息
        private string m_str_desc;                      //当被设置时 绘制描述信息

        private Pen m_pen;
        private SolidBrush m_brush;
        private StringFormat m_sf;
        private DrawingTools m_dt;

        /// <summary>
        /// 构造一个节点属性编辑器
        /// </summary>
        public STNodePropertyGrid() {
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.ResizeRedraw, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);

            m_pen = new Pen(Color.Black, 1);
            m_brush = new SolidBrush(Color.Black);
            m_sf = new StringFormat();
            m_sf.LineAlignment = StringAlignment.Center;
            m_sf.FormatFlags = StringFormatFlags.NoWrap;
            m_dt.Pen = m_pen;
            m_dt.SolidBrush = m_brush;

            this.ForeColor = Color.White;
            this.BackColor = Color.FromArgb(255, 35, 35, 35);

            this.MinimumSize = new Size(120, 50);
            this.Size = new Size(200, 150);
        }

        #region private method ==========

        private List<STNodePropertyItem> GetProperties(STNode node) {
            List<STNodePropertyItem> lst = new List<STNodePropertyItem>();
            if (node == null) return lst;
            Type t = node.GetType();
            foreach (var p in t.GetProperties()) {
                var attrs = p.GetCustomAttributes(true);
                foreach (var a in attrs) {
                    if (!(a is STNodePropertyAttribute)) continue;
                    var attr = a as STNodePropertyAttribute;
                    var item = new STNodePropertyItem() {
                        Node = node,
                        Name = attr.Name,
                        Description = attr.Description,
                        PropertyInfo = p,
                        Control = this,
                        Attribute = attr
                    };
                    object obj = Activator.CreateInstance(attr.DescriptorType);
                    if (!(obj is STNodePropertyDescriptor))
                        throw new ArgumentException("[STNodePropertyAttribute.Type]参数值必须为[STNodePropertyDescriptor]或者其子类的类型");
                    item.Descriptor = (STNodePropertyDescriptor)Activator.CreateInstance(attr.DescriptorType);
                    item.Descriptor.SetPropertyItem(item);
                    lst.Add(item);
                }
            }
            return lst;
        }

        private STNodeAttribute GetNodeAttribute(STNode node) {
            if (node == null) return null;
            Type t = node.GetType();
            foreach (var v in t.GetCustomAttributes(true)) {
                if (!(v is STNodeAttribute)) continue;
                return (STNodeAttribute)v;
            }
            return null;
        }

        private void SetItemRectangle() {
            int nw_p = 0, nw_h = 0;
            using (Graphics g = this.CreateGraphics()) {
                foreach (var v in m_lst_item) {
                    SizeF szf = g.MeasureString(v.Name, this.Font);
                    if (szf.Width > nw_p) nw_p = (int)Math.Ceiling(szf.Width);
                }
                for (int i = 0; i < m_KeysString.Length - 1; i++) {
                    SizeF szf = g.MeasureString(m_KeysString[i], this.Font);
                    if (szf.Width > nw_h) nw_h = (int)Math.Ceiling(szf.Width);
                }
                nw_p += 5; nw_h += 5;
                nw_p = Math.Min(nw_p, this.Width >> 1);
                m_nInfoLeft = Math.Min(nw_h, this.Width >> 1);

                int nTitleHeight = this._ShowTitle ? m_nTitleHeight : 0;
                for (int i = 0; i < m_lst_item.Count; i++) {
                    STNodePropertyItem item = m_lst_item[i];
                    Rectangle rect = new Rectangle(0, i * m_item_height + nTitleHeight, this.Width, m_item_height);
                    item.Rectangle = rect;
                    rect.Width = nw_p;
                    item.RectangleL = rect;
                    rect.X = rect.Right;
                    rect.Width = this.Width - rect.Left - 1;
                    rect.Inflate(-4, -4);
                    item.RectangleR = rect;
                    item.Descriptor.OnSetItemLocation();
                }
                m_nPropertyVHeight = m_lst_item.Count * m_item_height;
                if (this._ShowTitle) m_nPropertyVHeight += m_nTitleHeight;
            }
        }

        #endregion

        #region override ==========

        /// <summary>
        /// 当控件重绘时候发生
        /// </summary>
        /// <param name="e">事件参数</param>
        protected override void OnPaint(PaintEventArgs e) {
            base.OnPaint(e);
            Graphics g = e.Graphics;
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
            m_dt.Graphics = g;

            m_nOffsetY = m_b_current_draw_info ? m_nInfoOffsetY : m_nPropertyOffsetY;
            g.TranslateTransform(0, m_nOffsetY);

            if (m_b_current_draw_info) {
                m_nVHeight = m_nInfoVHeight;
                this.OnDrawInfo(m_dt);
            } else {
                m_nVHeight = m_nPropertyVHeight;
                for (int i = 0; i < m_lst_item.Count; i++) {
                    this.OnDrawPropertyItem(m_dt, m_lst_item[i], i);
                }
            }

            g.ResetTransform();

            if (this._ShowTitle) this.OnDrawTitle(m_dt);
            m_sf.FormatFlags = 0;
            if (!string.IsNullOrEmpty(m_str_err)) this.OnDrawErrorInfo(m_dt);
            if (!string.IsNullOrEmpty(m_str_desc)) this.OnDrawDescription(m_dt);
        }
        /// <summary>
        /// 当鼠标在控件上移动时候发生
        /// </summary>
        /// <param name="e">事件参数</param>
        protected override void OnMouseMove(MouseEventArgs e) {
            base.OnMouseMove(e);
            m_pt_move = e.Location;
            bool bHover = this._ShowTitle && m_rect_switch.Contains(e.Location);
            if (bHover != m_b_hover_switch) {
                m_b_hover_switch = bHover;
                this.Invalidate(m_rect_switch);
            }
            Point pt = new Point(e.X, e.Y - (int)m_nOffsetY);
            MouseEventArgs mea = new MouseEventArgs(e.Button, e.Clicks, pt.X, pt.Y, e.Delta);
            if (m_b_current_draw_info)
                this.OnProcessHelpMouseMove(mea);
            else
                this.OnProcessPropertyMouseMove(mea);
        }
        /// <summary>
        /// 当鼠标在控件上点下时候发生
        /// </summary>
        /// <param name="e">事件参数</param>
        protected override void OnMouseDown(MouseEventArgs e) {
            base.OnMouseDown(e);
            m_pt_down = e.Location;
            this.Focus();
            bool bRedraw = false;
            if (m_str_err != null) {
                bRedraw = true;
                m_str_err = null;
            }
            if (this._ShowTitle) {
                if (m_rect_switch.Contains(e.Location)) {
                    if (m_node_attribute == null || m_lst_item.Count == 0) return;
                    m_b_current_draw_info = !m_b_current_draw_info;
                    this.Invalidate();
                    return;
                } else if (m_rect_title.Contains(e.Location)) {
                    return;
                }
            }
            if (this._ShowTitle && m_rect_switch.Contains(e.Location)) {
                if (m_node_attribute == null || m_lst_item.Count == 0) return;
                m_b_current_draw_info = !m_b_current_draw_info;
                this.Invalidate();
                return;
            }
            Point pt = new Point(e.X, e.Y - (int)m_nOffsetY);
            MouseEventArgs mea = new MouseEventArgs(e.Button, e.Clicks, pt.X, pt.Y, e.Delta);

            if (m_b_current_draw_info)
                this.OnProcessInfoMouseDown(mea);
            else
                this.OnProcessPropertyMouseDown(mea);
            if (bRedraw) this.Invalidate();
        }
        /// <summary>
        /// 当鼠标在控件上抬起时候发生
        /// </summary>
        /// <param name="e">事件参数</param>
        protected override void OnMouseUp(MouseEventArgs e) {
            base.OnMouseUp(e);
            m_str_desc = null;
            if (m_item_down_value != null) {
                Point pt = new Point(e.X, e.Y - (int)m_nOffsetY);
                MouseEventArgs mea = new MouseEventArgs(e.Button, e.Clicks, pt.X, pt.Y, e.Delta);
                m_item_down_value.Descriptor.OnMouseUp(mea);
                if (m_pt_down == e.Location) {
                    m_item_down_value.Descriptor.OnMouseClick(mea);
                }
            }
            m_item_down_value = null;
            this.Invalidate();
        }
        /// <summary>
        /// 当鼠标离开控件时候发生
        /// </summary>
        /// <param name="e">事件参数</param>
        protected override void OnMouseLeave(EventArgs e) {
            base.OnMouseLeave(e);
            m_b_hover_switch = false;
            if (m_item_hover_value != null) m_item_hover_value.Descriptor.OnMouseLeave(e);
            m_item_hover = null;
            this.Invalidate();
        }
        /// <summary>
        /// 当鼠标在控件上滚动滚轮时候发生
        /// </summary>
        /// <param name="e">事件参数</param>
        protected override void OnMouseWheel(MouseEventArgs e) {
            base.OnMouseWheel(e);
            if (e.Delta > 0) {
                if (m_nOffsetY == 0) return;
                m_nOffsetY += m_item_height;
                if (m_nOffsetY > 0) m_nOffsetY = 0;
            } else {
                if (this.Height - m_nOffsetY >= m_nVHeight) return;
                m_nOffsetY -= m_item_height;
            }
            if (m_b_current_draw_info)
                m_nInfoOffsetY = m_nOffsetY;
            else
                m_nPropertyOffsetY = m_nOffsetY;
            this.Invalidate();
        }
        /// <summary>
        /// 当设置控件矩形区域时候发生
        /// </summary>
        /// <param name="x">x坐标</param>
        /// <param name="y">y坐标</param>
        /// <param name="width">宽度</param>
        /// <param name="height">高度</param>
        /// <param name="specified">指定需要设置的标识</param>
        //protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
        //    if (width < 120) width = 120;
        //    if (height < 50) height = 50;
        //    base.SetBoundsCore(x, y, width, height, specified);
        //}
        /// <summary>
        /// 当控件尺寸发生改变时候发生
        /// </summary>
        /// <param name="e">事件参数</param>
        protected override void OnResize(EventArgs e) {
            base.OnResize(e);
            m_rect_title.Width = this.Width;
            m_rect_title.Height = m_nTitleHeight;
            if (this._ShowTitle)
                m_rect_switch = new Rectangle(this.Width - m_nTitleHeight + 2, 2, m_nTitleHeight - 4, m_nTitleHeight - 4);
        }

        #endregion

        #region virtual method ==========

        /// <summary>
        /// 当绘制属性选项时候发生
        /// </summary>
        /// <param name="dt">绘制工具</param>
        /// <param name="item">目标属性选项</param>
        /// <param name="nIndex">选项所在索引</param>
        protected virtual void OnDrawPropertyItem(DrawingTools dt, STNodePropertyItem item, int nIndex) {
            Graphics g = dt.Graphics;
            m_brush.Color = (nIndex % 2 == 0) ? m_clr_item_1 : m_clr_item_2;
            g.FillRectangle(m_brush, item.Rectangle);
            if (item == m_item_hover || item == m_item_selected) {
                m_brush.Color = this._ItemHoverColor;
                g.FillRectangle(m_brush, item.Rectangle);
            }
            if (m_item_selected == item) {
                g.FillRectangle(m_brush, item.Rectangle.X, item.Rectangle.Y, 5, item.Rectangle.Height);
                if (this._AutoColor && this._STNode != null)
                    m_brush.Color = this._STNode.TitleColor;
                else
                    m_brush.Color = this._ItemSelectedColor;
                g.FillRectangle(m_brush, item.Rectangle.X, item.Rectangle.Y + 4, 5, item.Rectangle.Height - 8);
            }
            m_sf.Alignment = StringAlignment.Far;
            m_brush.Color = this.ForeColor;
            g.DrawString(item.Name, this.Font, m_brush, item.RectangleL, m_sf);

            item.Descriptor.OnPaintValueRectangle(m_dt);
            if (this._ReadOnlyModel) {
                m_pen.Color = this.ForeColor;
                g.DrawLine(m_pen,
                    item.RectangleR.Left + 2, item.RectangleR.Top + item.RectangleR.Height / 2,
                    item.RectangleR.Right - 2, item.RectangleR.Top + item.RectangleR.Height / 2);
                m_brush.Color = Color.FromArgb(125, 125, 125, 125);
                g.FillRectangle(m_brush, item.RectangleR);
            }
        }
        /// <summary>
        /// 绘制属性窗口标题
        /// </summary>
        /// <param name="dt">绘制工具</param>
        protected virtual void OnDrawTitle(DrawingTools dt) {
            Graphics g = dt.Graphics;
            if (this._AutoColor)
                m_brush.Color = this._STNode == null ? this._TitleColor : this._STNode.TitleColor;
            else
                m_brush.Color = this._TitleColor;
            g.FillRectangle(m_brush, m_rect_title);
            m_brush.Color = this._STNode == null ? this.ForeColor : this._STNode.ForeColor;
            m_sf.Alignment = StringAlignment.Center;
            g.DrawString(this._STNode == null ? this.Text : this._STNode.Title, this.Font, m_brush, m_rect_title, m_sf);
            //是否绘制面板切换按钮
            if (m_node_attribute == null || m_lst_item.Count == 0) return;
            if (m_b_hover_switch) {
                m_brush.Color = this.BackColor;
                g.FillRectangle(m_brush, m_rect_switch);
            }
            m_pen.Color = this._STNode == null ? this.ForeColor : this._STNode.ForeColor;
            m_brush.Color = m_pen.Color;
            int nT1 = m_rect_switch.Top + m_rect_switch.Height / 2 - 2;
            int nT2 = m_rect_switch.Top + m_rect_switch.Height / 2 + 1;
            g.DrawRectangle(m_pen, m_rect_switch.Left, m_rect_switch.Top, m_rect_switch.Width - 1, m_rect_switch.Height - 1);

            g.DrawLines(m_pen, new Point[]{
                new Point(m_rect_switch.Left + 2, nT1), new Point(m_rect_switch.Right - 3, nT1),
                new Point(m_rect_switch.Left + 3, nT1 - 1), new Point(m_rect_switch.Right - 3, nT1 - 1)
            });
            g.DrawLines(m_pen, new Point[]{
                new Point(m_rect_switch.Left + 2, nT2), new Point(m_rect_switch.Right - 3, nT2),
                new Point(m_rect_switch.Left + 2, nT2 + 1), new Point(m_rect_switch.Right - 4, nT2 + 1),
            });

            g.FillPolygon(m_brush, new Point[]{
                new Point(m_rect_switch.Left + 2, nT1),
                new Point(m_rect_switch.Left + 7, nT1),
                new Point(m_rect_switch.Left + 7, m_rect_switch.Top  ),
            });
            g.FillPolygon(m_brush, new Point[]{
                new Point(m_rect_switch.Right - 2, nT2),
                new Point(m_rect_switch.Right - 7, nT2),
                new Point(m_rect_switch.Right - 7, m_rect_switch.Bottom - 2 ),
            });
        }
        /// <summary>
        /// 当需要绘制属性描述信息时发生
        /// </summary>
        /// <param name="dt">绘制工具</param>
        protected virtual void OnDrawDescription(DrawingTools dt) {
            if (string.IsNullOrEmpty(m_str_desc)) return;
            Graphics g = dt.Graphics;
            SizeF szf = g.MeasureString(m_str_desc, this.Font, this.Width - 4);
            Rectangle rect_desc = new Rectangle(0, this.Height - (int)szf.Height - 4, this.Width, (int)szf.Height + 4);
            m_brush.Color = this._DescriptionColor;
            g.FillRectangle(m_brush, rect_desc);
            m_pen.Color = this._DescriptionColor;
            g.DrawRectangle(m_pen, 0, rect_desc.Top, rect_desc.Width - 1, rect_desc.Height - 1);
            rect_desc.Inflate(-4, 0);
            m_brush.Color = this.ForeColor;
            m_sf.Alignment = StringAlignment.Near;
            g.DrawString(m_str_desc, this.Font, m_brush, rect_desc, m_sf);
        }
        /// <summary>
        /// 当需要绘制错误信息时发生
        /// </summary>
        /// <param name="dt">绘制工具</param>
        protected virtual void OnDrawErrorInfo(DrawingTools dt) {
            if (string.IsNullOrEmpty(m_str_err)) return;
            Graphics g = dt.Graphics;
            SizeF szf = g.MeasureString(m_str_err, this.Font, this.Width - 4);
            Rectangle rect_desc = new Rectangle(0, 0, this.Width, (int)szf.Height + 4);
            m_brush.Color = this._ErrorColor;
            g.FillRectangle(m_brush, rect_desc);
            m_pen.Color = this._ErrorColor;
            g.DrawRectangle(m_pen, 0, rect_desc.Top, rect_desc.Width - 1, rect_desc.Height - 1);
            rect_desc.Inflate(-4, 0);
            m_brush.Color = this.ForeColor;
            m_sf.Alignment = StringAlignment.Near;
            g.DrawString(m_str_err, this.Font, m_brush, rect_desc, m_sf);
        }
        /// <summary>
        /// 当绘制节点信息时候发生
        /// </summary>
        /// <param name="dt">绘制工具</param>
        protected virtual void OnDrawInfo(DrawingTools dt) {
            if (m_node_attribute == null) return;
            var attr = m_node_attribute;
            Graphics g = dt.Graphics;
            Color clr_r = Color.FromArgb(this.ForeColor.A / 2, this.ForeColor);
            m_sf.Alignment = StringAlignment.Near;
            Rectangle rect = new Rectangle(0, this._ShowTitle ? m_nTitleHeight : 0, this.Width, m_item_height);
            Rectangle rect_l = new Rectangle(2, rect.Top, m_nInfoLeft - 2, m_item_height);
            Rectangle rect_r = new Rectangle(m_nInfoLeft, rect.Top, this.Width - m_nInfoLeft, m_item_height);
            m_brush.Color = m_clr_item_2;
            g.FillRectangle(m_brush, rect);
            m_brush.Color = this.ForeColor;
            m_sf.FormatFlags = StringFormatFlags.NoWrap;
            m_sf.Alignment = StringAlignment.Near;
            g.DrawString(m_KeysString[0], this.Font, m_brush, rect_l, m_sf);          //author
            m_brush.Color = clr_r;
            g.DrawString(attr.Author, this.Font, m_brush, rect_r, m_sf);
            rect.Y += m_item_height; rect_l.Y += m_item_height; rect_r.Y += m_item_height;

            m_brush.Color = m_clr_item_1;
            g.FillRectangle(m_brush, rect);
            m_brush.Color = this.ForeColor;
            g.DrawString(m_KeysString[1], this.Font, m_brush, rect_l, m_sf);          //mail
            m_brush.Color = clr_r;
            g.DrawString(attr.Mail, this.Font, m_brush, rect_r, m_sf);
            rect.Y += m_item_height; rect_l.Y += m_item_height; rect_r.Y += m_item_height;

            m_brush.Color = m_clr_item_2;
            g.FillRectangle(m_brush, rect);
            m_brush.Color = this.ForeColor;
            g.DrawString(m_KeysString[2], this.Font, m_brush, rect_l, m_sf);          //link_key
            m_brush.Color = clr_r;
            g.DrawString(attr.Link, this.Font, Brushes.CornflowerBlue, rect_r, m_sf); //link
            if (!string.IsNullOrEmpty(attr.Link)) m_rect_link = rect_r;
            //fill left
            m_brush.Color = Color.FromArgb(40, 125, 125, 125);
            g.FillRectangle(m_brush, 0, this._ShowTitle ? m_nTitleHeight : 0, m_nInfoLeft - 1, m_item_height * 3);

            rect.X = 5; rect.Y += m_item_height;
            rect.Width = this.Width - 10;
            if (!string.IsNullOrEmpty(m_node_attribute.Description)) {
                float h = g.MeasureString(m_node_attribute.Description, this.Font, rect.Width).Height;
                rect.Height = (int)Math.Ceiling(h / m_item_height) * m_item_height;
                m_brush.Color = clr_r;
                m_sf.FormatFlags = 0;
                g.DrawString(m_node_attribute.Description, this.Font, m_brush, rect, m_sf);
            }
            m_nInfoVHeight = rect.Bottom;
            bool bHasHelp = STNodeAttribute.GetHelpMethod(m_type) != null;
            rect.X = 5; rect.Y += rect.Height;
            rect.Height = m_item_height;
            m_sf.Alignment = StringAlignment.Center;
            m_brush.Color = Color.FromArgb(125, 125, 125, 125);
            g.FillRectangle(m_brush, rect);
            if (bHasHelp) m_brush.Color = Color.CornflowerBlue;
            g.DrawString(m_KeysString[3], this.Font, m_brush, rect, m_sf);
            if (bHasHelp) m_rect_help = rect;
            else {
                int w = (int)g.MeasureString(m_KeysString[3], this.Font).Width + 1;
                int x = rect.X + (rect.Width - w) / 2, y = rect.Y + rect.Height / 2;
                m_pen.Color = m_brush.Color;
                g.DrawLine(m_pen, x, y, x + w, y);
            }
            m_nInfoVHeight = rect.Bottom;
        }
        /// <summary>
        /// 当在属性面板鼠标点下时候发生
        /// </summary>
        /// <param name="e">鼠标事件参数</param>
        protected virtual void OnProcessPropertyMouseDown(MouseEventArgs e) {
            bool bRedraw = false;
            if (m_item_selected != m_item_hover) {
                m_item_selected = m_item_hover;
                bRedraw = true;
            }
            m_item_down_value = null;
            if (m_item_selected == null) {
                if (bRedraw) this.Invalidate();
                return;
            }
            if (m_item_selected.RectangleR.Contains(e.Location)) {
                m_item_down_value = m_item_selected;
                if (!this._ReadOnlyModel)
                    m_item_selected.Descriptor.OnMouseDown(e);
                else {
                    return;
                }
            } else if (m_item_selected.RectangleL.Contains(e.Location)) {
                m_str_desc = m_item_selected.Description;
                bRedraw = true;
            }
            if (bRedraw) this.Invalidate();
        }
        /// <summary>
        /// 当在信息面板鼠标点下时候发生
        /// </summary>
        /// <param name="e">鼠标事件参数</param>
        protected virtual void OnProcessInfoMouseDown(MouseEventArgs e) {
            try {
                if (m_rect_link.Contains(e.Location)) {
                    System.Diagnostics.Process.Start(m_node_attribute.Link);
                } else if (m_rect_help.Contains(e.Location)) {
                    STNodeAttribute.ShowHelp(m_type);
                }
            } catch (Exception ex) {
                this.SetErrorMessage(ex.Message);
            }
        }
        /// <summary>
        /// 当在属性面板鼠标移动时候发生
        /// </summary>
        /// <param name="e">鼠标事件参数</param>
        protected virtual void OnProcessPropertyMouseMove(MouseEventArgs e) {
            if (m_item_down_value != null) {
                m_item_down_value.Descriptor.OnMouseMove(e);
                return;
            }
            STNodePropertyItem item = null;
            foreach (var v in m_lst_item) {
                if (v.Rectangle.Contains(e.Location)) {
                    item = v;
                    break;
                }
            }
            if (item != null) {
                if (item.RectangleR.Contains(e.Location)) {
                    if (m_item_hover_value != item) {
                        if (m_item_hover_value != null) m_item_hover_value.Descriptor.OnMouseLeave(e);
                        m_item_hover_value = item;
                        m_item_hover_value.Descriptor.OnMouseEnter(e);
                    }
                    m_item_hover_value.Descriptor.OnMouseMove(e);
                } else {
                    if (m_item_hover_value != null) m_item_hover_value.Descriptor.OnMouseLeave(e);
                }
            }
            if (m_item_hover != item) {
                m_item_hover = item;
                this.Invalidate();
            }
        }
        /// <summary>
        /// 当在信息面板鼠标移动时候发生
        /// </summary>
        /// <param name="e">鼠标事件参数</param>
        protected virtual void OnProcessHelpMouseMove(MouseEventArgs e) {
            if (m_rect_link.Contains(e.Location) || m_rect_help.Contains(e.Location)) {
                this.Cursor = Cursors.Hand;
            } else this.Cursor = Cursors.Arrow;
        }

        #endregion

        #region public ==========

        /// <summary>
        /// 设置需要显示的STNode节点
        /// </summary>
        /// <param name="node">目标节点</param>
        public void SetSTNode(STNode node) {
            if (node == this._STNode) return;
            m_nInfoOffsetY = m_nPropertyOffsetY = 0;
            m_nInfoVHeight = m_nPropertyVHeight = 0;
            m_rect_link = m_rect_help = Rectangle.Empty;
            m_str_desc = m_str_err = null;
            this._STNode = node;
            if (node != null) {
                m_type = node.GetType();
                m_lst_item = this.GetProperties(node);
                m_node_attribute = this.GetNodeAttribute(node);
                this.SetItemRectangle();
                m_b_current_draw_info = m_lst_item.Count == 0 || this._InfoFirstOnDraw;
                if (this._AutoColor) this._ItemSelectedColor = this._STNode.TitleColor;
            } else {
                m_type = null;
                m_lst_item.Clear();
                m_node_attribute = null;
            }
            this.Invalidate();
        }
        /// <summary>
        /// 设置信息页面Key的显示文本
        /// </summary>
        /// <param name="strAuthor">作者</param>
        /// <param name="strMail">邮箱</param>
        /// <param name="strLink">连接</param>
        /// <param name="strHelp">查看帮助</param>
        public void SetInfoKey(string strAuthor, string strMail, string strLink, string strHelp) {
            m_KeysString = new string[] { strAuthor, strMail, strLink, strHelp };
        }
        /// <summary>
        /// 设置要显示的错误信息
        /// </summary>
        /// <param name="strText">错误信息</param>
        public void SetErrorMessage(string strText) {
            m_str_err = strText;
            this.Invalidate();
        }

        #endregion
    }
    /// <summary>
    /// 节点属性选项
    /// </summary>
    public class STNodePropertyItem
    {
        /// <summary>
        /// 获取目标节点
        /// </summary>
        public STNode Node { get; internal set; }
        /// <summary>
        /// 获取所属的节点属性编辑器控件
        /// </summary>
        public STNodePropertyGrid Control { get; internal set; }
        /// <summary>
        /// 获取选项所在区域
        /// </summary>
        public Rectangle Rectangle { get; internal set; }
        /// <summary>
        /// 获取选项名称所在区域
        /// </summary>
        public Rectangle RectangleL { get; internal set; }
        /// <summary>
        /// 获取选项值所在区域
        /// </summary>
        public Rectangle RectangleR { get; internal set; }
        /// <summary>
        /// 获取选项需要显示的名称
        /// </summary>
        public string Name { get; internal set; }
        /// <summary>
        /// 获取属性对应的描述信息
        /// </summary>
        public string Description { get; internal set; }
        /// <summary>
        /// 获取属性信息
        /// </summary>
        public PropertyInfo PropertyInfo { get; internal set; }
        /// <summary>
        /// 获取属性描述器
        /// </summary>
        public STNodePropertyDescriptor Descriptor { get; internal set; }

        public STNodePropertyAttribute Attribute { get; internal set; }
    }
}
