using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

namespace KitsuneCafe.UI
{

  public class DynamicLayout : ILayout
  {
    public float DefaultItemSize { get; set; }
    public float GutterSize { get; set; } = 0;
    public FlowDirection Direction { get; set; } = FlowDirection.Vertical;
    public int Buffer { get; set; } = 0;
    public bool Wrap { get; set; } = false;
    public int Count { get; set; }
    public float ContentSize { get; private set; }

    private readonly List<float> positions = new();
    private readonly List<float> sizes = new();

    public DynamicLayout(FlowDirection direction, float defaultItemSize = 22, float gutterSize = 0)
    {
      Direction = direction;
      GutterSize = gutterSize;
      DefaultItemSize = defaultItemSize;
    }

    public float GetItemPosition(int index)
    {
      if (index > 0)
      {
        return GetItemPosition(index - 1) + GetItemSize(index - 1) + GutterSize;
      }

      return 0;
    }

    public float GetItemSize(int index)
    {
      if (index >= 0 && index < sizes.Count)
      {
        return sizes[index];
      }

      return DefaultItemSize;
    }

    private float LastPositionOrDefault()
    {
      return positions.Count > 0 ? positions[^1] : DefaultItemSize + GutterSize;
    }

    public void Update(int itemCount, VisualElement container)
    {
      while (positions.Count < itemCount)
      {
        var last = LastPositionOrDefault();
        positions.Add(last + DefaultItemSize + GutterSize);
        sizes.Add(DefaultItemSize);
      }

      if (positions.Count > itemCount)
      {
        positions.RemoveRange(itemCount, positions.Count - itemCount);
        sizes.RemoveRange(itemCount, sizes.Count - itemCount);
      }

      Count = itemCount;
      ContentSize = LastPositionOrDefault();
    }

    private float CalculatePositions(int startingIndex = 0, float initialPosition = 0)
    {
      var position = initialPosition;

      for (int i = startingIndex; i < Count; i++)
      {
        positions[i] = position;
        position += GetItemSize(i) + GutterSize;
      }

      return ContentSize = position;
    }

    public void SetMeasuredItemSize(int index, float size)
    {
      if (index < sizes.Count && Mathf.Approximately(sizes[index], size))
      {
        return;
      }

      sizes[index] = size;

      var position = index > 0 ? positions[index - 1] + sizes[index - 1] + GutterSize : 0;
      CalculatePositions(index, position);
    }

    public int GetFirstVisibleIndex(float offset)
    {
      if (offset <= 0) { return 0; }

      for (int i = 0; i < Count; i++)
      {
        if (positions[i] >= offset)
        {
          return i;
        }
      }

      return positions.Count - 1;
    }

    public Range GetVisibleRange(float offset, float size)
    {
      var halfBuffer = Mathf.CeilToInt(Buffer / 2);

      var first = Math.Max(
        GetFirstVisibleIndex(offset) - halfBuffer,
        0
      );

      var count = 1;
      var position = GetItemPosition(first);
      var width = position + size;

      for (int i = first; i < Count; i++)
      {
        var item = GetItemPosition(i);
        if (item > width) { break; }
        count += 1;
      }

      count += halfBuffer;
      var last = Math.Min(Count, first + count);

      Debug.Log($"{first}:{last}");
      return new Range(first, last);
    }
  }
}
