using Io.Github.Kerwinxu.LibShapes.Core.Command;
using Io.Github.Kerwinxu.LibShapes.Core.Event;
using Io.Github.Kerwinxu.LibShapes.Core.Shape;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Windows.Forms;

/**
  这个画布需要对外联系的是
    1. 各种属性
    2. objectSelected ： 选择的对象更改事件。
 * 
 * **/

namespace Io.Github.Kerwinxu.LibShapes.Core
{



    /// <summary>
    /// 画布
    /// </summary>
    public partial class UserControlCanvas : UserControl
    {

        #region 构造函数
        public UserControlCanvas()
        {
            InitializeComponent();
            init();
        }

        private void init()
        {
            // 这里设置默认颜色
            GriddingInterval = 2;                    // 默认网格2mm
            commandRecorder = new CommandRecorder(); // 默认的命令记录器
            shapes = new Shapes();                   // 默认的形状
            state = new State.StateStandby(this);   // 默认是待机状态。
            // 这里先取得dpi
            var g = this.CreateGraphics();
            var _dpix = g.DpiX;
            var _dpiy = g.DpiY;
            g.Dispose();
            // 一堆的事件
            // 如下的鼠标事件，先将坐标转换成毫米了。
            bool isLeftDown = false; // 左键是否按下。移动的时候需要判断是否按下的。
            // todo 加入键盘事件的处理，
            this.MouseDown += (sender, e) => { if (!IsEdit) return; isLeftDown = true; if (e.Button == MouseButtons.Left) this.state.LeftMouseDown(PointTransform.pixToMM(_dpix, _dpiy, new PointF(e.X, e.Y))); this.Refresh(); };
            this.MouseMove += (sender, e) => { if (!IsEdit) return; if (!isLeftDown) return; if (e.Button == MouseButtons.Left) this.state.LeftMouseMove(PointTransform.pixToMM(_dpix, _dpiy, new PointF(e.X, e.Y))); this.Refresh(); };
            this.MouseUp += (sender, e) => { if (!IsEdit) return; isLeftDown = false; if (e.Button == MouseButtons.Left) this.state.LeftMouseUp(PointTransform.pixToMM(_dpix, _dpiy, new PointF(e.X, e.Y))); this.Refresh(); };
            this.MouseClick += (sender, e) => { if (!IsEdit) return; if (e.Button == MouseButtons.Right) this.state.RightMouseClick(PointTransform.pixToMM(_dpix, _dpiy, new PointF(e.X, e.Y))); this.Refresh(); };
            // 双缓冲，这里需要打开。
            this.DoubleBuffered = true;
            // 说是推荐如上的这个，
            //this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true);
            // 这里有一个特殊的设置，如果偏移为空的话，这里默认的偏移是有这个刻度尺的
            if (shapes.pointTransform == null ||
                (shapes.pointTransform != null && shapes.pointTransform.OffsetX == 0 && shapes.pointTransform.OffsetY == 0)
                )
            {
                // 稍微多留一点出来。
                shapes.pointTransform.OffsetX = scaleWidth * 1.5f;
                shapes.pointTransform.OffsetY = scaleWidth * 1.5f;
            }
        }

        #endregion

        #region 一堆的常量

        private float scaleWidth = 6;   // 刻度尺的宽度，这个是固定的，单位是mm
        private float scaleLineWidth = 0.1f;  // 刻度尺上的刻度线的宽度。
        private Font  scaleTextFont = new Font("Arial", 6); // 刻度尺上文字的字体。
        private float minIntervalpixel = 5;   // 比如网格，如果小于这个，就不打印了。
        private Color scaleBackColor = Color.Orange;
        private float grid_width = 0.25f;                        // 绘制的网格的宽度
        private Brush grid_brush = new SolidBrush(Color.Black);  // 网格的
        private Pen penSelectShape = new Pen(Color.Pink) {      // 选择框的画笔
            DashStyle=DashStyle.Dash,
            Width = 1f
        };

        #endregion

        #region 属性

        // 如下的很多设置成属性是因为我方便从别的地方设置，这个属性其实是有个set方法的。


        /// <summary>
        /// 是否可以编辑
        /// </summary>
        public bool IsEdit { get; set; }

        /// <summary>
        /// 形状
        /// </summary>
        public Shapes shapes;

        /// <summary>
        /// 显示网格
        /// </summary>
        public bool isDrawDridding { get; set; }

        /// <summary>
        /// 网格间隔
        /// </summary>
        public int GriddingInterval { get; set; } // 默认是1，2，5mm

        /// <summary>
        /// 对齐网格，这个在移动和更改尺寸的时候用到。
        /// </summary>
        public bool isAlignDridding { get; set; }

        /// <summary>
        /// 命令记录的
        /// </summary>
        internal   ICommandRecorder commandRecorder { get; set; }


        /// <summary>
        /// 是否安装了shift键
        /// </summary>
        public bool isShift { get; set; }

        private State.State _state;
        /// <summary>
        /// 现在的状态
        /// </summary>
        public State.State state { get { return _state; } set { _state = value; onStateChanged(); } }

        /// <summary>
        /// 当前选择的图形
        /// </summary>
        public ShapeEle SelectShape { get; set; }

        #endregion

        #region 事件

        #region 选择更改事件


        // 委托
        public delegate void ObjectSelected(object sender, ObjectSelectEventArgs e);
        // 事件
        public event ObjectSelected objectSelected;

        private void onObjectSelected(Object obj)
        {
            // 首先生成参数
            var args = new ObjectSelectEventArgs(obj);
            // 然后看看是否有监听的。
            if (objectSelected != null)
            {
                objectSelected(this, args);
            }
        }

        /// <summary>
        /// 给外部调用的，可以更改当前的选择。
        /// </summary>
        /// <param name="obj"></param>
        public void changeSelect(Object obj)
        {
            SelectShape = obj as ShapeEle;

            this.oldShapes = this.shapes.DeepClone();

            if (SelectShape == null)
            {
                // 这里是设置纸张。
                onObjectSelected(shapes.Paper);
            }else
            {
                onObjectSelected(obj);
            }
        }

        #endregion

        #region 状态更改事件

        // 委托
        public delegate void StateChanged(object sender, StateChangedEventArgs e);
        // 事件
        public event StateChanged stateChanged;

        private void onStateChanged()
        {
            // 首先生成参数
            var args = new StateChangedEventArgs(this.state);
            // 然后看看是否有监听的。
            if (stateChanged != null)
            {
                stateChanged(this, args);
            }
        }

        #endregion


        #region   属性更改事件处理，


        private Shapes oldShapes;
        public  void propertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            this.Refresh();
            // 这里时在发送给propertyGrid前就有保存。
            this.commandRecorder.addCommand(new Command.CommandShapesChanged()
            {
                canvas =this,
                OldShapes = this.oldShapes,
                NewShapes = this.shapes.DeepClone(),
            }) ;
        }

        #endregion

        #endregion

        #region 绘图相关

        /// <summary>
        /// 绘图
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UserControlCanvas_Paint(object sender, PaintEventArgs e)
        {
            if (this.shapes == null) {return; }
            // 这里要重新更新一下变量,这样相当于更新一遍。
            this.shapes.Vars = this.shapes.Vars;
            // 如下的取消了双缓冲，是因为并不能达到预期，反倒是非常闪烁。
            // 最简单的方式是设置属性 this.DoubleBuffered = true;
            // 这里重新绘图用上了双缓冲
            // 1. 首先在内存中建立Graphics对象
            //BufferedGraphicsContext currentContext = BufferedGraphicsManager.Current;
            //BufferedGraphics myBuffer = currentContext.Allocate(e.Graphics, e.ClipRectangle);
            //Graphics g = myBuffer.Graphics;
            // 1. 取得Graphics对象
            Graphics g = e.Graphics;
            // 全局的偏移和缩放,全局不涉及旋转，因为刻度尺。
            var matrix = shapes.GetMatrix();
            // 背景色，默认用这个控件的背景色填充。
            g.Clear(this.BackColor);
            // 2. 形状绘图
            if (this.shapes != null)
            {
                // 绘图，并且用上了偏移和缩放。
                shapes.Draw(g, matrix); 
            }
            // 3. 绘制网格
            if (isDrawDridding)
            {
                drawGrid(g);
            }
            // 4. 绘制选择框
            drawSelectRect(g);
            // 5. 绘制刻度尺
            drawScale(g);
            // 6. 这里绘制虚线，要选择哪些东西的
            if (this.state is State.ShapeRectSelect)
            {
                ((State.ShapeRectSelect)this.state).Draw(g);
            }

            // 7. 刷新双缓冲
            //myBuffer.Render(e.Graphics);
            //g.Dispose();
            //myBuffer.Dispose();//释放资源
        }


        /// <summary>
        /// 绘制网格
        /// </summary>
        /// <param name="g"></param>
        private void drawGrid(Graphics g)
        {
            // 我的思路是，首先判断是否小于最小
            if (GriddingInterval / 25.4 * g.DpiX * shapes.pointTransform.Zoom < minIntervalpixel) return;
            // 如下是计算范围
            PointF p1 = new PointF(0, 0);
            PointF p2 = new PointF(this.Width / g.DpiX * 25.4f, this.Height / g.DpiY * 25.4f);
            var point1 = shapes.pointTransform.CanvasToVirtualPoint(p1); // 左上角画布的坐标
            var point2 = shapes.pointTransform.CanvasToVirtualPoint(p2); // 右下角的坐标
            // 画笔
            Pen pen = new Pen(Color.Black);
            pen.Width = 0.1f; // 画笔的用很细的。
            int x_start = (int)point1.X / GriddingInterval * GriddingInterval;
            int y_start = (int)point1.Y / GriddingInterval * GriddingInterval;
            int i_count = (int)(point2.X - point1.X) / GriddingInterval;
            int j_count = (int)(point2.Y - point1.Y) / GriddingInterval;
            // 然后这里绘制
            for (int i = 0; i < i_count; i++)
            {
                for (int j = 0; j < j_count; j++)
                {
                    // 我这里是将坐标转成画布的坐标
                    var point3 = new PointF(
                        x_start + i * GriddingInterval,
                        y_start + j * GriddingInterval);
                    var point4 = shapes.pointTransform.VirtualToCanvasPoint(point3);
                    // 这里进行绘制，这里实际上是绘制一个圆
                    var point5 = new PointF(point4.X - grid_width/2, point4.Y - grid_width/2);
                    var rect = new RectangleF(point5, new SizeF(grid_width, grid_width));
                    //
                    g.FillEllipse(grid_brush, rect);
                }
            }


        }

        /// <summary>
        /// 绘制选择框
        /// </summary>
        /// <param name="g"></param>
        private void drawSelectRect(Graphics g)
        {
            if(this.SelectShape != null)
            {
                // 如果有图形，就取得边框，这个边框的尺寸控件是画布的尺寸控件，已经做过转换了。
                var rect = SelectShape.GetBounds(shapes.GetMatrix());
                // 然后绘制这个边框就是了。
                GraphicsPath path = new GraphicsPath();
                path.AddRectangle(rect);
                g.DrawPath(penSelectShape, path);


            }
        }

        /// <summary>
        /// 绘制刻度。
        /// </summary>
        /// <param name="g"></param>
        private void drawScale(Graphics g)
        {
            // 这个刻度值是在上边和左边的。
            // 1 . 首先绘制刻度的背景
            // 如下是计算范围
            var point1 = shapes.pointTransform.CanvasToVirtualPoint(new PointF(0, 0)); // 左上角虚拟的坐标
            var point2 = shapes.pointTransform.CanvasToVirtualPoint(new PointF(this.Width / g.DpiX * 25.4f, this.Height / g.DpiY * 25.4f)); // 右下角的坐标
            // 如下的是计算这个自定义控件的宽度和高度，请注意，这里不算缩放。
            float w = this.Width / g.DpiX * 25.4f;
            float h = this.Height / g.DpiY * 25.4f ;
            // 控件大小不变，这个如下的就是固定的。
            g.FillRectangle(
                new SolidBrush(this.scaleBackColor),
                new RectangleF(0,0, w, scaleWidth)
                );
            g.FillRectangle(
                new SolidBrush(this.scaleBackColor),
                new RectangleF(0, 0, scaleWidth, h)
                );
            // 2.然后是各个刻度，先水平,后竖直
            Pen pen = new Pen(Color.Black);
            pen.Width = scaleLineWidth;
            // 2.1 水平刻度
            horizontal_scale_line(g, pen, point1.X, point2.X, 1, 0.5f);
            horizontal_scale_line(g, pen, point1.X, point2.X, 2, 1);
            horizontal_scale_line(g, pen, point1.X, point2.X, 5, 2);
            horizontal_scale_line(g, pen, point1.X, point2.X, 10, 3);
            horizontal_scale_text(g, new SolidBrush(Color.Black), point1.X, point2.X);
            // 2.2 垂直刻度
            vertical_scale_line(g, pen, point1.Y, point2.Y, 1, 0.5f);
            vertical_scale_line(g, pen, point1.Y, point2.Y, 2, 1);
            vertical_scale_line(g, pen, point1.Y, point2.Y, 5, 2);
            vertical_scale_line(g, pen, point1.Y, point2.Y, 10, 3);
            vertical_scale_text(g, new SolidBrush(Color.Black), point1.Y, point2.Y);

            // 3. 左上角有重复的，这里直接空白出来
            g.FillRectangle(
                new SolidBrush(this.scaleBackColor),
                new RectangleF(0, 0, scaleWidth, scaleWidth)
                );

        }

        /// <summary>
        /// 水平刻度尺
        /// </summary>
        /// <param name="g"></param>
        /// <param name="start_x">开始的x坐标</param>
        /// <param name="end_x">结束的x坐标</param>
        /// <param name="y">y坐标</param>
        /// <param name="intInterval">间隔</param>
        /// <param name="line_length">线的长度</param>
        private void horizontal_scale_line(Graphics g, Pen pen, float start_x, float end_x, int intInterval, float line_length)
        {
            // 变量
            int x_start = (int)(start_x-1)/ intInterval * intInterval;
            int x_end = (int)(end_x+1)/ intInterval * intInterval;
            int i_count = (x_end - x_start) / intInterval +1;
            // 这里要计算一下是否太密了。
            if (intInterval / 25.4 * g.DpiX * shapes.pointTransform.Zoom < minIntervalpixel) return;
            if (intInterval / 25.4 * g.DpiY * shapes.pointTransform.Zoom < minIntervalpixel) return;
            // 这里是按照画布的坐标绘制的
            // 其y坐标为y，另外向上有line_length的长度
            // 这个首先算出起始的坐标是多少
            for (int i = 0; i < i_count; i++)
            {
                PointF p1 = new PointF(x_start + i * intInterval, 0);        // 虚拟的坐标。
                // 这里计算在画布中的坐标是多少
                var p3 = shapes.pointTransform.VirtualToCanvasPoint(p1);
                p3.Y = scaleWidth;  // 这个是固定的。
                var p4 = new PointF(p3.X, p3.Y - line_length);
                g.DrawLine(pen, p3, p4);
            }
 
        }

        /// <summary>
        /// 这个是绘制文本的
        /// </summary>
        /// <param name="g"></param>
        /// <param name="pen"></param>
        /// <param name="start_x"></param>
        /// <param name="end_x"></param>
        private void horizontal_scale_text(Graphics g, Brush brush, float start_x, float end_x)
        {
            int intInterval = 10;
            int x_start = (int)(start_x - 1) / intInterval * intInterval;
            int x_end = (int)(end_x + 1) / intInterval * intInterval;
            int i_count = (x_end - x_start) / intInterval + 1;
            for (int i = 0; i < i_count; i++)
            {
                // 这里写文字
                int j = x_start + i * intInterval ;
                string _text = (j/ intInterval).ToString(); // 显示的是cm，默认的是毫米
                // 然后求x坐标
                var p1 = new PointF(j, 0);
                var p2 = shapes.pointTransform.VirtualToCanvasPoint(p1);
                p2.Y = 0;// 手动更改成0
                // 这个p2坐标就是文字中线的位置，这里还要往左边移动一些。
                var _size = g.MeasureString(_text, scaleTextFont);
                // 然后这个转成
                p2.X -= _size.Width / 2;
                g.DrawString(_text, scaleTextFont, brush, p2);
            }
        }

        /// <summary>
        /// 竖直的刻度尺
        /// </summary>
        /// <param name="g"></param>
        /// <param name="start_y">开始的x坐标</param>
        /// <param name="end_y">结束的x坐标</param>
        /// <param name="y">y坐标</param>
        /// <param name="intInterval">间隔</param>
        /// <param name="line_length">线的长度</param>
        private void vertical_scale_line(Graphics g, Pen pen, float start_y, float end_y, int intInterval, float line_length)
        {
            // 变量
            int y_start = (int)(start_y - 1) / intInterval * intInterval;
            int y_end = (int)(end_y + 1) / intInterval * intInterval;
            int i_count = (y_end - y_start) / intInterval + 1;
            // 这里要计算一下是否太密了。
            if (intInterval / 25.4 * g.DpiY * shapes.pointTransform.Zoom < minIntervalpixel) return;
            if (intInterval / 25.4 * g.DpiY * shapes.pointTransform.Zoom < minIntervalpixel) return;
            // 这里是按照画布的坐标绘制的
            // 其y坐标为y，另外向上有line_length的长度
            // 这个首先算出起始的坐标是多少
            for (int i = 0; i < i_count; i++)
            {
                PointF p1 = new PointF(0, y_start + i * intInterval);        // 虚拟的坐标。
                // 这里计算在画布中的坐标是多少
                var p3 = shapes.pointTransform.VirtualToCanvasPoint(p1);
                p3.X = scaleWidth;  // 这个是固定的。
                var p4 = new PointF(p3.X - line_length, p3.Y);
                g.DrawLine(pen, p3, p4);
            }

        }

        /// <summary>
        /// 这个是绘制文本的
        /// </summary>
        /// <param name="g"></param>
        /// <param name="pen"></param>
        /// <param name="start_y"></param>
        /// <param name="end_y"></param>
        private void vertical_scale_text(Graphics g, Brush brush, float start_y, float end_y)
        {
            int intInterval = 10;
            int y_start = (int)(start_y - 1) / intInterval * intInterval;
            int y_end = (int)(end_y + 1) / intInterval * intInterval;
            int i_count = (y_end - y_start) / intInterval + 1;
            for (int i = 0; i < i_count; i++)
            {
                // 这里写文字
                int j = y_start + i * intInterval;
                string _text = (j / intInterval).ToString(); // 显示的是cm，默认的是毫米
                // 然后求x坐标
                var p1 = new PointF(0, j);
                var p2 = shapes.pointTransform.VirtualToCanvasPoint(p1);
                p2.X = 0;// 手动更改成0
                // 这个p2坐标就是文字中线的位置，这里还要往左边移动一些。
                var _size = g.MeasureString(_text, scaleTextFont);
                // 然后这个转成
                p2.Y -= _size.Height / 2;
                g.DrawString(_text, scaleTextFont, brush, p2);
            }
        }

        #endregion

        #region 请注意，如下的几个操作都只是处理顶层的

        /// <summary>
        /// 以某个点为中心放大
        /// </summary>
        /// <param name="point"></param>
        public void zoom(PointF point)
        {
            // 1. 取得这个点在虚拟画布上的地址
            var point2 = this.shapes.pointTransform.CanvasToVirtualPoint(point);
            // 2.  更新偏移
            this.shapes.pointTransform.OffsetX -= point2.X * (this.shapes.pointTransform.Zoom );
            this.shapes.pointTransform.OffsetY -= point2.Y * (this.shapes.pointTransform.Zoom );
            // 3 放大
            this.shapes.pointTransform.Zoom *= 2;
            // 4. 刷新
            this.Refresh();

        }

        /// <summary>
        /// 以某个点为中心缩小。
        /// </summary>
        /// <param name="point"></param>
        public void reduce(PointF point)
        {
            // 1. 取得这个点在虚拟画布上的地址
            var point2 = this.shapes.pointTransform.CanvasToVirtualPoint(point);
            // 2.  更新偏移
            this.shapes.pointTransform.OffsetX += point2.X * (this.shapes.pointTransform.Zoom/2);
            this.shapes.pointTransform.OffsetY += point2.Y * (this.shapes.pointTransform.Zoom/2);
            // 3 放大
            this.shapes.pointTransform.Zoom /= 2;
            // 4. 刷新
            this.Refresh();
        }

        /// <summary>
        /// 放大到屏幕
        /// </summary>
        public void zoomToScreen()
        {
            if (shapes != null)
            {
                var g = this.CreateGraphics();
                shapes.zoomTo(
                    g.DpiX, 
                    g.DpiY, 
                    this.Width, 
                    this.Height,
                    scaleWidth/25.4f*g.DpiX* 1.5f    // scaleWidth/25.4f*g.DpiX表示刻度尺实际的像素宽度，
                                                     // 而*1.5，表示两边流出1.5倍数的距离，这样，图形也不会太靠近边界。
                    );
                this.Refresh();
            }
        }


        /// <summary>
        /// 添加一个新的形状
        /// </summary>
        /// <param name="shape"></param>
        public void addShape(ShapeEle shape)
        {
            // 只是在顶层添加。
            shapes.lstShapes.Add(shape);
            changeSelect(shape); // 添加后是当作当前选择的。
        }

        /// <summary>
        /// 删除，
        /// </summary>
        public void deleteShapes(ShapeEle shape)
        {
            //返回的是第几个项目删除的。
            if (shape is ShapeMultiSelect) // 这里表示选择了多个。
            {
                // 这里说明是选择的一堆，这里一个一个的删除
                foreach (var item in ((ShapeMultiSelect)shape).shapes)
                {
                    deleteShapes(item);
                }
            }
            else
            {
                // 这里说明可能是一个形状。
                var index = shapes.lstShapes.IndexOf(shape);
                if (index == -1) return;
                var oldShapes = this.shapes.DeepClone();
                shapes.lstShapes.Remove(shape);// 请注意，这里只是删除顶层的。
                                               // 这里添加command
                commandRecorder.addCommand(new Command.CommandShapesChanged() { 
                    canvas=this,
                    OldShapes = oldShapes,
                    NewShapes = this.shapes.DeepClone()
            });
            }

            this.Refresh();// 刷新。
        }

        /// <summary>
        /// 删除已经选中的形状
        /// </summary>
        public void deleteShapes()
        {
            // 这里要判断一下是否是群组
            if (SelectShape != null)
            {
                 deleteShapes(SelectShape);
            }
             
        }

        // todo 请注意，如下的几个全部要添加命令

        public void forward(int id)
        {
            // todo 添加命令。
            var shape = shapes.getShape(id);
            int index = shapes.lstShapes.IndexOf(shape);// 取得下标
            if (index > 0)
            {
                // 如果不是最前面的
                shapes.lstShapes.Remove(shape);
                shapes.lstShapes.Insert(index - 1, shape);
            }
        }
        public void forwardToFront(int id)
        {
            // todo 添加命令。
            var shape = shapes.getShape(id);
            int index = shapes.lstShapes.IndexOf(shape);// 取得下标
            if (index > 0)
            {
                // 如果不是最前面的
                shapes.lstShapes.Remove(shape);
                shapes.lstShapes.Insert(0, shape);
            }
        }

        public void backward(int id)
        {
            // todo 添加命令。
            var shape = shapes.getShape(id);
            int index = shapes.lstShapes.IndexOf(shape);// 取得下标
            if (index >=  0 && index < shapes.lstShapes.Count-1)
            {
                // 如果不是最前面的
                shapes.lstShapes.Remove(shape);
                shapes.lstShapes.Insert(index + 1, shape);
            }
        }
        public void backwardToEnd(int id)
        {
            // todo 添加命令。
            var shape = shapes.getShape(id);
            int index = shapes.lstShapes.IndexOf(shape);// 取得下标
            if (index >= 0 && index < shapes.lstShapes.Count - 1)
            {
                // 如果不是最前面的
                shapes.lstShapes.Remove(shape);
                shapes.lstShapes.Insert(shapes.lstShapes.Count-1, shape);
            }
        }


        public void forward()
        {
            //这里用当前的
            if (SelectShape != null)
            {
                forward(SelectShape.ID);
            }
        }
        public void forwardToFront()
        {
            if (SelectShape != null)
            {
                forwardToFront(SelectShape.ID);
            }
        }
        public void backward()
        {
            //这里用当前的
            if (SelectShape != null)
            {
                backward(SelectShape.ID);
            }
        }
        public void backwardToEnd()
        {
            //这里用当前的
            if (SelectShape != null)
            {
                backwardToEnd(SelectShape.ID);
            }
        }



        /// <summary>
        /// 组成群组
        /// </summary>
        /// <param name="shapes"></param>
        public ShapeGroup mergeGroup(List<ShapeEle> shapes)
        {
            // todo 添加命令。
            ShapeGroup group = new ShapeGroup();
            group.shapes.AddRange(shapes);
            group.ID = this.shapes.getNextId();
            foreach (var item in shapes)
            {
                this.shapes.lstShapes.Remove(item); // 从旧的里边删除。
            }
            addShape(group);
            return group;
        }

        /// <summary>
        /// 取消这个群组
        /// </summary>
        /// <param name="group"></param>
        public void cancelGroup(ShapeGroup group)
        {
            this.shapes.lstShapes.Remove(group);
            foreach (var item in group.shapes)
            {
                this.shapes.lstShapes.Add(item);
            }
        }

        /// <summary>
        /// 将选择的组成群组
        /// </summary>
        public void mergeGroup()
        {
            if (SelectShape is ShapeGroup)
            {
                var group =  mergeGroup(((ShapeGroup)SelectShape).shapes);
                changeSelect(group);
            }
            
        }

        /// <summary>
        /// 将选择的取消群组
        /// </summary>
        public void cancelGroup()
        {
            // 得判断
            if (SelectShape is ShapeGroup)
            {
                cancelGroup((ShapeGroup)SelectShape);
                changeSelect(null);// 取消选择
            }
        }

        #region 各种对齐


        /// <summary>
        /// 上对齐
        /// </summary>
        public void align_top() {
            if (this.SelectShape is Shape.ShapeMultiSelect)
            {
                // 
                float _y = ((Shape.ShapeMultiSelect)this.SelectShape).shapes[0].Y;
                foreach (var item in ((Shape.ShapeMultiSelect)this.SelectShape).shapes)
                {
                    item.Y = _y;
                }
                this.Refresh();
            }
        
        }

        /// <summary>
        /// 下对齐
        /// </summary>
        public void align_bottom() {

            if (this.SelectShape is Shape.ShapeMultiSelect)
            {
                // 
                float _y = ((Shape.ShapeMultiSelect)this.SelectShape).shapes[0].Y;
                float _height = ((Shape.ShapeMultiSelect)this.SelectShape).shapes[0].Height;
                float _y_height = _y + _height;
                foreach (var item in ((Shape.ShapeMultiSelect)this.SelectShape).shapes)
                {
                    item.Y = _y_height - item.Height; // 减去高度，得到新的y
                }
                this.Refresh();
            }
        }


        /// <summary>
        /// 左对齐
        /// </summary>
        public void align_left() {
            if (this.SelectShape is Shape.ShapeMultiSelect)
            {
                // 
                float _x = ((Shape.ShapeMultiSelect)this.SelectShape).shapes[0].X;
                foreach (var item in ((Shape.ShapeMultiSelect)this.SelectShape).shapes)
                {
                    item.X = _x;
                }
                this.Refresh();
            }

        }

        /// <summary>
        /// 右对齐
        /// </summary>
        public void align_right()
        {
            if (this.SelectShape is Shape.ShapeMultiSelect)
            {
                float _x = ((Shape.ShapeMultiSelect)this.SelectShape).shapes[0].X;
                float _width = ((Shape.ShapeMultiSelect)this.SelectShape).shapes[0].Width;
                float _y_width = _x + _width;
                foreach (var item in ((Shape.ShapeMultiSelect)this.SelectShape).shapes)
                {
                    item.X = _y_width - item.Width; // 减去高度，得到新的y
                }
                this.Refresh();
            }
        }

        /// <summary>
        /// 水平居中。
        /// </summary>
        public void align_center() {
            if (this.SelectShape is ShapeMultiSelect)
            {
                // 首先取得哪个的宽度是最宽的
                float _x=0, _width=0;
                foreach (var item in ((Shape.ShapeMultiSelect)this.SelectShape).shapes)
                {
                    if (item.Width > _width)
                    {
                        _x = item.X;
                        _width = item.Width;
                    }
                }

                // 然后所有的都超这个对齐
                float _center = _x + _width / 2;
                foreach (var item in ((Shape.ShapeMultiSelect)this.SelectShape).shapes)
                {
                    item.X = _center - item.Width / 2;
                }

                this.Refresh();

            }
        
        }

        /// <summary>
        /// 垂直居中。
        /// </summary>
        public void align_midele() {
            if (this.SelectShape is ShapeMultiSelect)
            {
                // 首先取得哪个的宽度是最宽的
                float _y = 0, _height = 0;
                foreach (var item in ((Shape.ShapeMultiSelect)this.SelectShape).shapes)
                {
                    if (item.Height > _height)
                    {
                        _y = item.Y;
                        _height = item.Height;
                    }
                }

                // 然后所有的都超这个对齐
                float _midele = _y + _height / 2;
                foreach (var item in ((Shape.ShapeMultiSelect)this.SelectShape).shapes)
                {
                    item.Y = _midele - item.Height / 2;
                }

                this.Refresh();

            }
        }

        #endregion

        #region 撤销重做
        /// <summary>
        /// 撤销
        /// </summary>
        public void undo()
        {
            this.commandRecorder.Undo();
            this.Refresh();
        }

        public void redo()
        {
            this.commandRecorder.Redo();
            this.Refresh();
        }

        #endregion

        #region 剪切复制粘贴

        /// <summary>
        /// 复制的保存在这里
        /// </summary>
        private ShapeEle _copy_shape;

        /// <summary>
        /// 剪切
        /// </summary>
        public void cut()
        {
            // 
            if (SelectShape != null)
            {
                _copy_shape = SelectShape.DeepClone();// 先保存
                deleteShapes(SelectShape);// 删除
            }
        }

        /// <summary>
        /// 复制
        /// </summary>
        public void copy()
        {
            if (SelectShape != null)
            {
                _copy_shape = SelectShape.DeepClone();// 跟剪切的区别是这里不删除。
            }
        }

        /// <summary>
        /// 粘贴
        /// </summary>
        public void paste()
        {
            // 我这里先思考一下思路，这个本质上是复制，
            // 首先要判断一下是否是ShapeMultiSelect，如果是，表示要复制内部的，而如果不是，那么只是复制这一个。
            if (_copy_shape is Shape.ShapeMultiSelect)
            {
                ShapeMultiSelect shapeMultiSelect = new ShapeMultiSelect();
                // 这个里边所有的形状都复制
                foreach (var item in ((ShapeMultiSelect)_copy_shape).shapes)
                {
                    // 说明这里就一个形状
                    addShape(item);
                    currectIdAndYadd(item, 5);  // 这个会更新id以及向下移动
                    shapeMultiSelect.shapes.Add(item);
                }
                //
                changeSelect(shapeMultiSelect);
            }
            else
            {
                // 说明这里就一个形状
                addShape(_copy_shape);
                currectIdAndYadd(_copy_shape, 5);  // 这个会更新id以及向下移动
                changeSelect(_copy_shape);         // 这个是当前的id
            }

            // 设置新的。
            _copy_shape = _copy_shape.DeepClone();

            this.Refresh();

        }

        /// <summary>
        /// 矫正id的，并且矫正的需要y加上一个指定的数字的。
        /// </summary>
        /// <param name="shape"></param>
        /// <param name="y_add"></param>
        private void  currectIdAndYadd(ShapeEle shape, float y_add)
        {
            if (shape is ShapeMulti)
            {
                foreach (var item in ((ShapeMultiSelect)shape).shapes)
                {
                    currectIdAndYadd(item, y_add);
                }
            }
            else
            {
                shape.ID = shapes.getNextId();
                shape.Y += y_add;
            }
        }

        #endregion

        public void selectAll()
        {
            // todo 全选
        }



        #endregion

        public void setVars(Dictionary<string , string> dict)
        {
            this.shapes.Vars = dict;
            this.Refresh();
        }


        #region 小函数

        /// <summary>
        /// 对齐网格后的坐标，这个主要是提供给更改尺寸的。
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public PointF gridAlign(PointF point)
        {
            // 这里首先判断是否需要对齐网格
            if (isAlignDridding)
            {
                // 这里需要转换坐标
                var p1 = this.shapes.pointTransform.CanvasToVirtualPoint(point); // 先转成虚拟的坐标
                var p2 = new PointF()
                {
                    X = ((int)Math.Round(p1.X / GriddingInterval, 0)) * GriddingInterval,     // 对齐,四舍五入五
                    Y = ((int)Math.Round(p1.Y / GriddingInterval, 0)) * GriddingInterval,
                };
                return this.shapes.pointTransform.VirtualToCanvasPoint(p2);      // 转成画布的坐标

            }
            else
            {
                return point; // 不用对齐，直接返回原先的。
            }
        }

        #endregion

    }
}
