using System;
using Unity.Properties;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;

namespace KitsuneCafe.UI
{
  public enum FlowDirection
  {
    Vertical = 0,
    Horizontal = 1
  }


  public abstract record CollectionItemSize;

  [Serializable]
  public record FixedItemSize(float Size) : CollectionItemSize;

  [Serializable]
  public record DynamicItemSize() : CollectionItemSize
  {
    public static readonly DynamicItemSize Instance = new();
  }

  public class CollectionItemSizeConverter : UxmlAttributeConverter<CollectionItemSize>
  {
    public override CollectionItemSize FromString(string value)
    {
      return value switch
      {
        "()" => new DynamicItemSize(),
        var v => new FixedItemSize(int.Parse(v))
      };
    }

    public override string ToString(CollectionItemSize value)
    {
      return value switch
      {
        DynamicItemSize => "()",
        var v => v.ToString(),
      };
    }
  }

  [UxmlElement]
  public partial class RecycleView : VisualElement, IDisposable
  {
    public const string RecycleViewBaseClass = "kitsunecafe__recycle-view";
    public const string RecycleViewScrollViewClass = "kitsunecafe__recycle-view--scroll-view";
    public const string RecycleViewContentContainerClass = "kitsunecafe__recycle-view--content-container";
    public const string RecycleViewDynamicItemClass = "kitsunecafe__recycle-view--dynamic-item";


    private FlowDirection direction;

    [UxmlAttribute, CreateProperty]
    public FlowDirection Direction
    {
      get => direction;
      set
      {
        if (direction != value)
        {
          direction = value;
          virtualizationController.Layout = CreateLayout();
          UpdateDirection();
        }

      }
    }

    private bool isDynamicSize = false;

    [UxmlAttribute, CreateProperty]
    public bool IsDynamicSize
    {
      get => isDynamicSize;
      set
      {
        if (isDynamicSize != value)
        {
          isDynamicSize = value;
          virtualizationController.Layout = CreateLayout();
        }

      }
    }

    private float itemSize = 22;

    [UxmlAttribute, CreateProperty, Delayed, Tooltip("In a dynamic layout, this is used for initial calculations")]
    public float ItemSize
    {
      get => itemSize;
      set
      {
        if (itemSize != value)
        {
          itemSize = value;
          if (virtualizationController.Layout is FixedLayout fixedLayout)
          {
            fixedLayout.ItemSize = value;
          }
          else if (virtualizationController.Layout is DynamicLayout dynamicLayout)
          {
            dynamicLayout.DefaultItemSize = value;
          }
        }
      }
    }

    private int bufferCount = 8;

    [UxmlAttribute, CreateProperty, Delayed]
    public int BufferCount
    {
      get => bufferCount;
      set
      {
        if (bufferCount != value)
        {
          bufferCount = value;
          if (virtualizationController.Layout is FixedLayout fixedLayout)
          {
            fixedLayout.Buffer = value;
          }
          else if (virtualizationController.Layout is DynamicLayout dynamicLayout)
          {
            dynamicLayout.Buffer = value;
          }
        }
      }
    }

    private float gutter = 0;

    [UxmlAttribute, CreateProperty, Delayed]
    public float Gutter
    {
      get => gutter;
      set
      {
        if (gutter != value)
        {
          gutter = value;
          if (virtualizationController.Layout is FixedLayout fixedLayout)
          {
            fixedLayout.GutterSize = value;
          }
          else if (virtualizationController.Layout is DynamicLayout dynamicLayout)
          {
            dynamicLayout.GutterSize = value;
          }
        }
      }
    }

    private bool wrap = false;

    [UxmlAttribute, CreateProperty]
    public bool Wrap
    {
      get => wrap;
      set
      {
        if (wrap != value)
        {
          wrap = value;
          if (virtualizationController.Layout is FixedLayout fixedLayout)
          {
            fixedLayout.Wrap = wrap;
          }
          else if (virtualizationController.Layout is DynamicLayout dynamicLayout)
          {
            dynamicLayout.Wrap = wrap;
          }
        }
      }
    }


    [CreateProperty]
    public ICollectionDataSource DataSource
    {
      get => virtualizationController?.DataSource;
      set
      {
        if (virtualizationController != null)
        {
          virtualizationController.DataSource = value;
          virtualizationController.Setup();
        }
      }
    }

    private readonly ScrollView scrollView;
    public override VisualElement contentContainer => scrollView.contentContainer;

    private readonly IRecycleVirtualizationContainer virtualizationController;

    public RecycleView()
    {
      AddToClassList(RecycleViewBaseClass);

      scrollView = new ScrollView();
      scrollView.AddToClassList(RecycleViewScrollViewClass);
      hierarchy.Add(scrollView);
      contentContainer.AddToClassList(RecycleViewContentContainerClass);

      virtualizationController = new RecycleVirtualizationController
      {
        Container = contentContainer
      };

      RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
    }

    private ILayout CreateLayout()
    {
      UpdateDirection();

      return isDynamicSize switch
      {
        true => new DynamicLayout(Direction, itemSize) { Buffer = bufferCount, GutterSize = gutter },
        false => new FixedLayout(Direction, itemSize) { Buffer = bufferCount, GutterSize = gutter }
      };
    }


    private void UpdateDirection()
    {
      switch (direction)
      {
        case FlowDirection.Vertical:
          scrollView.mode = ScrollViewMode.Vertical;
          scrollView.horizontalScroller.valueChanged -= virtualizationController.OnScrolled;
          scrollView.verticalScroller.valueChanged += virtualizationController.OnScrolled;
          Debug.Log("vertical");
          break;
        case FlowDirection.Horizontal:
          scrollView.mode = ScrollViewMode.Horizontal;
          scrollView.verticalScroller.valueChanged -= virtualizationController.OnScrolled;
          scrollView.horizontalScroller.valueChanged += virtualizationController.OnScrolled;
          Debug.Log("horizontal");
          break;
      }
    }

    public void OnGeometryChanged(GeometryChangedEvent evt)
    {
      if (evt.newRect.size != evt.oldRect.size && virtualizationController != null)
      {
        virtualizationController.OnParentSizeChanged(evt.newRect.size);
      }
    }

    public void Dispose()
    {
      if (virtualizationController is IDisposable disposable)
      {
        disposable.Dispose();
      }
    }

    ~RecycleView()
    {
      Dispose();
    }
  }
}
