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

public abstract class DraggablePlaceholder : MonoBehaviour
{
    protected const string OutlineColor = "_OutlineColor";

    [SerializeField]
    private Color _validColor = Color.green;
    [SerializeField]
    private Color _invalidColor = Color.red;

    private Camera _mainCamCache;
    private Rect _lvlBoundsCache;
    private bool _canBePlacedHere;
    private List<SpriteRenderer> _outlineRenderers = new List<SpriteRenderer>();

    public Color ValidColor => _validColor;
    public Color InvalidColor => _invalidColor;
    public Camera MainCamCache => _mainCamCache;
    public Rect LevelBoundCache => _lvlBoundsCache;
    public bool PlaceableHere => _canBePlacedHere;

    public List<SpriteRenderer> OutlineRenderers
    {
        get => _outlineRenderers;
    }

    private List<SpriteRenderer> _allRenderers = new List<SpriteRenderer>();
    
    /// <summary>
    /// calculate level boundaries and finds main camera.
    /// </summary>
    protected virtual void Start()
    {
        _mainCamCache = Camera.main;
        _lvlBoundsCache = LevelManager.Instance.CurrentLevel.CalculateBounds();
        _lvlBoundsCache.xMax += 1;
        _lvlBoundsCache.yMax += 1;

        _allRenderers = GetComponentsInChildren<SpriteRenderer>().ToList();
        UpdatePosition();
    }

    /// <summary>
    /// check for mouse click being released (and tries to place object)<br/>
    /// also updates placeholder's position and shows if position is valid
    /// </summary>
    protected virtual void Update()
    {
        if (!Input.GetMouseButton(0))
        {
            if (_canBePlacedHere)
            {
                Place();
            }
            Destroy(gameObject);
        }

        _canBePlacedHere = CanBePlacedHere();
        UpdatePosition();

        ShowValidity();
    }

    /// <summary>
    /// sets the position of the placeholder on the grid so it follows the mouse
    /// </summary>
    protected virtual void UpdatePosition()
    {
        var mousePos = Vector3Int.RoundToInt(_mainCamCache.ScreenToWorldPoint(Input.mousePosition));
        mousePos.z = 0;

        if (!_lvlBoundsCache.Contains(mousePos))
        {
            _allRenderers.ForEach(x => x.enabled = false);
        }
        else
        {
            _allRenderers.ForEach(x => x.enabled = true);
        }
        transform.position = mousePos;

    }

    /// <summary>
    /// helps determine if a unit can be placed on the tile sitting at unit's position (out of bound? obstacle? invalid tile?<br/>
    /// default behaviour is : you cant place anything over any already existing thing<br/>
    /// override to change this behaviour
    /// </summary>
    public virtual bool CanBePlacedHere()
    {
        return _lvlBoundsCache.Contains(transform.position);
    }

    /// <summary>
    /// how your character will appear depending on the validity of the tile you want to put them on<br/>
    /// default behaviour is changes color of all sprite renderers of the Outline Transform to red if invalid, green otherwise
    /// </summary>
    /// <param name="isValidPosition"></param>
    /// <returns></returns>
    public virtual void ShowValidity()
    {
        Color getColor() => _canBePlacedHere ? _validColor : _invalidColor;

        foreach (var child in _outlineRenderers)
        {
            child.material.SetColor(OutlineColor, getColor());
        }
    }
    public abstract void Place();
}
