using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace Vortex.Demo.Pacman {
    
    public class TraceMap {
        public const short UNPASSABLE = -1;
        public const short AVAILABLE = 0;

        short[,] m_Map;
        int m_Width, m_Height;

        public TraceMap(int width, int height) {
            m_Width = width;
            m_Height = height;
            m_Map = new short[width, height];
        }

        public short this[Point point] {
            get { return m_Map[point.X, point.Y]; }
            set { m_Map[point.X, point.Y] = value; }
        }

        public short this[int x, int y] {
            get { return m_Map[x, y]; }
            set { m_Map[x, y] = value; }
        }

        public bool Trace(Point from, Point to) {
            short iteration = 1;
            Point position = from;
            this[from] = iteration;

            for (; ;) {
                //check if no exit here
                if (iteration > m_Height * m_Width) {
                    return false;
                }
                for (int n = 0; n < m_Height; ++n) {
                    for (int m = 0; m < m_Width; ++m) {
                        if (this[m, n] == iteration) {
                            if (to.X == m && to.Y == n) {
                                return true;
                            }
                            MarkAround(m, n, (short)(iteration + 1));
                        }
                    }
                }
                iteration++;
            }
        }

        private void MarkAround(int x, int y, short iterationValue) {
            Mark(x + 1, y, iterationValue);
            Mark(x, y + 1, iterationValue);
            Mark(x, y - 1, iterationValue);
            Mark(x - 1, y, iterationValue);
        }

        private void Mark(int x, int y, short iterationValue) {
            if (x >= 0 && x < m_Width && y >= 0 && y < m_Height) {
                if (m_Map[x, y] == AVAILABLE) {
                    m_Map[x, y] = iterationValue;
                }
            }
        }

        public List<Point> GetPath(Point from, Point to) {
            List<Point> locationPath = new List<Point>();

            if (from == to) {
                return locationPath;
            }

            Point pos = to;
            short lookupValue = this[pos];
            
            while (pos != from) {
                locationPath.Add(pos);
                lookupValue--;
                pos = LookForValueAround(pos, lookupValue);
            }

            locationPath.Reverse();
            return locationPath;
        }

        ///<summary>Looks for minimal point around. If it is start point -> terminate recursion</summary>
        private Point LookForValueAround(Point atPoint, short value) {
            if (MatchValue(atPoint.X + 1, atPoint.Y, value)) {
                return new Point(atPoint.X + 1, atPoint.Y);
            }
            if (MatchValue(atPoint.X - 1, atPoint.Y, value)) {
                return new Point(atPoint.X - 1, atPoint.Y);
            }
            if (MatchValue(atPoint.X, atPoint.Y + 1, value)) {
                return new Point(atPoint.X, atPoint.Y + 1);
            }
            if (MatchValue(atPoint.X, atPoint.Y - 1, value)) {
                return new Point(atPoint.X, atPoint.Y - 1);
            }
            throw new Exception("Can't find value around");
        }

        private bool MatchValue(int x, int y, short value) {
            if (x >= 0 && x < m_Width && y >= 0 && y < m_Height) {
                return m_Map[x, y] == value;
            } else {
                return false;
            }
        }
    }

}
