using CommonAI.RTS;
using CommonLang.Vector;
using CommonAI.RTS.Manhattan;
using CommonAI.Zone;
using CommonAI.Zone.ZoneEditor;
using CommonLang;
using GameEditorPlugin.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CommonAI.Zone.Helper;
using CommonLang.Property;

namespace GameEditorPlugin.Tools
{
    public partial class FormAstar : Form
    {
        private AStarTerrain display;
        private Vector2 mPathBegin;
		public static Vector2 pic_lastMouesPos = new Vector2(0, 0);
		private string mScenePath;

        public FormAstar(string scene_path = null, TerrainDefinitionMap define = null, SceneData zone = null)
        {
            InitializeComponent();
            this.mScenePath = scene_path;
            this.display = new AStarTerrain(define);
            try
            {
                display.RandomSeed = DateTime.Now.Millisecond;
                display.Reset(zone);
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message);
            }
        }


        private void FormAstar_Load(object sender, EventArgs e)
        {
            this.displayTerrainPanel1.Display = display;
            this.display.PathFinder.SpaceDivEnableDistance = (int)num_EnableSpaceDivDistance.Value;
            this.display.PathFinder.SpaceOptimizePathLimitDistance = (int)num_OptimizePathLimit.Value;
            this.display.SetSpaceDiv((int)num_SpaceDivDistance.Value);
            this.display.GenShowItems(toolStripDropDownButton3);
        }


        private void resetToolStripMenuItem_Click(object sender, EventArgs e)
        {
            display.Reset();
        }
        private void btn_LoadScene_Click(object sender, EventArgs e)
        {
            OpenFileDialog fd = new OpenFileDialog();
            if (mScenePath != null)
            {
                fd.InitialDirectory = mScenePath;
            }
            if (fd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                try
                {
                    string text = File.ReadAllText(fd.FileName);
                    SceneData sd = EditorTemplates.DataFromXML<SceneData>(text);
                    display.Reset(sd);
                }
                catch (Exception err)
                {
                    MessageBox.Show(err.Message);
                }
            }
        }

        private void btn_FillTerrain_Click(object sender, EventArgs e)
        {
            btn_CleanTerrain.Checked = false;
        }
        private void btn_CleanTerrain_Click(object sender, EventArgs e)
        {
            btn_FillTerrain.Checked = false;
        }
        private void num_SpaceDivDistance_ValueChanged(object sender, EventArgs e)
        {
            this.display.SetSpaceDiv((int)num_SpaceDivDistance.Value);
        }
        private void num_EnableSpaceDivDistance_ValueChanged(object sender, EventArgs e)
        {
            this.display.PathFinder.SpaceDivEnableDistance = (int)num_EnableSpaceDivDistance.Value;
        }
        private void num_OptimizePathLimit_ValueChanged(object sender, EventArgs e)
        {
            this.display.PathFinder.SpaceOptimizePathLimitDistance = (int)num_OptimizePathLimit.Value;
        }


        private void displayTerrainPanel1_PictureBoxMouseDown(object sender, MouseEventArgs e)
        {
            float wx = display.screenToWorldX(e.X);
            float wy = display.screenToWorldY(e.Y);
			pic_lastMouesPos.SetX(wx);
			pic_lastMouesPos.SetY(wy);

			if (btn_FillTerrain.Checked || btn_CleanTerrain.Checked)
            {
                display.ShowPoint = true;
                int size = int.Parse(com_FillTerrainSize.Text);
                display.SetPointer(wx, wy, size);
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    display.FillTerrain(btn_FillTerrain.Checked);
                }
            }
            else if (mPathBegin == null)
            {
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    mPathBegin = new Vector2(e.X, e.Y);
                    display.BeginFindPath(wx, wy);
                }
            }
            else if (!btn_FreeMove.Checked)
            {
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    mPathBegin = null;
                    display.EndFindPath(wx, wy, btn_Optimize.Checked);
                }
            }
        }
        private void displayTerrainPanel1_PictureBoxMouseMove(object sender, MouseEventArgs e)
        {
            float wx = display.screenToWorldX(e.X);
            float wy = display.screenToWorldY(e.Y);
			pic_lastMouesPos.SetX(wx);
			pic_lastMouesPos.SetY(wy);

			display.ShowPoint = false;
            if (btn_FillTerrain.Checked || btn_CleanTerrain.Checked)
            {
                display.ShowPoint = true;
                int size = int.Parse(com_FillTerrainSize.Text);
                display.SetPointer(wx, wy, size);
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    display.FillTerrain(btn_FillTerrain.Checked);
                }
            }
            else if (btn_FreeMove.Checked)
            {
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    if (mPathBegin != null)
                    {
                        display.EndFindPath(wx, wy, btn_Optimize.Checked);
                    }
                }
            }
        }
        private void displayTerrainPanel1_PictureBoxMouseUp(object sender, MouseEventArgs e)
        {
            float wx = display.screenToWorldX(e.X);
            float wy = display.screenToWorldY(e.Y);
            if (btn_FillTerrain.Checked)
            {
            }
            else if (btn_FreeMove.Checked)
            {
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                {
                    if (mPathBegin != null)
                    {
                        mPathBegin = null;
                        display.EndFindPath(wx, wy, btn_Optimize.Checked);
                    }
                }
            }
        }


        class AStarTerrain : DisplayManhattanTerrain
        {
            [Desc("显示点", "show")]
            public bool ShowPoint = true;

            private const int BLOCK_VALUE = (int)0x7F00FF00;
            private int random_seed_index = 1;

            private ZoneInfo zone_data;
            private TerrainDefinitionMap define;
            private AstarManhattan path_finder;
            private AstarManhattan.MWayPoint current_path;
            private Vector2 path_begin;
            private Vector2 path_end;

            private long last_pathfind_tick = 0;
            private long last_touchmap_tick = 0;

            private Vector2 point_pos = new Vector2();
            private int point_size = 1;

            public override AstarManhattan PathFinder
            {
                get { return path_finder; }
            }
            public override int SpaceDivSize
            {
                get { return path_finder.SpaceDivSize; }
            }
            public int RandomSeed
            {
                get { return random_seed_index; }
                set { random_seed_index = value; }
            }

            public AStarTerrain(TerrainDefinitionMap define)
            {
                this.define = define;
                this.ShowSpaceDiv = true;
                this.ShowTerrainMesh = true;
            }
            public void SetSpaceDiv(int value)
            {
                if (zone_data != null)
                {
                    if (path_finder == null || path_finder.SpaceDivSize != value)
                    {
                        this.path_finder = new AstarManhattan(this.zone_data.TemplateID, new EditorZoneManhattanMap(zone_data, define), true, value);
                    }
                }
            }
            public void Reset(SceneData sd = null)
            {
                if (sd == null)
                {
                    Random random = new Random(random_seed_index);
                    random_seed_index++;

                    this.zone_data = new ZoneInfo(random.Next(50, 200), random.Next(50, 200), 32, 32);
                    for (int x = 0; x < zone_data.XCount; x++)
                    {
                        for (int y = 0; y < zone_data.YCount; y++)
                        {
                            zone_data.TerrainMatrix[x, y] = CUtils.RandomPercent(random, 85) ? 0 : BLOCK_VALUE;
                        }
                    }
                    this.InitTerrain(zone_data);
                }
                else
                {
                    zone_data = (ZoneInfo)sd.ZoneData.Clone();
                    //                     zone_data.mGridCellW = sd.;
                    //                     zone_data.mGridCellH = 32;
                    this.InitTerrain(zone_data);
                }
            }
            public override void Dispose()
            {

            }
            //--------------------------------------------------------------------------------------
            protected override void InitTerrain(ZoneInfo zone)
            {
                base.InitTerrain(zone);
                if (path_finder != null)
                {
                    path_finder.Dispose();
                }
                this.path_finder = new AstarManhattan(zone.TemplateID, new EditorZoneManhattanMap(zone, define), true, 8);
            }

            internal void SetPointer(float sx, float sy, int r)
            {
                point_pos.SetX(sx);
                point_pos.SetY(sy);
                point_size = r;
            }
            internal void BeginFindPath(float sx, float sy)
            {
                path_begin = new Vector2(sx, sy);
                path_end = null;
                current_path = null;
            }
            internal void EndFindPath(float dx, float dy, bool optimize)
            {
                path_end = new Vector2(dx, dy);
                {
                    var sw = Stopwatch.StartNew();
                    path_finder.findPath(path_begin.X, path_begin.Y, dx, dy, out current_path, optimize);
                    sw.Stop();
                    last_pathfind_tick = sw.ElapsedTicks;
                }
            }
            internal void FillTerrain(bool block)
            {
                int bx, by;
                path_finder.PosToBlock(point_pos.X, point_pos.Y, out bx, out by);
                path_finder.FillTerrainByBlock(
                        (bx - point_size / 2),
                        (by - point_size / 2),
                        (point_size),
                        (point_size),
                        block ? BLOCK_VALUE : 0);
            }

            //--------------------------------------------------------------------------------------

            #region Render

            private Pen path_pen = new Pen(Color.Red);
            private SolidBrush path_brush = new SolidBrush(Color.FromArgb(0x80, Color.Gray));

            private Pen pen_point = new Pen(Color.FromArgb(0xA0, 0xff, 0xff, 0xff));
            private Pen pen_begin = new Pen(Color.FromArgb(0xFF, 0, 0xff, 0));
            private Pen pen_space_div = new Pen(Color.FromArgb(0x20, 0xff, 0xff, 0xff));
            private Pen pen_space_div_next = new Pen(Color.FromArgb(0x20, 0xff, 0xff, 0xff));
            //private Brush brush_mesh = new SolidBrush(Color.FromArgb(0x80, 0xff, 0xff, 0xff));
            private Pen pen_path_begin = new Pen(Color.FromArgb(0x10, 0xFF, 0xFF, 0xFF));
            private Pen pen_ray = new Pen(Color.Yellow);

            public override void render(Graphics g)
            {
                pen_point.Width =
                pen_space_div_next.Width =
                pen_space_div.Width =
                pen_begin.Width =
                path_pen.Width =
                pen_path_begin.Width =
                pen_ray.Width =
                1 / getCameraScale();

                base.render(g);
            }

            protected override void renderTerrain(Graphics g, RectangleF rect)
            {
                base.renderTerrain(g, rect);

                if (ShowPoint)
                {
                    int bx, by;
                    path_finder.PosToBlock(point_pos.X, point_pos.Y, out bx, out by);
                    g.DrawRectangle(pen_point,
                        (bx - point_size / 2) * base.CellW,
                        (by - point_size / 2) * base.CellH,
                        (point_size) * base.CellW,
                        (point_size) * base.CellH);
                }
            }
            protected override void renderCell(Graphics g, int bx, int by, int flag)
            {
                base.renderCell(g, bx, by, flag);
                if (ShowSpaceDiv && path_finder != null && path_finder.SpaceDivSize > 0)
                {
                    if (bx % path_finder.SpaceDivSize == 0 && by % path_finder.SpaceDivSize == 0)
                    {
                        int cx = bx / path_finder.SpaceDivSize;
                        int cy = by / path_finder.SpaceDivSize;
                        var sn = path_finder.GetSpaceMapNode(cx, cy);
                        if (sn != null && sn.AnchorNode != null)
                        {
                            g.DrawRectangle(pen_space_div,
                                sn.AnchorNode.BX * CellW,
                                sn.AnchorNode.BY * CellH,
                                CellW,
                                CellH);
                            foreach (var next in sn.NextsSpaceNode)
                            {
                                var link = sn.GetNextLink(next);
                                if (link != null)
                                {
                                    renderPath(g, new Vector2(sn.AnchorNode.PosX, sn.AnchorNode.PosY), link, pen_space_div_next, pen_space_div_next);
                                }
                            }
                        }
                    }
                }
            }
            protected override void renderMeshCell(Graphics g, AstarManhattan.MMapNode tn)
            {
                //if (tn.Mesh != null)
                {
                    //g.FillRectangle(brush_mesh, tn.BX * CellW, tn.BY * CellH, CellW, CellH);
                    base.renderMeshCell(g, tn);
                }
            }

            protected void renderPath(Graphics g, Vector2 path_begin, AstarManhattan.MWayPoint path, Pen pen_line, Pen pen_begin = null, Brush pen_brush = null)
            {
                if (path_begin != null && pen_begin != null)
                {
                    g.DrawLine(pen_begin, path_begin.X, path_begin.Y, path.PosX, path.PosY);
                }
                if (path != null)
                {
                    foreach (AstarManhattan.MWayPoint wp in path)
                    {
                        if (pen_brush != null)
                        {
                            g.FillRectangle(pen_brush, wp.BX * CellW, wp.BY * CellH, CellW, CellH);
                        }
                        if (wp.Next != null)
                        {
                            g.DrawLine(pen_line, wp.PosX, wp.PosY, wp.Next.PosX, wp.Next.PosY);
                        }
                    }
                }
            }

            protected override void renderObjects(Graphics g, RectangleF worldBounds)
            {
                if (path_begin != null)
                {
                    g.DrawArc(path_pen, path_begin.X - 1, path_begin.Y - 1, 2, 2, 0, 360);
                }
                if (path_end != null)
                {
                    g.DrawArc(path_pen, path_end.X - 1, path_end.Y - 1, 2, 2, 0, 360);
                }
                if (path_begin != null && path_end != null)
                {
                    g.DrawLine(pen_path_begin, path_begin.X, path_begin.Y, path_end.X, path_end.Y);
                    float tx, ty, td;

                    Stopwatch watch = new Stopwatch();
                    watch.Start();
                    bool ret = (path_finder.GetLineTouchPoint(path_begin.X, path_begin.Y, path_end.X, path_end.Y, out tx, out ty, out td));
                    watch.Stop();
                    last_touchmap_tick = watch.ElapsedTicks;
                    if (ret)
                    {
                        g.DrawLine(pen_ray, path_begin.X, path_begin.Y, tx, ty);
                        g.DrawArc(pen_ray, tx - 1, ty - 1, 2, 2, 0, 360);
                    }
                    else
                    {
                        g.DrawLine(pen_ray, path_begin.X, path_begin.Y, path_end.X, path_end.Y);
                        g.DrawArc(pen_ray, path_end.X - 1, path_end.Y - 1, 2, 2, 0, 360);
                    }
                }
                if (current_path != null)
                {
                    renderPath(g, path_begin, current_path, path_pen, pen_begin, path_brush);
                }
            }

            protected override void renderSpaceDiv(Graphics g, RectangleF rect)
            {
                base.renderSpaceDiv(g, rect);
            }

            protected override void renderScreen(Graphics g, RectangleF worldBounds)
            {
				g.DrawString(
                    string.Format(" mnodes={0}/{1} mesh={2} wps={3} pathfind={4}(t) touchmap={5}(t), Mouse=({6},{7})",
                    AstarManhattan.MMapNode.ActiveObjectCount,
                    (PathFinder != null ? (PathFinder.MMap.XCount * PathFinder.MMap.YCount) : 0),
                    AstarManhattan.MMapNode.BlockMesh.ActiveObjectCount,
                    AstarManhattan.MWayPoint.ActiveObjectCount,
                    last_pathfind_tick,
                    last_touchmap_tick,
					pic_lastMouesPos.X,
					pic_lastMouesPos.Y
					),
                    Control.DefaultFont, new SolidBrush(Color.White), 10, 10);
            }

            #endregion


        }

        private void gCToolStripMenuItem_Click(object sender, EventArgs e)
        {
            GC.Collect();
        }
    }
}