using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class GridManager
{
    public const int MAP_SIZE_X = 64;
    public const int MAP_SIZE_Y = 64;
    public const float UNITY_GRID_SIZE = 1.0f;
    public const float UNITY_BOARD_DEPTH = 0.5f;
    public const float UNITY_CHARACTER_DEPTH = 0.05f;

    public HashSet<GridObject> m_objectList;
    private static GridManager ms_instance;

    public enum TerrainType
    {
        Grass,
        Mountain,
        Solid
    }

    private TerrainType[,] m_terrain;

    public static GridManager Instance
    {
        get { return ms_instance;  }
    }

    public GridManager()
    {
        if (ms_instance != null)
        {
            Debug.LogError("Only one gridman allowed");
        }
        ms_instance = this;

        m_objectList = new HashSet<GridObject>();
        m_terrain = new TerrainType[MAP_SIZE_X, MAP_SIZE_Y];
    }

    public TerrainType GetTerrainType( GridCoord pos )
    {
        return m_terrain[pos.x, pos.y];
    }

    public void AddObject( GridObject obj )
    {
        m_objectList.Add(obj);
    }

    public void RemoveObject( GridObject obj )
    {
        m_objectList.Remove(obj);
    }

    public bool TestCollision( GridCoord pos, out GridObject retObj, bool checkTerrain )
    {
        if (checkTerrain)
        {
            if (IsTerrainPassable(pos) == false)
            {
                retObj = null;
                return true;
            }
        }

        // TODO - Optimize this. Could just keep things sorted on one axis, which would be easy.
        foreach( GridObject obj in m_objectList )
        {
            if ( pos == obj.GridPosition )
            {
                retObj = obj;
                return true;
            }
        }

        retObj = null;
        return false;
    }


    public Dictionary<GridCoord,int> GetValidMoves(GridObject gridObj, GridCoord startPos, int movesAllowed)
    {
        // Nice, I get the axis sorting for free here, since GridCoord returns a hash value!
        Dictionary<GridCoord, int> validList = new Dictionary<GridCoord, int>();

        GetValidMovesRecurse(gridObj, startPos, validList, movesAllowed, true);
        return validList;
    }

    private bool IsTerrainPassable( GridCoord pos )
    {
        // TODO: Deal with characters that can move over 'solid' terrain. i.e. water/walls
        return GetTerrainType(pos) != TerrainType.Solid;
    }

    private int GetTerrainCost( GridCoord pos )
    {
        switch ( GetTerrainType(pos) )
        {
            case TerrainType.Mountain: return 2;
            default: return 1;
        }
    }

    private void GetValidMovesRecurse(GridObject gridObj, GridCoord curPos, Dictionary<GridCoord, int> validList, int movesLeft, bool isMoving)
    {
        // TODO: May need to optimize this

        // All moves gone?
        if (movesLeft <= 0 )
        {
            //Debug.Log(curPos.x + " " + curPos.y + " " + movesLeft + " outofmoves");
            return;
        }

        // Map bounds
        if (( curPos.x < 0 ) || ( curPos.y < 0 ))
        {
            //Debug.Log(curPos.x + " " + curPos.y + " " + movesLeft + " oob");
            return;
        }
        if ( curPos.x >= MAP_SIZE_X )
        {
            //Debug.Log(curPos.x + " " + curPos.y + " " + movesLeft + " oob");
            return;
        }
        if ( curPos.y >= MAP_SIZE_Y )
        {
            //Debug.Log(curPos.x + " " + curPos.y + " " + movesLeft + " oob");
            return;
        }

        // Already walked it?
        int existingCost;
        bool shouldAdd = true;
        if (validList.TryGetValue(curPos, out existingCost))
        {
            shouldAdd = false;
            if ( movesLeft <= existingCost )
            {
                return;
            }
        }
        
        if (isMoving)
        {
            // Collision test
            GridObject collisionObj;
            if (TestCollision(curPos, out collisionObj, true))
            {
                if (collisionObj != gridObj)
                {
                    //Debug.Log(curPos.x + " " + curPos.y + " " + movesLeft + " collide");
                    return;
                }
            }
        }

        // Add and recurse
        if (shouldAdd)
        {
            validList.Add(curPos, movesLeft);
        }

        int newMovesLeft;
        if (isMoving)
        {
            newMovesLeft = movesLeft - GetTerrainCost(curPos);
        }
        else
        {
            newMovesLeft = movesLeft - 1;
        }

        GetValidMovesRecurse(gridObj, new GridCoord(curPos.x + 1, curPos.y), validList, newMovesLeft, isMoving);
        GetValidMovesRecurse(gridObj, new GridCoord(curPos.x, curPos.y + 1), validList, newMovesLeft, isMoving);
        GetValidMovesRecurse(gridObj, new GridCoord(curPos.x - 1, curPos.y), validList, newMovesLeft, isMoving);
        GetValidMovesRecurse(gridObj, new GridCoord(curPos.x, curPos.y - 1), validList, newMovesLeft, isMoving);
    }

    void RemoveFromList( Dictionary<GridCoord, int> list, GridCoord pos )
    {
        list.Remove(pos);
    }

    public Dictionary<GridCoord, int> GetActionRange( GridCoord startPos, bool allowMelee, int maxRange)
    {
        Dictionary<GridCoord, int> validList = new Dictionary<GridCoord, int>();

        GetValidMovesRecurse(null, startPos, validList, maxRange + 1, false);

        if ( allowMelee == false )
        {
            RemoveFromList(validList, new GridCoord(startPos.x + 1, startPos.y));
            RemoveFromList(validList, new GridCoord(startPos.x - 1, startPos.y));
            RemoveFromList(validList, new GridCoord(startPos.x, startPos.y - 1));
            RemoveFromList(validList, new GridCoord(startPos.x, startPos.y + 1));
        }

        return validList;
    }

}
