using UnityEngine;
using GatherAndDefend.LevelEditor;
using System.Collections;
using UnityEngine.Tilemaps;
using System.Threading.Tasks;
using System;
using GatherAndDefend.Events;
using static LevelManager;
/// <param name="position">The position the tile should be placed on</param>
/// <param name="tile">the tile we should place</param>
/// <param name="tilePlacement">the action of placing the tile on the tilemap</param>
/// <returns></returns>
public delegate Task PlacementAnimationHandler(GatherAndDefend.LevelEditor.TileData tileData, Action tilePlacement, Func<bool> shouldKillTask);
public class LevelManagerScript : SingletonBehaviour<LevelManagerScript>
{
    private Action updateAction = null;
    public bool loadOnStart = false;
    public Level firstLevel;

    [SerializeField] private SpriteRenderer _worldBackgroundSpriteRenderer;
    [SerializeField] private Sprite[] _worldBackgrounds;

    async void Start()
    {
        //only when the level is loaded do we start updating
        EventAggregator.Instance.GetEvent<LevelLoadedEvent>().Attach(Instance_LevelLoaded);
        EventAggregator.Instance.GetEvent<ExitingLevelEvent>().Attach(KillLoading);

        if (PlayerPrefs.GetInt(LoadingManager.LevelToLoad) >= 12)
        {
            _worldBackgroundSpriteRenderer.sprite = _worldBackgrounds[1];
        }

        if (loadOnStart && firstLevel)
        {
            await LevelManager.Instance.LoadLevel(firstLevel, PlacementAnimation);
        }

    }

    void OnDestroy()
    {
        EventAggregator.Instance.GetEvent<ExitingLevelEvent>().Detach(KillLoading);
        updateAction = null;
    }

    void Update()
    {
        updateAction?.Invoke();

        // Gala hack to win level instantly
        if (Input.GetKeyDown(KeyCode.Alpha0))
        {
            EventAggregator.Instance.GetEvent<LastWaveCompletedEvent>().Invoke();
        }
    }

    private void Instance_LevelLoaded(Level level)
    {
        //only when the level is loaded do we start updating
        updateAction = LevelManager.Instance.UpdateLevel;
        LevelManager.Instance.LevelTransform = transform;
    }

    /// <summary>
    /// the tile falls from the top of the screen, up to it's position on the tilemap
    /// </summary>
    /// <param name="position">where it should land</param>
    /// <param name="tile">what the tile represents</param>
    /// <param name="putTileOnTilemap">the action of actually placing the tile</param>
    /// <returns></returns>
    public static async Task PlacementAnimation(GatherAndDefend.LevelEditor.TileData tile, Action putTileOnTilemap, Func<bool> shouldKillTask)
    {
        const string layer = "Unit";
        float speed = 0;
        float acceleration = -9.81f;
        
        //create the falling GameObject that will placehold for the tile
        var tilePlaceholder = new GameObject("tile");
        tilePlaceholder.transform.SetParent(Instance.transform);
        var rend = tilePlaceholder.AddComponent<SpriteRenderer>();
        rend.sortingOrder = 10;
        rend.sortingLayerName = layer;

        if (tile.Tile is LevelTile)
        {
            rend.sprite = (tile.Tile as LevelTile).Sprite;
        }
        else
        {
            rend.sprite = (tile.Tile as Tile).sprite;
        }

        //position the tile over the board (past the camera's FOV)
        tilePlaceholder.transform.position = tile.Position + Vector3.up * Camera.main.orthographicSize * 2;

        var delta = tilePlaceholder.transform.position - tile.Position;
        var direction = delta.normalized;
        var signY = delta.y / Mathf.Abs(delta.y);
        
        while (true)
        {
            //if the placeholder was destroyed, stop
            if (!tilePlaceholder) break;

            //make the placeholder fall and check if we went past the target position
            speed += acceleration * Time.deltaTime;
            tilePlaceholder.transform.position += speed * Time.deltaTime * direction;
            var newDelta = tilePlaceholder.transform.position - tile.Position;
            var newSign = newDelta.y / Mathf.Abs(newDelta.y);

            if (newSign != signY) break;

            if (shouldKillTask())
            {
                return;
            }
            await Task.Yield();
        }

        //place the tile and destroy the placeholder
        putTileOnTilemap.Invoke();
        Destroy(tilePlaceholder);
    }
    void KillLoading()
    {
        LevelManager.Instance.KillLoading();
    }
}