using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace AStarPathFinding
{
    //타일 속성
    //기본, 출발점, 목적지, 벽, 열린리스트, 닫힌리스트, 길 
    public enum eTileState
    { None, Start, Goal, Wall, Road, RRoad, LRoad, URoad, DRoad, Open, Close, Path }

    public partial class Form1 : Form
    {
        public static string Direction { get; set; } // 방향 설정!!!

        //구조체=====================
        struct stCurrentAction//사용자의 세팅(타일배치)종류 확인용 구조체
        {
            public object sender;//호출한 버튼
            public eTileState actionType;//세팅 타입
            public stCurrentAction(object sender, eTileState type)
            {
                this.sender = sender;
                this.actionType = type;
            }
        }
        //======================구조체

        //상수=======================
        const int TABLE_SIZE = 16;//테이블 사이즈 기본 값 16
        readonly Point defaultStartPoint = new Point(5, 6);//디폴트 시작점
        readonly Point defaultGoalPoint = new Point(9, 6);//디폴트 목적지
        readonly Point[] defaultWallPoint = new Point[3]//디폴트 벽 위치
            { new Point(7, 5), new Point(7, 6), new Point(7, 7) };
        //readonly Point[] defaultRoadPoint = new Point[] { };



        readonly Color defaultButtonColor;//디폴트 버튼 색
        //========================상수

        //렌더링======================
        readonly int tableNodeSize;//노드사이즈
        readonly Pen pen = new Pen(Color.DimGray, 1);//선 그리기용
        readonly Font font = new Font("바탕", 10, FontStyle.Regular);//폰트
        //=======================렌더링

        //길찾기=======================
        eTileState[,] tableData;//테이블 데이터
        AstarPathFinder pathFinder;//길찾기 클래스
        Point startPoint;//시작점
        Point goalPoint;//목표점
        
        //=======================길찾기

        //세팅=========================
        stCurrentAction? curAction = null;//현재 사용자가 선택한 배치타입
        //=========================세팅


        protected override CreateParams CreateParams
        { get { var cp = base.CreateParams; cp.ExStyle |= 0x02000000; return cp; } }

        public Form1()
        {
            InitializeComponent();

            defaultButtonColor = Color.LightGray;

            tableNodeSize = pn_Table.Size.Width / TABLE_SIZE;

            startPoint = defaultStartPoint;
            goalPoint = defaultGoalPoint;

            tableData = new eTileState[TABLE_SIZE, TABLE_SIZE];
            SetDefault();

            pathFinder = new AstarPathFinder(new Point(TABLE_SIZE, TABLE_SIZE), tableData);
        }

        //테이블 초기화
        void SetDefault()
        {
            for(int i = 0; i < TABLE_SIZE; i++)
            {
                for(int j = 0; j < TABLE_SIZE; j++)
                {
                    tableData[i, j] = eTileState.None;
                }
            }

            tableData[defaultStartPoint.X, defaultStartPoint.Y] = eTileState.Start;
            tableData[defaultGoalPoint.X, defaultGoalPoint.Y] = eTileState.Goal;

            foreach(Point iter in defaultWallPoint)
                tableData[iter.X, iter.Y] = eTileState.Wall;

            startPoint = defaultStartPoint;
            goalPoint = defaultGoalPoint;
        }

        //노드 그리기
        void DrawNode(PaintEventArgs e, Color color, AstarNode node)
        {
            Rectangle rect = new Rectangle(node.axis.X * tableNodeSize, node.axis.Y * tableNodeSize, tableNodeSize, tableNodeSize);

            e.Graphics.FillRectangle(new SolidBrush(color), rect);
            e.Graphics.DrawRectangle(pen, rect);

            e.Graphics.DrawString(node.F.ToString(), font, Brushes.Black, rect.X + 1, rect.Y + 4);
            e.Graphics.DrawString(node.G.ToString(), font, Brushes.Black, rect.X + 1, rect.Y + tableNodeSize * .7f);
            e.Graphics.DrawString(node.H.ToString(), font, Brushes.Black, rect.X + tableNodeSize - 1, rect.Y + tableNodeSize * .7f, new StringFormat(StringFormatFlags.DirectionRightToLeft));

            if(node.parent != null)
            {
                Point center = new Point(rect.X + tableNodeSize / 2, rect.Y + tableNodeSize / 2);
                RectangleF centerRect = new RectangleF(new PointF(center.X - 2.5f, center.Y - 2.5f), new SizeF(5, 5));
                e.Graphics.DrawLine(pen, center, pathFinder.AddPoint(center, node.ParentDirection, -8));
                e.Graphics.FillRectangle(new SolidBrush(Color.DimGray), centerRect);
            }
        }

        /// <summary>
        /// 버튼 선택시 들어옴, 현재 선택한 배치타입 설정
        /// </summary>
        /// <param name="sender">null 일경우 초기화</param>
        /// <param name="type">None 일경우 취소</param>
        void SetCurrentAction(object sender, eTileState type)
        {
            if(type != eTileState.Open)//오픈은 스텝모드,,..
                pathFinder.Initialize();

            if(curAction != null)
            {
                (curAction.Value.sender as Button).BackColor = defaultButtonColor;

                if(type == eTileState.None || type == curAction.Value.actionType)
                {
                    curAction = null;
                    return;
                }
            }

            if(sender != null)
            {
                curAction = new stCurrentAction(sender, type);

                if(type != eTileState.None)
                    (sender as Button).BackColor = Color.LightSeaGreen;
            }
        }

        private void pn_Table_Paint(object sender, PaintEventArgs e)
        {
            for(int i = 0; i < TABLE_SIZE; i++)
            {
                for(int j = 0; j < TABLE_SIZE; j++) 
                {
                    Color color = new Color();

                    switch(tableData[i, j])
                    {
                        case eTileState.None:
                            break;
                        case eTileState.Start:
                            color = Color.IndianRed;
                            break;
                        case eTileState.Goal:
                            color = Color.LightGreen;
                            break;
                        case eTileState.Wall:
                            color = Color.CornflowerBlue;
                            break;
                        case eTileState.Open:
                            color = Color.LemonChiffon;
                            break;
                        case eTileState.Close:
                            color = Color.BurlyWood;
                            break;
                        case eTileState.Path:
                            color = Color.Plum;
                            break;
                        case eTileState.Road:
                            color = Color.Yellow;
                            break;
                        case eTileState.LRoad:
                            color = Color.DarkRed;
                            break;
                        case eTileState.RRoad:
                            color = Color.DarkBlue;
                            break;
                        case eTileState.URoad:
                            color = Color.Bisque;
                            break;
                        case eTileState.DRoad:
                            color = Color.BlueViolet;
                            break;
                    }
                    DrawNode(e, color, pathFinder.GetAstarNode(new Point(i, j)));
                }
            }
        }

        private void btn_SetStartPoint_Click(object sender, EventArgs e)
        {
            SetCurrentAction(sender, eTileState.Start);
        }

        private void btn_SetGoalPoint_Click(object sender, EventArgs e)
        {
            SetCurrentAction(sender, eTileState.Goal);
        }

        private void btn_SetWallPoint_Click(object sender, EventArgs e)
        {
            SetCurrentAction(sender, eTileState.Wall);
        }

        private void btn_SetDefault_Click(object sender, EventArgs e)
        {
            SetCurrentAction(sender, eTileState.None);

            SetDefault();
            pn_Table.Refresh();
        }

        private void btn_SetRoadPoint_Click(object sender, EventArgs e)
        {
            SetCurrentAction(sender, eTileState.Road);
        }

        private void btn_SetLeftRoadPoint_Click(object sender, EventArgs e)
        {
            SetCurrentAction(sender, eTileState.LRoad);
        }

        private void btn_SetRightRoadPoint_Click(object sender, EventArgs e)
        {
            SetCurrentAction(sender, eTileState.RRoad);
        }

        private void btn_SetUpRoadPoint_Click(object sender, EventArgs e)
        {
            SetCurrentAction(sender, eTileState.URoad);
        }

        private void btn_SetDownRoadPoint_Click(object sender, EventArgs e)
        {
            SetCurrentAction(sender, eTileState.DRoad);
        }

        private void pn_Table_MouseDown(object sender, MouseEventArgs e)
        {
            int x = e.X / tableNodeSize;
            int y = e.Y / tableNodeSize;

            if(tableData[x, y] == eTileState.Start || tableData[x, y] == eTileState.Goal)
            {
                return;
            }

            if(curAction != null)
            {
                switch(curAction.Value.actionType)
                {
                    case eTileState.Start:
                        if(tableData[x, y] == eTileState.Wall) break;

                        tableData[startPoint.X, startPoint.Y] = eTileState.None;

                        startPoint = new Point(x, y);
                        SetCurrentAction(null, eTileState.None);
                        tableData[startPoint.X, startPoint.Y] = eTileState.Start;

                        break;
                    case eTileState.Goal:
                        if(tableData[x, y] == eTileState.Wall) break;

                        tableData[goalPoint.X, goalPoint.Y] = eTileState.None;

                        goalPoint = new Point(x, y);
                        SetCurrentAction(null, eTileState.None);
                        tableData[goalPoint.X, goalPoint.Y] = eTileState.Goal;

                        break;
                    case eTileState.Wall:
                        if(tableData[x, y] == eTileState.Start || tableData[x, y] == eTileState.Goal)
                            break;

                        if(tableData[x, y] == eTileState.Wall)
                        {
                            tableData[x, y] = eTileState.None;
                            break;
                        }

                        tableData[x, y] = eTileState.Wall;
                        break;
                    case eTileState.Road:
                        if (tableData[x, y] == eTileState.Start || tableData[x, y] == eTileState.Goal)
                            break;

                        if (tableData[x, y] == eTileState.Road)
                        {
                            tableData[x, y] = eTileState.None;
                            break;
                        }

                        tableData[x, y] = eTileState.Road;
                        break;
                    case eTileState.LRoad:
                        if (tableData[x, y] == eTileState.Start || tableData[x, y] == eTileState.Goal)
                            break;

                        if (tableData[x, y] == eTileState.LRoad)
                        {
                            tableData[x, y] = eTileState.None;
                            break;
                        }

                        tableData[x, y] = eTileState.LRoad;
                        break;
                    case eTileState.RRoad:
                        if (tableData[x, y] == eTileState.Start || tableData[x, y] == eTileState.Goal)
                            break;

                        if (tableData[x, y] == eTileState.RRoad)
                        {
                            tableData[x, y] = eTileState.None;
                            break;
                        }

                        tableData[x, y] = eTileState.RRoad;
                        break;                    
                    case eTileState.URoad:
                        if (tableData[x, y] == eTileState.Start || tableData[x, y] == eTileState.Goal)
                            break;

                        if (tableData[x, y] == eTileState.URoad)
                        {
                            tableData[x, y] = eTileState.None;
                            break;
                        }

                        tableData[x, y] = eTileState.URoad;
                        break;                    
                    case eTileState.DRoad:
                        if (tableData[x, y] == eTileState.Start || tableData[x, y] == eTileState.Goal)
                            break;

                        if (tableData[x, y] == eTileState.DRoad)
                        {
                            tableData[x, y] = eTileState.None;
                            break;
                        }

                        tableData[x, y] = eTileState.DRoad;
                        break;
                    default:
                        break;
                }
            }
            (sender as Panel).Refresh();
        }

        private void pn_Table_MouseMove(object sender, MouseEventArgs e)
        {
            //if(e.Button == MouseButtons.Left)
            //{
            //    int x = e.X / tableNodeSize;
            //    int y = e.Y / tableNodeSize;

            //    if (this.tableData[x, y] == eTileState.Start || this.tableData[x, y] == eTileState.Goal)
            //    {
            //        return;
            //    }

            //    if (curAction != null)
            //    {
            //        switch (curAction.Value.actionType)
            //        {
            //            case eTileState.Road:
            //                if (tableData[x, y] == eTileState.Start || tableData[x, y] == eTileState.Goal)
            //                    break;

            //                if (tableData[x, y] == eTileState.Road)
            //                {
            //                    tableData[x, y] = eTileState.None;
            //                    break;
            //                }

            //                tableData[x, y] = eTileState.Road;
            //                break;
            //            default:
            //                break;
            //        }
            //    }
            //    (sender as Panel).Refresh();
            //}
        }

        public bool clickState { get; set; }

        private void btn_PathFind_Click(object sender, EventArgs e)
        {
            //SetCurrentAction(null, eTileState.None);
            //pathFinder.PathFind(pn_Table, startPoint, goalPoint);

            {
                if (clickState)
                {
                    SetCurrentAction(null, eTileState.None);
                    pathFinder.PathFind(pn_Table, startPoint, goalPoint);
                    clickState = false;
                    Direction = "Up";
                }
                else
                {
                    SetCurrentAction(null, eTileState.None);
                    pathFinder.PathFind(pn_Table, goalPoint, startPoint);
                    clickState = true;
                    Direction = "Up";
                }
            }
        }

        private void btn_Step_Click(object sender, EventArgs e)
        {
            SetCurrentAction(null, eTileState.Open);

            pathFinder.PathFind(pn_Table, startPoint, goalPoint, true);
        }
    }
}