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

public class MovementController : MonoBehaviour
{
    public Transform signUIPrefab;

    private MapData mapData;
    private DisplayCar displayCar;

    private const string DEFAULT_INPUT = "Up";
    private string currentInput = DEFAULT_INPUT;
    private Road2 currentRoad;
    private RoadPoint currentRoadPoint;

    Dictionary<int, string> indexesDirection = new Dictionary<int, string>();
    Dictionary<string, int> directionIndexes = new Dictionary<string, int>(){
        {"Up", 0},
        {"Right", 1},
        {"Down", 2},
        {"Left", 3}
    };
    private int currentDirectionIndex;

    public delegate void MovementCompleteDelegate();
    public static event MovementCompleteDelegate movementCompleteDelegate = null;

    public void SetData(MapData mapData)
    {
        currentDirectionIndex = directionIndexes[DEFAULT_INPUT];

        foreach (var item in directionIndexes)
            indexesDirection.Add(item.Value, item.Key);
        
        this.mapData = mapData;

        InputScreen.swipeInputDelegate += SwipeInputListener;
        CarScript.signCollisionDelegate += SignCollisionListener;

        currentRoadPoint = RoadPointFromMonument(mapData.start);
        
        displayCar = GetComponent<DisplayCar>();
        displayCar.CreateCar(currentRoadPoint);

        ChooseNextRoad();
    }

    void OnDestroy ()
    {
        InputScreen.swipeInputDelegate -= SwipeInputListener;
        CarScript.signCollisionDelegate -= SignCollisionListener;
    }

    private void ChooseNextRoad()
    {
        DebugLog("CHOOSE NEXT ROAD");

        var validRoads = new List<Road2>();

        foreach (var road in mapData.roads)
            if (currentRoad == null || road != currentRoad)
                if (road.point1.IsEqual(currentRoadPoint) || road.point2.IsEqual(currentRoadPoint))
                    validRoads.Add(road);

        var validDirections = new Dictionary<string, Road2>();

        foreach (var road in validRoads)
        {
            DebugLog("VALID ROAD: "+road);
            if (road.point1.IsEqual(currentRoadPoint)){
                if(road.point2.x - currentRoadPoint.x > 0) validDirections.Add("Right", road);
                else if(road.point2.x - currentRoadPoint.x < 0) validDirections.Add("Left", road);
                else if(road.point2.y - currentRoadPoint.y > 0) validDirections.Add("Up", road);
                else if(road.point2.y - currentRoadPoint.y < 0) validDirections.Add("Down", road);
            }
            if (road.point2.IsEqual(currentRoadPoint)){
                if(road.point1.x - currentRoadPoint.x > 0) validDirections.Add("Right", road);
                else if(road.point1.x - currentRoadPoint.x < 0) validDirections.Add("Left", road);
                else if(road.point1.y - currentRoadPoint.y > 0) validDirections.Add("Up", road);
                else if(road.point1.y - currentRoadPoint.y < 0) validDirections.Add("Down", road);
            }
        }

        // Check for end of level
        var endRoadPoint = RoadPointFromMonument(mapData.end);
        if (endRoadPoint.IsEqual(currentRoadPoint) || endRoadPoint.IsEqual(currentRoadPoint))
        {
            movementCompleteDelegate();
            SignCollisionListener(mapData.end);
            return;
        }

        string newDirection = DirectionForCurrentRotation(DEFAULT_INPUT);
        if (validDirections.ContainsKey(currentInput)) //Road available for currentInput?
            newDirection = currentInput;
        else if (!validDirections.ContainsKey(newDirection)) //Road unavailable for DefaultInput?
            foreach (var direction in validDirections)
                newDirection = direction.Key;

        var startingPoint = currentRoadPoint;
        currentRoad = validDirections[newDirection];
        var nextRoadPoint = currentRoad.point1.IsEqual(currentRoadPoint) ? currentRoad.point2 : currentRoad.point1;
        DebugLog("New Road: "+currentRoad+", currentRoadPoint: "+currentRoadPoint+", input: "+newDirection);

        StartCoroutine(MoveCar(startingPoint, nextRoadPoint, Constants.TRAVERSE_TILE_DURATION));
        displayCar.SetDirection(newDirection);
        currentDirectionIndex = directionIndexes[newDirection];
        displayCar.SetInputFeedback(DEFAULT_INPUT);
        currentInput = DirectionForCurrentRotation(DEFAULT_INPUT);
    }

    private RoadPoint RoadPointFromMonument(Monument monument)
    {
        var yLength = mapData.tiles.GetLength(1);
        var x = monument.tileIndex[0] + monument.positionOnTile.x - 1f;
        var y = yLength - monument.tileIndex[1] - 1 + monument.positionOnTile.y - 1f;
        return new RoadPoint(x, y);
    }

	public IEnumerator MoveCar(RoadPoint start, RoadPoint end, float lerpTime = 0.5f)
	{
		float startTime = Time.time;
		float timeSinceStart = 0f;
		float percentage = 0f;

		while (true)
		{
			timeSinceStart = Time.time - startTime;
			percentage = timeSinceStart / lerpTime;

			float currentX = Mathf.Lerp(start.x, end.x, percentage);
			float currentY = Mathf.Lerp(start.y, end.y, percentage);
            displayCar.SetPosition(new RoadPoint(currentX, currentY));

			if (percentage >= 1) break;

			yield return new WaitForEndOfFrame();
		}
            
        currentRoadPoint = end;
        ChooseNextRoad();
	}

    private string DirectionForCurrentRotation(string directionName)
    {
        //Change direction according to current rotation
        int index = directionIndexes[directionName];
        int convertedIndex = (currentDirectionIndex + index) % 4;
        return indexesDirection[convertedIndex];
    }

    private void SwipeInputListener(string directionName)
    {
        displayCar.SetInputFeedback(directionName);
        currentInput = DirectionForCurrentRotation(directionName);
    }

    private void SignCollisionListener(Monument sign)
    {
        if (currentRoad.IsEqual(sign.road))
        {
            if (sign.image == "start") //TODO Refactor
                return;
            var canvasUI = GameObject.Find("CanvasUI/Glass").transform;
            Transform obj = Instantiate(signUIPrefab, canvasUI.position, Quaternion.identity, canvasUI);
            obj.GetComponent<DisplaySign>().Initiate(sign);
            obj.name = "Sign "+sign.image;
        }
    }

    private void DebugLog(string str)
    {
        if (Constants.DEBUG_MODE)
            Debug.Log(str);
    }
}