GSSAPI="BASE"  # This ensures that a full module is generated by Cython

from gssapi.raw.cython_types cimport *
from gssapi.raw.ext_buffer_sets cimport *
from gssapi.raw.names cimport Name
from gssapi.raw.oids cimport OID

from gssapi.raw.misc import GSSError
from gssapi.raw.named_tuples import InquireNameResult, GetNameAttributeResult

cdef extern from "python_gssapi_ext.h":
    OM_uint32 gss_display_name_ext(OM_uint32 *min_stat, gss_name_t name,
                                   gss_OID name_type,
                                   gss_buffer_t output_name) nogil

    OM_uint32 gss_inquire_name(OM_uint32 *min_stat, gss_name_t name,
                               int *name_is_mn, gss_OID *mech_type,
                               gss_buffer_set_t *attrs) nogil

    OM_uint32 gss_get_name_attribute(OM_uint32 *min_stat, gss_name_t name,
                                     gss_buffer_t attr, int *authenticated,
                                     int *complete, gss_buffer_t value,
                                     gss_buffer_t display_value,
                                     int *more) nogil

    OM_uint32 gss_set_name_attribute(OM_uint32 *min_stat, gss_name_t name,
                                     int complete, gss_buffer_t attr,
                                     gss_buffer_t value) nogil

    OM_uint32 gss_delete_name_attribute(OM_uint32 *min_stat, gss_name_t name,
                                        gss_buffer_t attr) nogil

    OM_uint32 gss_export_name_composite(OM_uint32 *min_stat, gss_name_t name,
                                        gss_buffer_t exported_name) nogil

    # GSS_C_NT_COMPOSITE_EXPORT lives in ext_rfc6680_comp_oid.pyx


def display_name_ext(Name name not None, OID name_type not None):
    # GSS_C_EMPTY_BUFFER
    cdef gss_buffer_desc output_name = gss_buffer_desc(0, NULL)

    cdef OM_uint32 maj_stat, min_stat

    maj_stat = gss_display_name_ext(&min_stat, name.raw_name,
                                    &name_type.raw_oid, &output_name)

    if maj_stat == GSS_S_COMPLETE:
        name_text = (<char*>output_name.value)[:output_name.length]
        gss_release_buffer(&min_stat, &output_name)
        return name_text
    else:
        raise GSSError(maj_stat, min_stat)


def inquire_name(Name name not None, mech_name=True, attrs=True):
    cdef int *name_is_mn_ptr = NULL
    cdef gss_OID *mn_mech_ptr = NULL
    cdef gss_buffer_set_t *attr_names_ptr = NULL

    cdef gss_buffer_set_t attr_names = GSS_C_NO_BUFFER_SET
    if attrs:
        attr_names_ptr = &attr_names

    cdef int name_is_mn = 0
    cdef gss_OID mn_mech
    if mech_name:
        name_is_mn_ptr = &name_is_mn
        mn_mech_ptr = &mn_mech

    cdef OM_uint32 maj_stat, min_stat

    maj_stat = gss_inquire_name(&min_stat, name.raw_name, name_is_mn_ptr,
                                mn_mech_ptr, attr_names_ptr)

    cdef int i
    cdef OID py_mech = None
    if maj_stat == GSS_S_COMPLETE:
        py_attr_names = []

        if attr_names != GSS_C_NO_BUFFER_SET:
            for i in range(attr_names.count):
                attr_name = attr_names.elements[i]
                py_attr_names.append(
                    (<char*>attr_name.value)[:attr_name.length]
                )

            gss_release_buffer_set(&min_stat, &attr_names)

        if name_is_mn:
            py_mech = OID()
            py_mech.raw_oid = mn_mech[0]

        return InquireNameResult(py_attr_names, <bint>name_is_mn, py_mech)
    else:
        raise GSSError(maj_stat, min_stat)


def set_name_attribute(Name name not None, attr not None, value not None,
                       bint complete=False):
    cdef gss_buffer_desc attr_buff = gss_buffer_desc(len(attr), attr)
    cdef gss_buffer_desc val_buff

    cdef OM_uint32 maj_stat, min_stat

    cdef size_t value_len = len(value)
    cdef size_t i
    for val in value:
        val_buff = gss_buffer_desc(len(val), val)
        i += 1
        if i == value_len:
            maj_stat = gss_set_name_attribute(&min_stat, name.raw_name,
                                              complete, &attr_buff, &val_buff)
        else:
            maj_stat = gss_set_name_attribute(&min_stat, name.raw_name, 0,
                                              &attr_buff, &val_buff)

        if maj_stat != GSS_S_COMPLETE:
            raise GSSError(maj_stat, min_stat)


def get_name_attribute(Name name not None, attr not None, more=None):
    cdef gss_buffer_desc attr_buff = gss_buffer_desc(len(attr), attr)

    cdef gss_buffer_desc val_buff = gss_buffer_desc(0, NULL)
    cdef gss_buffer_desc displ_val_buff = gss_buffer_desc(0, NULL)
    cdef int complete
    cdef int authenticated

    cdef int more_val = -1
    py_vals = []
    py_displ_vals = []

    cdef OM_uint32 maj_stat, min_stat

    while more_val != 0:
        maj_stat = gss_get_name_attribute(&min_stat, name.raw_name,
                                          &attr_buff,
                                          &authenticated, &complete,
                                          &val_buff, &displ_val_buff,
                                          &more_val)

        if maj_stat == GSS_S_COMPLETE:
            py_vals.append((<char*>val_buff.value)[:val_buff.length])
            py_displ_vals.append(
                (<char*>displ_val_buff.value)[:displ_val_buff.length])

            gss_release_buffer(&min_stat, &val_buff)
            gss_release_buffer(&min_stat, &displ_val_buff)
        else:
            raise GSSError(maj_stat, min_stat)

    return GetNameAttributeResult(py_vals, py_displ_vals, <bint>authenticated,
                                  <bint>complete)


def delete_name_attribute(Name name not None, attr not None):
    cdef gss_buffer_desc attr_buff = gss_buffer_desc(len(attr), attr)

    cdef OM_uint32 maj_stat, min_stat

    maj_stat = gss_delete_name_attribute(&min_stat, name.raw_name,
                                         &attr_buff)

    if maj_stat != GSS_S_COMPLETE:
        raise GSSError(maj_stat, min_stat)


def export_name_composite(Name name not None):
    cdef gss_buffer_desc res = gss_buffer_desc(0, NULL)

    cdef OM_uint32 maj_stat, min_stat

    maj_stat = gss_export_name_composite(&min_stat, name.raw_name, &res)

    if maj_stat == GSS_S_COMPLETE:
        py_res = (<char*>res.value)[:res.length]
        gss_release_buffer(&min_stat, &res)
        return py_res
    else:
        raise GSSError(maj_stat, min_stat)
