using System;
using System.Collections.Generic;
using MultiWheelC.TrajectoryPlanning.Utils;

namespace MultiWheelC.TrajectoryPlanning.CoarsePath.Search;

/// <summary>
/// 为 Hybrid A* Open List 提供确定性优先级的二叉最小堆。
/// 排序严格依次比较 F、H、较大的 G 和插入序号；F、H、G 必须为有限且非负的等效米代价。
/// </summary>
/// <typeparam name="T">与一组搜索代价关联的节点或条目类型。</typeparam>
public sealed class BinaryMinHeap<T>
{
    private readonly List<HeapEntry> _entries = new List<HeapEntry>();
    private long _nextInsertionSequence;

    /// <summary>创建空的确定性 Open List 堆。</summary>
    public BinaryMinHeap()
    {
    }

    /// <summary>当前堆内尚未出队的条目数量。</summary>
    public int Count { get { return _entries.Count; } }

    /// <summary>
    /// 将一个条目和其搜索排序代价压入堆。
    /// 参数：item 为关联条目；f、h、g 均为有限且非负的等效米代价。
    /// 失败：任一代价无效、item 为 null（仅引用类型）或插入序号耗尽时抛出异常。
    /// </summary>
    public void Push(T item, double f, double h, double g)
    {
        if (ReferenceEquals(item, null)) throw new ArgumentNullException(nameof(item));
        ValidateCost(f, nameof(f));
        ValidateCost(h, nameof(h));
        ValidateCost(g, nameof(g));
        if (_nextInsertionSequence == long.MaxValue)
            throw new InvalidOperationException("The binary heap insertion sequence has been exhausted.");

        var entry = new HeapEntry(item, f, h, g, _nextInsertionSequence);
        _nextInsertionSequence++;
        _entries.Add(entry);
        SiftUp(_entries.Count - 1);
    }

    /// <summary>
    /// 弹出当前排序最优的条目。
    /// 返回：按 F、H、较大 G 和插入序号排序后的最小条目；空堆时抛出 <see cref="InvalidOperationException"/>。
    /// </summary>
    public T Pop()
    {
        if (_entries.Count == 0) throw new InvalidOperationException("The binary heap is empty.");

        HeapEntry result = _entries[0];
        int lastIndex = _entries.Count - 1;
        if (lastIndex == 0)
        {
            _entries.RemoveAt(0);
            return result.Item;
        }

        _entries[0] = _entries[lastIndex];
        _entries.RemoveAt(lastIndex);
        SiftDown(0);
        return result.Item;
    }

    /// <summary>清空尚未出队的条目；后续插入序号继续单调递增以保持整个实例内的确定性。</summary>
    public void Clear()
    {
        _entries.Clear();
    }

    private void SiftUp(int index)
    {
        while (index > 0)
        {
            int parentIndex = (index - 1) / 2;
            if (Compare(_entries[index], _entries[parentIndex]) >= 0) return;
            Swap(index, parentIndex);
            index = parentIndex;
        }
    }

    private void SiftDown(int index)
    {
        while (true)
        {
            int leftChildIndex = index * 2 + 1;
            if (leftChildIndex >= _entries.Count) return;

            int bestChildIndex = leftChildIndex;
            int rightChildIndex = leftChildIndex + 1;
            if (rightChildIndex < _entries.Count && Compare(_entries[rightChildIndex], _entries[leftChildIndex]) < 0)
                bestChildIndex = rightChildIndex;

            if (Compare(_entries[bestChildIndex], _entries[index]) >= 0) return;
            Swap(index, bestChildIndex);
            index = bestChildIndex;
        }
    }

    private void Swap(int firstIndex, int secondIndex)
    {
        HeapEntry temporary = _entries[firstIndex];
        _entries[firstIndex] = _entries[secondIndex];
        _entries[secondIndex] = temporary;
    }

    private static int Compare(HeapEntry left, HeapEntry right)
    {
        int comparison = left.F.CompareTo(right.F);
        if (comparison != 0) return comparison;

        comparison = left.H.CompareTo(right.H);
        if (comparison != 0) return comparison;

        comparison = right.G.CompareTo(left.G);
        if (comparison != 0) return comparison;

        return left.InsertionSequence.CompareTo(right.InsertionSequence);
    }

    private static void ValidateCost(double value, string parameterName)
    {
        if (!NumericGuard.IsFinite(value) || value < 0d)
            throw new ArgumentOutOfRangeException(parameterName, "Search costs must be finite and non-negative.");
    }

    private sealed class HeapEntry
    {
        public HeapEntry(T item, double f, double h, double g, long insertionSequence)
        {
            Item = item;
            F = f;
            H = h;
            G = g;
            InsertionSequence = insertionSequence;
        }

        public T Item { get; }
        public double F { get; }
        public double H { get; }
        public double G { get; }
        public long InsertionSequence { get; }
    }
}
