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

namespace Vortex.Demo.Pacman {

    ///<summary>Possible types of dots on map</summary>
	public enum DotType {
        None,
        Small,
        Big
    };

    ///<summary></summary>
	public struct WorldMapItem {
        public MapItemType type;
        public short index;
    };

    public class WorldMap {
        WorldMapItem[,] m_cells;
        int m_width;
        int m_height;
        int m_DotCount;

        public int Width {
            get { return m_width; }
        }

        public int Height {
            get { return m_height; }
        }

        public int DotCount {
            get { return m_DotCount; }
        }

        public void Fill(MapItemType[,] itemTypes) {
            m_width = itemTypes.GetLength(0);
            m_height = itemTypes.GetLength(1);
            m_DotCount = 0;
            m_cells = new WorldMapItem[m_width, m_height];
            
            //go through dimensions and build map
            for(int n = 0; n < m_height; ++n) {
                for (int m = 0; m < m_width; ++m) {
                    MapItemType type = itemTypes[m, n];
                    m_cells[m, n].type = type;
                    //calculate dot counts
                    if (type == MapItemType.Dot || type == MapItemType.SuperDot) {
                        m_DotCount++;
                    }
                }
            }

            CalculateWallIndices();
        }

        private void CalculateWallIndices() {
            for (int n = 0; n < m_height; ++n) {
                for (int m = 0; m < m_width; ++m) {
                    MapItemType type = m_cells[m, n].type;
                    if (type == MapItemType.Wall) {
                        short index = CalcIndexForCell(m, n);
                        m_cells[m, n].index = index;
                    }
                }
            }
        }

        const int NO_MASK = 0, TOP_MASK = 1, RIGHT_MASK = 2, BOTTOM_MASK = 4, LEFT_MASK = 8;
        private short CalcIndexForCell(int x, int y) {
            bool nbAtTop = IsWall(x, y - 1), nbAtRight = IsWall(x + 1, y),
                 nbAtBottom = IsWall(x, y + 1), nbAtLeft = IsWall(x - 1, y);

            int nbCount = (nbAtTop ? 1 : 0) + (nbAtRight ? 1 : 0) + (nbAtBottom ? 1 : 0) + (nbAtLeft ? 1 : 0);
            if (nbCount > 2) {
                if (nbCount == 3) {
                    nbAtTop = nbAtTop && ((nbAtTop && nbAtBottom) || !CheckContinousWallAt(x, y - 1, false));
                    nbAtBottom = nbAtBottom && ((nbAtTop && nbAtBottom) || !CheckContinousWallAt(x, y + 1, false));
                    nbAtRight = nbAtRight && ((nbAtRight && nbAtLeft) || !CheckContinousWallAt(x + 1, y, true));
                    nbAtLeft = nbAtLeft && ((nbAtRight && nbAtLeft) || !CheckContinousWallAt(x - 1, y, true));
                }
                else {
                    nbAtTop = !CheckContinousWallAt(x, y - 1, false);
                    nbAtRight = !CheckContinousWallAt(x + 1, y, true);
                    nbAtBottom = !CheckContinousWallAt(x, y + 1, false);
                    nbAtLeft = !CheckContinousWallAt(x - 1, y, true);
                }
            }
            return (short)((nbAtTop ? TOP_MASK : NO_MASK) | (nbAtRight ? RIGHT_MASK : NO_MASK) | (nbAtBottom ? BOTTOM_MASK : NO_MASK) | (nbAtLeft ? LEFT_MASK : NO_MASK));
        }

        private bool CheckContinousWallAt(int x, int y, bool vertical) {
            if (vertical) {
                return IsWall(x, y) && IsWall(x, y - 1) && IsWall(x, y + 1);
            } else {
                return IsWall(x, y) && IsWall(x - 1, y) && IsWall(x + 1, y);
            }
        }

        public bool IsWall(int x, int y) {
            return InMap(x, y) && this[x, y].type == MapItemType.Wall;
        }

        public WorldMapItem this[int x, int y] {
            get { return m_cells[x, y]; }
        }

        public bool InMap(int x, int y) {
            return x >= 0 && y >= 0 && x < m_width && y < m_height;
        }

        public bool IsAccessible(int x, int y) {
            return !IsWall(x, y);
        }

        public DotType GrabCell(int x, int y) {
            if (InMap(x, y)) {
                if (m_cells[x, y].type == MapItemType.Dot) {
                    m_cells[x, y].type = MapItemType.Blank;
                    m_DotCount--;
                    return DotType.Small;
                } else
                if (m_cells[x, y].type == MapItemType.SuperDot) {
                    m_cells[x, y].type = MapItemType.Blank;
                    m_DotCount--;
                    return DotType.Big;
                }
            }
            return DotType.None;
        }

        public TraceMap ToTraceMap() {
            TraceMap traceMap = new TraceMap(Width, Height);
            for (int n = 0; n < Height; ++n) {
                for (int m = 0; m < Width; ++m) {
                    traceMap[m, n] = (short)((m_cells[m, n].type == MapItemType.Wall) ? TraceMap.UNPASSABLE : TraceMap.AVAILABLE);
                }
            }
            return traceMap;
        }
    }

}
