using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace Lua.Internal;

[StructLayout(LayoutKind.Auto)]
public struct FastStackCore<T>
{
    const int InitialCapacity = 8;

    T?[] array;
    int tail;

    public int Count => tail;

    public readonly ReadOnlySpan<T> AsSpan()
    {
        if (array == null) return [];
        return array.AsSpan(0, tail);
    }

    public readonly Span<T?> GetBuffer()
    {
        if (array == null) return [];
        return array.AsSpan();
    }

    public readonly T? this[int index]
    {
        get
        {
            return array[index];
        }
    }

    public void Push(in T item)
    {
        array ??= new T[InitialCapacity];

        if (tail == array.Length)
        {
            var newArray = new T[tail * 2];
            Array.Copy(array, newArray, tail);
            array = newArray;
        }

        array[tail] = item;
        tail++;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool TryPop(out T value)
    {
        if (tail == 0)
        {
            value = default!;
            return false;
        }

        tail--;
        value = array[tail]!;
        array[tail] = default;

        return true;
    }

    public T Pop()
    {
        if (!TryPop(out var result)) throw new InvalidOperationException("Empty stack");
        return result;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool TryPeek(out T value)
    {
        if (tail == 0)
        {
            value = default!;
            return false;
        }

        value = array[tail - 1]!;
        return true;
    }

    public T Peek()
    {
        if (!TryPeek(out var result)) throw new InvalidOperationException();
        return result;
    }


    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void EnsureCapacity(int capacity)
    {
        if (array == null)
        {
            array = new T[InitialCapacity];
        }

        var newSize = array.Length;
        while (newSize < capacity)
        {
            newSize *= 2;
        }

        Array.Resize(ref array, newSize);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void NotifyTop(int top)
    {
        if (tail < top) tail = top;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void Clear()
    {
        array.AsSpan(0, tail).Clear();
        tail = 0;
    }
}
