package openldap

/*#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <ldap.h>

int _berval_get_len(struct berval **ber, int i){
	return ber[i]->bv_len;
}

char* _berval_get_value(struct berval **ber, int i){
	return ber[i]->bv_val;
}

*/
// #cgo CFLAGS: -DLDAP_DEPRECATED=1
// #cgo linux CFLAGS: -DLINUX=1
// #cgo LDFLAGS: -lldap -llber
import "C"

import (
	"errors"
	"fmt"
	"unsafe"
)

// ------------------------------------------ RESULTS methods ---------------------------------------------
/*

	openldap C API : 

    int ldap_count_messages( LDAP *ld, LdapMessage *result )
    LdapMessage *ldap_first_message( LDAP *ld, LdapMessage *result )
    LdapMessage *ldap_next_message ( LDAP *ld, LdapMessage *message )

	int ldap_count_entries( LDAP *ld, LdapMessage *result )
	LdapMessage *ldap_first_entry( LDAP *ld, LdapMessage *result )
	LdapMessage *ldap_next_entry ( LDAP *ld, LdapMessage *entry )

    char *ldap_first_attribute(LDAP *ld, LdapMessage *entry, BerElement **berptr )
    char *ldap_next_attribute (LDAP *ld, LdapMessage *entry, BerElement *ber )

    char **ldap_get_values(LDAP *ld, LdapMessage *entry, char *attr)
    struct berval **ldap_get_values_len(LDAP *ld, LdapMessage *entry,char *attr)

    int ldap_count_values(char **vals)
    int ldap_count_values_len(struct berval **vals)
    void ldap_value_free(char **vals)
    void ldap_value_free_len(struct berval **vals)

*/

func (self *LdapMessage) Count() int {
	// API : int ldap_count_messages(LDAP *ld, LDAPMessage *chain )
	// err : (count = -1)
	count := int(C.ldap_count_messages(self.ldap.conn, self.msg))
	if count == -1 {
		panic("LDAP::Count() (ldap_count_messages) error (-1)")
	}
	return count

}

func (self *LdapMessage) FirstMessage() *LdapMessage {

	var msg *C.LDAPMessage
	msg = C.ldap_first_message(self.ldap.conn, self.msg)
	if msg == nil {
		return nil
	}
	_msg := new(LdapMessage)
	_msg.ldap = self.ldap
	_msg.errno = 0
	_msg.msg = msg
	return _msg
}

func (self *LdapMessage) NextMessage() *LdapMessage {
	var msg *C.LDAPMessage
	msg = C.ldap_next_message(self.ldap.conn, self.msg)

	if msg == nil {
		return nil
	}
	_msg := new(LdapMessage)
	_msg.ldap = self.ldap
	_msg.errno = 0
	_msg.msg = msg
	return _msg
}

/* an alias to ldap_count_message() ? */
func (self *LdapEntry) CountEntries() int {
	// API : int ldap_count_messages(LDAP *ld, LDAPMessage *chain )
	// err : (count = -1)
	return int(C.ldap_count_entries(self.ldap.conn, self.entry))
}

func (self *LdapMessage) FirstEntry() *LdapEntry {

	var msg *C.LDAPMessage
	// API: LdapMessage *ldap_first_entry( LDAP *ld, LdapMessage *result )
	msg = C.ldap_first_entry(self.ldap.conn, self.msg)
	if msg == nil {
		return nil
	}
	_msg := new(LdapEntry)
	_msg.ldap = self.ldap
	_msg.errno = 0
	_msg.entry = msg
	return _msg
}

func (self *LdapEntry) NextEntry() *LdapEntry {
	var msg *C.LDAPMessage
	// API: LdapMessage *ldap_next_entry ( LDAP *ld, LdapMessage *entry )
	msg = C.ldap_next_entry(self.ldap.conn, self.entry)

	if msg == nil {
		return nil
	}
	_msg := new(LdapEntry)
	_msg.ldap = self.ldap
	_msg.errno = 0
	_msg.entry = msg
	return _msg
}

func (self *LdapEntry) FirstAttribute() (string, error) {

	var ber *C.BerElement

	// API: char *ldap_first_attribute(LDAP *ld, LdapMessage *entry, BerElement **berptr )
	rv := C.ldap_first_attribute(self.ldap.conn, self.entry, &ber)

	if rv == nil {
		// error
		return "", nil
	}
	self.ber = ber
	return C.GoString(rv), nil
}

func (self *LdapEntry) NextAttribute() (string, error) {

	// API: char *ldap_next_attribute (LDAP *ld, LdapMessage *entry, BerElement *ber )
	rv := C.ldap_next_attribute(self.ldap.conn, self.entry, self.ber)

	if rv == nil {
		// error
		return "", nil
	}
	return C.GoString(rv), nil
}

// private func
func sptr(p uintptr) *C.char {
	return *(**C.char)(unsafe.Pointer(p))
}

// private func used to convert null terminated char*[] to go []string
func cstrings_array(x **C.char) []string {
	var s []string
	for p := uintptr(unsafe.Pointer(x)); sptr(p) != nil; p += unsafe.Sizeof(uintptr(0)) {
		s = append(s, C.GoString(sptr(p)))
	}
	return s
}

/*
 FIXME: does not work for binary attributes
 FIXME:
  If  the  attribute values are binary in nature, and thus not suitable to be returned as an array of char *'s, the ldap_get_values_len() routine can be used instead.  It
  takes the same parameters as ldap_get_values(), but returns a NULL-terminated array of pointers to berval structures, each containing the length of and a pointer  to  a
  value.
 */

// OK for pure ASCI entries.
func (self *LdapEntry) GetValues_ascii(attr string) []string {

	_attr := C.CString(attr)
	defer C.free(unsafe.Pointer(_attr))

	// DEPRECATED
	// API: char **ldap_get_values(LDAP *ld, LdapMessage *entry, char *attr)
	values := cstrings_array(C.ldap_get_values(self.ldap.conn, self.entry, _attr))

	return values
}

// FIXME: need to verify binary values.
func (self *LdapEntry) GetValues(attr string) []string {
	var s []string

	_attr := C.CString(attr)
	defer C.free(unsafe.Pointer(_attr))

	var bv **C.struct_berval
	
	//API: struct berval **ldap_get_values_len(LDAP *ld, LDAPMessage *entry, char *attr)
	bv = C.ldap_get_values_len(self.ldap.conn, self.entry, _attr)

	var i int
	count := int(C.ldap_count_values_len(bv))

	for i = 0 ; i < count; i++ {
		s = append(s, C.GoStringN(C._berval_get_value(bv, C.int(i)), C._berval_get_len(bv, C.int(i))))
	}

	// free allocated array (bv)
	C.ldap_value_free_len(bv)

	return s
}

// ------------------------------------------------ RESULTS -----------------------------------------------
/*
    int ldap_result ( LDAP *ld, int msgid, int all, struct timeval *timeout, LdapMessage **result );
	int ldap_msgfree( LdapMessage *msg );
	int ldap_msgtype( LdapMessage *msg );
	int ldap_msgid  ( LdapMessage *msg );

*/

// Result()
// take care to free LdapMessage result with MsgFree()
//
func (self *Ldap) Result() (*LdapMessage, error) {

	var msgid int = 1
	var all int = 1

	var tv C.struct_timeval
	tv.tv_sec = 30

	var msg *C.LDAPMessage

	// API: int ldap_result( LDAP *ld, int msgid, int all, struct timeval *timeout, LDAPMessage **result );
	rv := C.ldap_result(self.conn, C.int(msgid), C.int(all), &tv, &msg)

	if rv != LDAP_OPT_SUCCESS {
		return nil, errors.New(fmt.Sprintf("LDAP::Result() error :  %d (%s)", rv, ErrorToString(int(rv))))
	}

	_msg := new(LdapMessage)
	_msg.ldap = self
	_msg.errno = int(rv)
	_msg.msg = msg

	return _msg, nil
}

// MsgFree() is used to free LDAP::Result() allocated data
//
// returns -1 on error.
//
func (self *LdapMessage) MsgFree() int{
        if self.msg != nil {
                rv := C.ldap_msgfree(self.msg)
                self.msg = nil
                return int(rv)
        }
        return -1
}


//  ---------------------------------------- DN Methods ---------------------------------------------------
/*

	char *ldap_get_dn( LDAP *ld, LdapMessage *entry)
	int   ldap_str2dn( const char *str, LDAPDN *dn, unsigned flags)
	void  ldap_dnfree( LDAPDN dn)
	int   ldap_dn2str( LDAPDN dn, char **str, unsigned flags)

	char **ldap_explode_dn( const char *dn, int notypes)
	char **ldap_explode_rdn( const char *rdn, int notypes)

	char *ldap_dn2ufn  ( const char * dn )
	char *ldap_dn2dcedn( const char * dn )
	char *ldap_dcedn2dn( const char * dn )
	char *ldap_dn2ad_canonical( const char * dn )

*/

// GetDn() return the DN (Distinguish Name) for self LdapEntry
func (self *LdapEntry) GetDn() string {
	// API: char *ldap_get_dn( LDAP *ld, LDAPMessage *entry )
	rv := C.ldap_get_dn(self.ldap.conn, self.entry)
	defer C.free(unsafe.Pointer(rv))

	if rv == nil {
		err := self.ldap.Errno()
		panic(fmt.Sprintf("LDAP::GetDn() error %d (%s)", err, ErrorToString(err)))
	}

	val := C.GoString(rv)
	return val
}
