from libc.stdint cimport uint32_t

from cymem.cymem cimport Pool

from ..structs cimport TokenC


cdef struct State:
    TokenC* sent
    int* stack
    int i
    int sent_len
    int stack_len


cdef int add_dep(const State *s, const int head, const int child, const int label) except -1


cdef int pop_stack(State *s) except -1
cdef int push_stack(State *s) except -1


cdef bint has_head(const TokenC* t) nogil


cdef inline int get_idx(const State* s, const TokenC* t) nogil:
    return t - s.sent


cdef inline TokenC* get_n0(const State* s) nogil:
    return &s.sent[s.i]


cdef inline TokenC* get_n1(const State* s) nogil:
    if (s.i+1) >= s.sent_len:
        return NULL
    else:
        return &s.sent[s.i+1]


cdef inline TokenC* get_n2(const State* s) nogil:
    if (s.i + 2) >= s.sent_len:
        return NULL
    else:
        return &s.sent[s.i+2]


cdef inline TokenC* get_s0(const State *s) nogil:
    return &s.sent[s.stack[0]]


cdef inline TokenC* get_s1(const State *s) nogil:
    # Rely on our padding to ensure we don't go out of bounds here
    return &s.sent[s.stack[-1]]


cdef inline TokenC* get_s2(const State *s) nogil:
    # Rely on our padding to ensure we don't go out of bounds here
    return &s.sent[s.stack[-2]]

cdef const TokenC* get_right(const State* s, const TokenC* head, const int idx) nogil

cdef const TokenC* get_left(const State* s, const TokenC* head, const int idx) nogil

cdef inline bint at_eol(const State *s) nogil:
    return s.i >= s.sent_len


cdef inline bint is_final(const State *s) nogil:
    return at_eol(s) # The stack will be attached to root anyway


cdef int children_in_buffer(const State *s, const int head, const int* gold) except -1
cdef int head_in_buffer(const State *s, const int child, const int* gold) except -1
cdef int children_in_stack(const State *s, const int head, const int* gold) except -1
cdef int head_in_stack(const State *s, const int child, const int* gold) except -1

cdef State* init_state(Pool mem, TokenC* sent, const int sent_length) except NULL


cdef int count_left_kids(const TokenC* head) nogil


cdef int count_right_kids(const TokenC* head) nogil


# From https://en.wikipedia.org/wiki/Hamming_weight
cdef inline uint32_t _popcount(uint32_t x) nogil:
    """Find number of non-zero bits."""
    cdef int count = 0
    while x != 0:
        x &= x - 1
        count += 1
    return count


cdef inline uint32_t _nth_significant_bit(uint32_t bits, int n) nogil:
    cdef int i
    for i in range(32):
        if bits & (1 << i):
            n -= 1
            if n < 1:
                return i
    return 0
