import t, c
from stdint import *
from w32.win32process import TlsAlloc, TlsGetValue, TlsSetValue
import stdlib
import stdio
import string


# Maximum provider stack depth (compiler verifies sufficiency; increase if needed)
_MAX_PROVIDER_DEPTH: t.CDefine = 32


@t.Object
class _ProviderStack:
    """Provider stack, stores pairs of field names and pointers."""
    names: t.CArray[VOIDPTR, 32]
    ptrs:  t.CArray[VOIDPTR, 32]
    top:   UINT64


# TLS index (global; all threads share the same index)
_tls_index: ULONG = 0


@t.TLS
def _init_tls():
    """Initialize TLS index (executes once; subsequent calls are skipped)."""
    global _tls_index
    _tls_index = TlsAlloc()


def _get_stack() -> _ProviderStack | t.CPtr:
    """Obtain current thread's provider stack. Allocate one if none exists."""
    _init_tls()
    stack: _ProviderStack | t.CPtr = TlsGetValue(_tls_index)
    if stack == None:
        stack = stdlib.malloc(_ProviderStack.__sizeof__())
        string.memset(stack, 0, _ProviderStack.__sizeof__())
        TlsSetValue(_tls_index, stack)
    return stack


def _push_provider(field_name: str, ptr: t.CPtr):
    """Push a provider (field_name, ptr) onto the stack."""
    stack: _ProviderStack | t.CPtr = _get_stack()
    if stack.top >= _MAX_PROVIDER_DEPTH:
        stdio.printf("[with] ERROR: provider stack overflow (depth=%lu, max=%d), field '%s' not injected\n", stack.top, _MAX_PROVIDER_DEPTH, field_name)
        return
    stack.names[stack.top] = field_name
    stack.ptrs[stack.top] = ptr
    stack.top = stack.top + 1


def _pop_provider():
    """Pop the top provider entry from the stack."""
    stack: _ProviderStack | t.CPtr = _get_stack()
    if stack.top > 0:
        stack.top = stack.top - 1


def _find_provider(field_name: str) -> t.CPtr:
    """Search for matching field_name starting from stack top.
    Return associated pointer, or NULL if no match found."""
    stack: _ProviderStack | t.CPtr = _get_stack()
    for i in range(stack.top - 1, -1, -1):
        if stack.names[i] == field_name:
            return stack.ptrs[i]
    return None