using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using VOID.Generic.Dict;
using VOID.Generic.Objects.Item;
using VOID.Generic.Objects.Unit;
using VOID.Generic.Objects.Unit.Actions;
using VOID.Generic.Objects.Unit.Events;
using VOID.UserInterface.Game.Interfaces;
using VOID.UserInterface.Game.Util;

namespace VOID.UserInterface.Game
{
    /// <summary>
    /// Manages all visuals in one instance of <see cref="OtherUnitBackpackUI"/>.
    /// </summary>
    public class OtherUnitBackpackUI : MonoBehaviour, IHasWindow
    {
        public static readonly List<OtherUnitBackpackUI> CurrentList = new();
        
        // WindowUI
        public WindowUI windowUI { get; private set; }
        
        // ContainerObject, its content to which this UI is connected and also unit who opened it
        public UnitObject unit { get; private set; }
        private UnitObjectBackpack _unitBackpack;
        private UnitObject _openedBy;
        
        // Visual Elements
        private VisualElement _rootElement;
        private VisualElement _slotsElement;
        private SlotUI[] _slots;
        
        private void Awake()
        {
            CurrentList.Add(this);

            var uiDocument = gameObject.GetComponent<UIDocument>();
            var template = uiDocument.rootVisualElement;
            
            // Find all important elements
            _rootElement = template.Q("root-container"); 
            _slotsElement = template.Q("slots");
            windowUI = gameObject.GetComponent<WindowUI>();
            
            // Remove place holders
            _slotsElement.Clear();

            windowUI.OnClose += Close;

            _rootElement.dataSource = this;
            _rootElement.pickingMode = PickingMode.Position;
        }

        /// <summary>
        /// Opens newly initialized <see cref="OtherUnitBackpackUI"/> <see cref="VisualElement"/>.
        /// This should be called from <see cref="OtherUnitBackpackManagerUI"/>.
        /// </summary>
        public void Open(UnitObject unit, UnitObject openedBy)
        {
            this.unit = unit;
            _unitBackpack = unit.unitBackpack;
            _openedBy = openedBy;
            
            _slots = new SlotUI[_unitBackpack.items.Length];
            for (var i = 0; i < _unitBackpack.items.Length; i++)
                SetSlot(i, _unitBackpack.items[i]);

            unit.unitEvents.onClosedByOtherUnit.AddListener(OnClose);
            unit.unitEvents.onBackpackResize.AddListener(OnResize);
            unit.unitEvents.onBackpackSwap.AddListener(OnSwap);
            unit.unitEvents.onBackpackPick.AddListener(OnMoveIn);
            unit.unitEvents.onBackpackDrop.AddListener(OnMoveOut);
            
            windowUI.SetTitle(unit.basicData.finalName);

            // Make sure it is visible
            _rootElement.style.display = DisplayStyle.Flex;
        }

        /// <summary>
        /// Orders player's active <see cref="UnitObject"/> to <see cref="CloseOtherUnitAction"/>.
        /// This action will close <see cref="WindowUI"/> which execute <see cref="OnClose"/>.
        /// </summary>
        public void Close()
        {
            new CloseOtherUnitAction(_openedBy, unit).DoIt();
        }

        private void OnClose(CloseOtherUnitEvent closeOtherUnitEvent)
        {
            unit.unitEvents.onClosedByOtherUnit.RemoveListener(OnClose);
            unit.unitEvents.onBackpackResize.RemoveListener(OnResize);
            unit.unitEvents.onBackpackSwap.RemoveListener(OnSwap);
            unit.unitEvents.onBackpackPick.RemoveListener(OnMoveIn);
            unit.unitEvents.onBackpackDrop.RemoveListener(OnMoveOut);
            windowUI.OnClose -= Close;
            _rootElement.parent.parent.Remove(_rootElement.parent);
            CurrentList.Remove(this);
            Destroy(this);
        }

        private void OnResize(BackpackResizeEvent backpackResizeEvent)
        {
            // Before resizing - remove all unused slots 
            if (backpackResizeEvent.beforeSlotCount > backpackResizeEvent.afterSlotCount)
                for (var i = backpackResizeEvent.afterSlotCount; i < backpackResizeEvent.beforeSlotCount; i++)
                    RemoveSlot(i);

            Array.Resize(ref _slots, backpackResizeEvent.afterSlotCount);

            // After resizing - insert empty slots
            if (backpackResizeEvent.beforeSlotCount < backpackResizeEvent.afterSlotCount)
                for (var i = backpackResizeEvent.beforeSlotCount; i < backpackResizeEvent.afterSlotCount; i++)
                    SetSlot(i, null);
        }

        private void OnSwap(BackpackSwapEvent backpackSwapEvent)
        {
            SetSlot(backpackSwapEvent.firstSlotIndex, backpackSwapEvent.firstItem);
            SetSlot(backpackSwapEvent.secondSlotIndex, backpackSwapEvent.secondItem);
        }

        private void OnMoveIn(BackpackPickEvent backpackPickEvent)
        {
            SetSlot(backpackPickEvent.slotIndex, backpackPickEvent.item);
        }

        private void OnMoveOut(BackpackDropEvent backpackDropEvent)
        {
            SetSlot(backpackDropEvent.slotIndex, null);
        }
        
        private void SetSlot(int slotIndex, ItemObject item)
        {
            // Create new VisualElement if not exists
            if (_slots[slotIndex] is null)
            {
                _slots[slotIndex] = new SlotUI();
                _slots[slotIndex].InitNew(_slotsElement, SlotType.OtherUnitBackpackSlot, slotIndex, "Slot-" + slotIndex);
            }

            // Put item's icon or else remain default
            _slots[slotIndex].Set(item);  
        }

        private void RemoveSlot(int slotIndex)
        {
            _slots[slotIndex].Destroy();
        }
    }
}
