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

namespace VOID.UserInterface.Game
{
    public class ContainerUI : MonoBehaviour, IHasWindow
    {
        public static List<ContainerUI> currentList { get; } = new();

        // WindowUI
        public WindowUI windowUI { get; private set; }

        // ContainerObject, its content to which this UI is connected and also unit who opened it
        public ContainerObject container { get; private set; }
        private ContainerObjectContent _content;
        private UnitObject _unit;

        // Visual Elements
        private VisualElement _rootElement;
        private VisualElement _slotsElement;
        private SlotUI[] _slots;

        // Sort buttons
        private Button _sortButton;

        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>();
            
            // Sort buttons
            _sortButton = template.Q<Button>("Sort");

            // Remove place holders
            _slotsElement.Clear();

            windowUI.OnClose += Close;

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

            // Sort buttons
            _sortButton.clicked += () => {
                var worldBound = _sortButton.worldBound;
                var positionToDisplayDropdown = new Vector2(worldBound.x, worldBound.y + worldBound.height);
                
                DropdownUI.current.Prepare(positionToDisplayDropdown);
                DropdownUI.current.AddOption("Name ↑", () => _content.Sort(item => item.basicData.finalName, SortDirectionType.Ascending, _unit));
                DropdownUI.current.AddOption("Name ↓", () => _content.Sort(item => item.basicData.finalName, SortDirectionType.Descending, _unit));
                DropdownUI.current.AddOption("Price ↑", () => _content.Sort(item => item.itemData.finalValue, SortDirectionType.Ascending, _unit));
                DropdownUI.current.AddOption("Price ↓", () => _content.Sort(item => item.itemData.finalValue, SortDirectionType.Descending, _unit));
                DropdownUI.current.AddOption("Weight ↑", () => _content.Sort(item => item.itemData.finalWeight, SortDirectionType.Ascending, _unit));
                DropdownUI.current.AddOption("Weight ↓", () => _content.Sort(item => item.itemData.finalWeight, SortDirectionType.Descending, _unit));
                DropdownUI.current.AddOption("Quality ↑", () => _content.Sort(item => item.itemData.quality.index, SortDirectionType.Ascending, _unit));
                DropdownUI.current.AddOption("Quality ↓", () => _content.Sort(item => item.itemData.quality.index, SortDirectionType.Descending, _unit));
                DropdownUI.current.Open();
            };
        }

        public void Open(ContainerObject container, UnitObject unit) {
            this.container = container;
            _content = container.containerContent;
            _unit = unit;

            _slots = new SlotUI[_content.items.Length];
            for (var i = 0; i < _content.items.Length; i++)
                SetSlot(i, _content.items[i]);

            container.containerEvents.onClose.AddListener(OnClose);
            container.containerEvents.onResize.AddListener(OnResize);
            container.containerEvents.onSwap.AddListener(OnSwap);
            container.containerEvents.onMoveIn.AddListener(OnMoveIn);
            container.containerEvents.onMoveOut.AddListener(OnMoveOut);
            container.containerEvents.onSort.AddListener(OnSort);

            windowUI.SetTitle(container.basicData.finalName);

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

        public void Close() {
            _unit.OrderInstantly(new CloseContainerAction(_unit, container));
        }

        private void OnClose(ContainerCloseEvent containerCloseEvent) {
            container.containerEvents.onClose.RemoveListener(OnClose);
            container.containerEvents.onResize.RemoveListener(OnResize);
            container.containerEvents.onSwap.RemoveListener(OnSwap);
            container.containerEvents.onMoveIn.RemoveListener(OnMoveIn);
            container.containerEvents.onMoveOut.RemoveListener(OnMoveOut);
            container.containerEvents.onSort.RemoveListener(OnSort);
            windowUI.OnClose -= Close;
            _rootElement.parent.parent.Remove(_rootElement.parent);
            currentList.Remove(this);
            Destroy(this);
        }

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

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

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

        private void OnSwap(ContainerSwapEvent containerSwapEvent) {
            SetSlot(containerSwapEvent.firstSlotIndex, containerSwapEvent.firstItem);
            SetSlot(containerSwapEvent.secondSlotIndex, containerSwapEvent.secondItem);
        }

        private void OnMoveIn(ContainerMoveInEvent containerMoveInEvent) {
            SetSlot(containerMoveInEvent.slotIndex, containerMoveInEvent.item);
        }

        private void OnMoveOut(ContainerMoveOutEvent containerMoveOutEvent) {
            SetSlot(containerMoveOutEvent.slotIndex, null);
        }

        private void OnSort(ContainerSortEvent containerSortEvent) {
            var containerItems = containerSortEvent.container.containerContent.items;
            for (var i = 0; i < containerItems.Length; i++)
                SetSlot(i, containerItems[i]);
        }

        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.ContainerSlot, slotIndex, "Slot-" + slotIndex);
            }

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

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