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

public class LevelManager : MonoBehaviour {

    public static LevelManager instance;

	public MapGrid grid;

    public PawnScript pawnPrefab;

    [SerializeField] private TextAsset textAssetLevel;
    [SerializeField] private TextAsset textAssetEntryPoints;

    private List<SpawnEntry> spawnEntryPoints;
    private const string EntryPointString = "Entry";
    private const string LogTrap = "LogTrap";
    private const string SpikeTrap = "SpikeTrap";
    private const string WallTurnLeft = "WallTurnLeft";
    private const string WallTurnRight = "WallTurnRight";
    private const string EmptyTile = "Empty";
    private const string ExitTile = "Exit";
    private const string WallTile = "Wall";
    private const string WaterWallRight = "WaterWallRight";
    private const string WaterWallLeft = "WaterWallLeft";
    private const string FireWallRight = "FireWallRight";
    private const string FireWallLeft = "FireWallLeft";
    private const string WaterTile = "Water";
    private const string RoadTile = "Road";

    private const string textAssetLevelBase = "LevelsConfig/Level{0}";
    private const string textAssetEntryPointsBase = "LevelsConfig/Entries{0}";

    private int totalPilgrims = 0;
    private int goodPilgrims = 0;
    private int badPilgrims = 0;
    private int savePilgrims = 0;
    private int killedGoodPilgrims = 0;
    private int SpawnedPilgrims = 0;

    public int goalSafePilgrims= 0;

    public void InitializeLevel()
    {
        if (instance != null) { 
            Destroy(instance.gameObject); 
        } else { 
            instance = this; 
        }

        MusicManager.instance.StopMusic();

        int levelNum = GameManager.Instance.level;

        spawnEntryPoints = new List<SpawnEntry>();

        string textAssetLevelName = string.Format(textAssetLevelBase, levelNum);
        string textAssetEntryPointName = string.Format(textAssetEntryPointsBase, levelNum);

        //parse grid info
        string[,] level = CsvParser.Parse((Resources.Load<TextAsset>(textAssetLevelName)).text);

        //parse entrypoints info. [0] = i; [1] = j; [2] = direction (1= up; 2 = east; 3= south; 4 = west); [3] = time to wait until spawning; [4] = no. of pilgrims to spawn; [5] interval between spwans
        string[,] entryPoints = CsvParser.Parse(((TextAsset)Resources.Load(textAssetEntryPointName)).text);
        
        int width = level.GetLength(0);
        int length = level.GetLength(1);

        int dontcare = 0;

        Tile[,] tiles = new Tile[width, length];

        for (int k = 0; k < entryPoints.GetLength(1); k++)
        {
            int i = int.Parse(entryPoints[0, k]);
            int j = int.Parse(entryPoints[1, k]);

            int thisTotalPilgrims = int.Parse(entryPoints[4, k]);
            bool isGood = entryPoints[6, k] == "1";
            spawnEntryPoints.Add(new SpawnEntry(int.Parse(entryPoints[2, k]), new Vector2(i, j), float.Parse(entryPoints[3, k]), thisTotalPilgrims, float.Parse(entryPoints[5, k]), isGood));
            totalPilgrims += thisTotalPilgrims;
            goodPilgrims = goodPilgrims + (isGood ? thisTotalPilgrims : 0);
            badPilgrims = badPilgrims + (!isGood ? thisTotalPilgrims : 0);
        }

        for (int i=0; i<width; i++)
        {
            for (int j = 0; j < length; j++)
            {
                TileType type = TileType.EmptyTileType;

                //Goal (e.g. 20) is set on file as "Exit20"
                if(level[i, j].StartsWith(ExitTile))
                {
                    //if goal not set
                    if (goalSafePilgrims == 0)
                    {
                        //try parse the goal
                        if (int.TryParse(level[i, j].Substring(4), out goalSafePilgrims))
                        {
                            Debug.Log("Goal: " + goalSafePilgrims);
                            level[i, j] = ExitTile;
                        }
                        else
                        {
                            //calculate dafult value?
                            goalSafePilgrims = totalPilgrims;
                        }
                    }
                    //if goal already set, and there's another exit with number, then ignore the latter
                    else if (int.TryParse(level[i, j].Substring(4), out dontcare))
                    {
                        level[i, j] = ExitTile;
                    }
                }

                switch (level[i, j]) {
                    case EmptyTile:
                    case "":
                        type = TileType.EmptyTileType;
                        break;
                    case EntryPointString:
                    case RoadTile:
                        type = TileType.RoadTile;
                        break;
                    case WallTile:
                        type = TileType.WallTile;
                        break;
                    case ExitTile:
                        type = TileType.ObjectiveTile;
                        break;
                    case SpikeTrap:
                        type = TileType.WaterTrap;
                        break;
                    case LogTrap:
                        type = TileType.FireTrap;
                        break;
                    case WallTurnLeft:
                        type = TileType.WallTurnLeft;
                        break;
                    case WallTurnRight:
                        type = TileType.WallTurnRight;
                        break;
                    case WaterWallRight:
                        type = TileType.WaterWallRight;
                        break;
                    case WaterWallLeft:
                        type = TileType.WaterWallLeft;
                        break;
                    //case FireWallRight:
                    //    type = TileType.FireWallRight;
                    //    break;
                    //case FireWallLeft:
                    //    type = TileType.FireWallLeft;
                    //    break;
                    case WaterTile:
                        type = TileType.WaterTile;
                        break;                        
                    default:
                        Debug.LogError("Unknown tile: " + level[i, j]);
                        type = TileType.EmptyTileType;
                        break;
                }

                int y = length - j - 1;
                tiles[i, y] = new Tile(type, new Vector2(i, y), !level[i, j].StartsWith("-"));
            }
        }

        MapGrid mp = new MapGrid(tiles);

        //instantiate grid
        for(int i = 0; i < width; i ++)
        {
            for (int j = 0; j < length; j++)
            {
                //Debug.LogFormat("Tiles [{0},{1}]: {2}", i, j, tiles[i, j]);
                Vector3 tilePos = new Vector3(tiles[i, j].Position.x, 0, tiles[i, j].Position.y);
                TileScript tileScript = Instantiate(Resources.Load<TileScript>("Prefabs/" + tiles[i, j].Type.Prefab), tilePos, Quaternion.identity) as TileScript;
                tileScript.transform.parent = this.transform;
                tiles[i, j].script = tileScript;
            }
        }
        GameManager.Instance.map = mp;

        foreach (SpawnEntry se in spawnEntryPoints) {
            StartCoroutine("StartSpawnPawns", se);
        }

        GameManager.Instance.UpdateAliveCounter(goodPilgrims);
    }

    public void PawnKilled(PawnScript pawn)
    {
        //check for goodbad pawn
        if (pawn.isGood) {
            killedGoodPilgrims++;
        }

        CheckPilgrimsStatus();
    }

    public void PawnArrivedObjective()
    {
        //check for goodbad pawn
        savePilgrims++;
        GameManager.Instance.UpdateSafeCounter(savePilgrims);
        CheckPilgrimsStatus();
    }

    public void BadPawnEnteredObjective()
    { GameManager.Instance.LevelFailed(); }

    private IEnumerator StartSpawnPawns(SpawnEntry se)
    {
        yield return new WaitForSeconds(se.time);
        StartCoroutine("SpawnPawns", se);
    }

    private IEnumerator SpawnPawns(SpawnEntry se)
    {
        for (int k = 0; k < se.amount; k++)
        {
            yield return new WaitForSeconds(se.interval);
            PawnScript pawn = Instantiate(pawnPrefab, new Vector3(se.Position.x, 0, se.Position.y), Quaternion.identity) as PawnScript;
            pawn.transform.parent = this.transform;
            pawn.Direction = se.Direction;
            pawn.isGood = se.goodPawn;

            pawn.DarkenShader(!se.goodPawn);

            SpawnedPilgrims++;
            CheckPilgrimsStatus();
        }
    }

    private void CheckPilgrimsStatus()
    {
        int currentAlive = goodPilgrims - killedGoodPilgrims - savePilgrims;
        GameManager.Instance.UpdateAliveCounter(currentAlive);

        if (savePilgrims >= goalSafePilgrims)
        {
            //Load next Level
            Debug.Log("Level Finished. YAY");
            GameManager.Instance.LevelSuccesseded();
        } else if (currentAlive + savePilgrims < goalSafePilgrims) {
            //LEVEL OVER
            Debug.Log("You god damn useless shmop");
            GameManager.Instance.LevelFailed();
        }
    }

    private bool ArrayContainsValues(string[,] targetSearchArray, int value1, int value2)//, out string[,] indexResult)
    {
        for (int i = 0; i < targetSearchArray.GetLength(0); i++)
        {
            for (int j = 0; j < targetSearchArray.GetLength(1); j++)
            {
                if(i == value1 && j == value2)
                {
                    return true;
                }
            }
        }

        return false;
    }
}
