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

/// <summary>
/// can be inherited by tiles in order to be added to the level manager
/// </summary>
public abstract class LevelTile : TileBase, ILevelObject
{
    public const string project_name = "Gather And Defend";
    [SerializeField]
    private Sprite _sprite;
    public Sprite Sprite => _sprite;
    public bool Instantiated { get; set; }

    [SerializeField]
    private bool _isCollidable;
    public bool IsCollidable => _isCollidable;

    [SerializeField]
    private Vector3 customOffset = Vector3.zero;

    public Vector3 Position { get; protected set; }

    private Tilemap _tilemap;
    public Tilemap Tilemap => _tilemap;
    public string TilemapName
    {
        get => _tilemap.name;
    }
    public string Name { get => name; protected set => name = value; }

    public virtual void LevelStart() { }
    public virtual void LevelDestroy() { }
    public virtual void LevelUpdate() { }
    public virtual bool Equals(ILevelObject other)
    {
        return other is LevelTile otherTile 
            && Name == otherTile.Name
            && Position == otherTile.Position
            && TilemapName == otherTile.TilemapName;
    }
    public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject go)
    {
        //only execute if application is in play mode
        if (!Application.isPlaying)
        {
            return base.StartUp(position, tilemap, go);
        }

        //if it was created from the level manager, then it is already instantiated, and we shouldn't do it again
        if (Instantiated)
        {
            return base.StartUp(position, tilemap, go);
        }

        var comp = tilemap.GetComponent<Tilemap>();

        //need to create an instance of the tile, otherwise the position will change for all tiles instead of only this one.
        var instance = Instantiate(this);
        instance.name = name;
        instance.Position = position;
        instance._tilemap = comp;

        //if tile already exists, dont add to level manager
        if (LevelManager.Instance.Has<LevelTile>(tile => tile.Equals(instance)))
        {
            return base.StartUp(position, tilemap, go);
        }

        LevelManager.Instance.Add(instance);
        return base.StartUp(position, tilemap, go);
    }
    public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    {
        tileData.sprite = _sprite;
        tileData.transform.SetTRS(customOffset, Quaternion.identity, Vector3.one);
        tileData.color = Color.white;
    }
    public override void RefreshTile(Vector3Int position, ITilemap tilemap)
    {
        base.RefreshTile(position, tilemap);
        if (!Application.isPlaying) return;

        //this will check if tile was removed from tilemap
        if (tilemap.GetTile(position) == null)
        {
            LevelManager.Instance.Remove(this);
        }
    }
    public virtual Dictionary<string, object> ToDictionary()
    {
        return new Dictionary<string, object>()
        {
            {nameof(Name), Name },
            {nameof(Position), new float[]{Position.x, Position.y, Position.z } },
            {nameof(TilemapName), TilemapName },
            {nameof(ILevelObject.ObjectType), nameof(ILevelObject.ObjectType.Tile) },
            {nameof(_isCollidable), _isCollidable }
        };
    }

    public virtual void LoadDictionary(Dictionary<string, object> dict)
    {
        Name = dict[nameof(Name)].ToString();
        Position = dict[nameof(Position)].ToVector3();
        var tilemapName = dict[nameof(TilemapName)].ToString();
        _isCollidable = bool.Parse(dict[nameof(_isCollidable)].ToString());
        _tilemap = FindObjectOfType<Grid>().GetComponentInChildren<Tilemap>(tilemapName);
    }

    public void AddToLevel()
    {
        if (!_tilemap) return;
        _tilemap.SetTile(Vector3Int.RoundToInt(Position), this);
    }

    public void RemoveFromLevel()
    {
        if (!_tilemap) return;
        _tilemap.SetTile(Vector3Int.RoundToInt(Position), null);
    }
}