#ifndef WIN32
#ifdef DO_NETINFO
#include <netinfo/ni.h>
#include <stdlib.h>
#include <stdio.h>
#include "lprintf.h"
#include "netinfo.h"
#define DMID 27
#include "debug.h"

struct netinfo {
	int custom_errcode;
	ni_status status;
	void *handle;
};

static char *_str(int i) {
	static char bf[50];
	lprintf(bf,50,"%d",i);
	return bf;
}

NetInfo *netinfo_open() {
	NetInfo *ni;
	ni_status r;

	ni = calloc(1,sizeof(NetInfo));	
	ni->status = ni_open(NULL, ".", &ni->handle);
    if (ni->status != NI_OK) {
		free(ni);
		return NULL;
	}
	return ni;
}
void netinfo_free(NetInfo **ni) {
	if (!ni || !*ni) return ;
	ni_free((*ni)->handle);
	free(*ni); *ni = NULL;
}
int netinfo_exists(NetInfo *ni, char *root, char *path) {
	char bf[BFSZ];
	ni_status r;
	ni_id dir;

	if (!ni) return FALSE;

	lprintf(bf,BFSZ,"/%s/%s",root,path);
	r = ni_pathsearch(ni->handle, &dir, bf);
	return r == NI_OK;
}
int netinfo_user_exists(NetInfo *ni, char *username) {
	return netinfo_exists(ni,"users",username);
}
int netinfo_group_exists(NetInfo *ni, char *groupname) {
	return netinfo_exists(ni,"groups",groupname);
}
int netinfo_group_gid(NetInfo *ni, char *group, int *gid) {
	ni_idlist idlist;
	ni_namelist vals;
	ni_id dir, sub;
	int i, j;

	ni->status = ni_pathsearch(ni->handle, &dir, "/groups");
	if (ni->status != NI_OK) return FALSE;

	ni->status = ni_children(ni->handle, &dir, &idlist);
	if (ni->status != NI_OK) return FALSE;

	for(i = 0; i < idlist.ni_idlist_len; i++) {
		NI_INIT(&sub);
		sub.nii_object = idlist.ni_idlist_val[i];

		NI_INIT(&vals);
		ni->status = ni_lookupprop(ni->handle, &sub, "name", &vals);
		if (ni->status != NI_OK) return FALSE;

		for(j = 0; j < vals.ni_namelist_len && strcmp(group,vals.ni_namelist_val[j])!=0; j++);
		if (j == vals.ni_namelist_len) continue;

		NI_INIT(&vals);
		ni->status = ni_lookupprop(ni->handle, &sub, "gid", &vals);
		if (ni->status != NI_OK) return FALSE;

		*gid = atoi(vals.ni_namelist_val[0]);
		break;
	}
	return TRUE;
}
int netinfo_user_new_uid(NetInfo *ni, int *uid) {
	int cur_id, max_id, i, j;
	ni_idlist idlist;
	ni_namelist vals;
	ni_id dir, user;

	ni->status = ni_pathsearch(ni->handle, &dir, "/users");
	if (ni->status != NI_OK) return FALSE;

	ni->status = ni_children(ni->handle, &dir, &idlist);
	if (ni->status != NI_OK) return FALSE;

	cur_id = max_id = 0;
	for(i = 0; i < idlist.ni_idlist_len; i++) {
		NI_INIT(&user);
		user.nii_object = idlist.ni_idlist_val[i];

		ni->status = ni_lookupprop(ni->handle, &user, "uid", &vals);
		if (ni->status != NI_OK) return FALSE;

		for(j = 0; j < vals.ni_namelist_len; j++) {
			cur_id = atoi(vals.ni_namelist_val[j]);
			if (cur_id > max_id) max_id = cur_id;
		}
	}

	*uid = max_id+1;
	return TRUE;
}
int netinfo_group_new_gid(NetInfo *ni, int *gid) {
	int cur_id, max_id, i, j;
	ni_idlist idlist;
	ni_namelist vals;
	ni_id dir, user;

	ni->status = ni_pathsearch(ni->handle, &dir, "/groups");
	if (ni->status != NI_OK) return FALSE;

	ni->status = ni_children(ni->handle, &dir, &idlist);
	if (ni->status != NI_OK) return FALSE;

	cur_id = max_id = 0;
	for(i = 0; i < idlist.ni_idlist_len; i++) {
		NI_INIT(&user);
		user.nii_object = idlist.ni_idlist_val[i];

		ni->status = ni_lookupprop(ni->handle, &user, "gid", &vals);
		if (ni->status != NI_OK) return FALSE;

		for(j = 0; j < vals.ni_namelist_len; j++) {
			cur_id = atoi(vals.ni_namelist_val[j]);
			if (cur_id > max_id) max_id = cur_id;
		}
	}

	*gid = max_id+1;
	return TRUE;
}
int netinfo_user_add(NetInfo *ni, char *username, Pair *misc) {
	ni_proplist proplist;
	ni_id dir, newdir;
	ni_property prop;
	Pair *pmisc;

	if (!ni) return FALSE;
	if (!username) {
		ni->custom_errcode = NI_E_PARAM;
		return FALSE;
	}

	ni->status = ni_pathsearch(ni->handle, &dir, "/users");
	if (ni->status != NI_OK) return FALSE;

	NI_INIT(&proplist);

    NI_INIT(&prop);
	prop.nip_name = ni_name_dup("name");
    ni_namelist_insert(&prop.nip_val, username, NI_INDEX_NULL);
	ni_proplist_insert(&proplist, prop, NI_INDEX_NULL);
	ni_name_free(&prop.nip_name);
    ni_namelist_free(&prop.nip_val);

	for(pmisc = misc; pmisc; pmisc = pmisc->next) {
		NI_INIT(&prop);
		prop.nip_name = ni_name_dup(pmisc->var);
		ni_namelist_insert(&prop.nip_val, pmisc->val, NI_INDEX_NULL);
		ni_proplist_insert(&proplist, prop, NI_INDEX_NULL);
		ni_name_free(&prop.nip_name);
		ni_namelist_free(&prop.nip_val);
	}

	ni->status = ni_create(ni->handle, &dir, proplist, &newdir, NI_INDEX_NULL);
    ni_proplist_free(&proplist);

    if (ni->status != NI_OK) return FALSE;

	return TRUE;
}
int netinfo_group_add(NetInfo *ni, char *groupname, Pair *misc) {
	ni_proplist proplist;
	ni_id dir, newdir;
	ni_property prop;
	Pair *pmisc;

	if (!ni) return FALSE;
	if (!groupname) {
		ni->custom_errcode = NI_E_PARAM;
		return FALSE;
	}

	ni->status = ni_pathsearch(ni->handle, &dir, "/groups");
	if (ni->status != NI_OK) return FALSE;

	NI_INIT(&proplist);

    NI_INIT(&prop);
	prop.nip_name = ni_name_dup("name");
    ni_namelist_insert(&prop.nip_val, groupname, NI_INDEX_NULL);
	ni_proplist_insert(&proplist, prop, NI_INDEX_NULL);
	ni_name_free(&prop.nip_name);
    ni_namelist_free(&prop.nip_val);

	for(pmisc = misc; pmisc; pmisc = pmisc->next) {
		NI_INIT(&prop);
		prop.nip_name = ni_name_dup(pmisc->var);
		ni_namelist_insert(&prop.nip_val, pmisc->val, NI_INDEX_NULL);
		ni_proplist_insert(&proplist, prop, NI_INDEX_NULL);
		ni_name_free(&prop.nip_name);
		ni_namelist_free(&prop.nip_val);
	}

	ni->status = ni_create(ni->handle, &dir, proplist, &newdir, NI_INDEX_NULL);
    ni_proplist_free(&proplist);

    if (ni->status != NI_OK) return FALSE;

	return TRUE;
}
char *netinfo_error(NetInfo *ni, char *bf, int bfsz) {
	if (ni == NULL) lprintf(bf,bfsz,"NetInfo ptr is null");
	else {
		switch(ni->custom_errcode) {
		case NI_E_PARAM:
			ncpy(bf,"required parameter null",bfsz-1); 
			break;
		default:
			lprintf(bf,bfsz,"%s",ni_error(ni->status));
			break;
		};		 
	}
	return bf;
}

#ifdef TESTING_NETINFO
int main(int argc, char *argv[]) {
  	Pair *misc = NULL;
	char bf[BFSZ];
	NetInfo *ni;
	int gid,uid;	

	ni = netinfo_open();
	if (!ni) {
		printf("netinfo_open: open failed\n");
		exit(1);
	}

	if (!netinfo_user_exists(ni,"chrisp"))
		printf("netinfo_user_exists: chrisp does not exist\n");
	else
		printf("netinfo_user_exists: chrisp exists\n");

	if (!netinfo_user_exists(ni,"jesse"))
		printf("netinfo_user_exists: jesse does not exist\n");
	else
		printf("netinfo_user_exists: jesse exists\n");
	
	if (!netinfo_user_exists(ni,"mail"))
		printf("netinfo_user_exists: mail does not exist\n");
	else
		printf("netinfo_user_exists: mail exists\n");

	if (!netinfo_user_exists(ni,"test"))
		printf("netinfo_user_exists: test does not exist\n");
	else
		printf("netinfo_user_exists: test exists\n");

	if (!netinfo_group_gid(ni,"mail",&gid)) {
		printf("netinfo_group_gid: %s\n",netinfo_error(ni,bf,BFSZ));

		if (!netinfo_group_new_gid(ni,&gid))
			printf("netinfo_group_new_gid: %s\n",netinfo_error(ni,bf,BFSZ));
		else {
			printf("netinfo_group_new_gid: free gid=%d\n",gid);

			pair_add(&misc,"gid",_str(gid));
			pair_add(&misc,"passwd","*");
			if (!netinfo_group_add(ni,"mail",misc))
				printf("netinfo_group_add: %s\n",netinfo_error(ni,bf,BFSZ));
			else
				printf("netinfo_group_add: group added\n");
			pair_free(&misc);
			misc = NULL;
		}
	}
	else
		printf("netinfo_group_gid: mail=%d\n",gid);

	if (!netinfo_user_new_uid(ni, &uid))
		printf("netinfo_user_new_uid: %s\n",netinfo_error(ni,bf,BFSZ));
	else
		printf("netinfo_user_new_uid: free uid=%d\n",uid);

	pair_add(&misc,"realname","SurgeMail Mail Server");
	pair_add(&misc,"shell","/dev/null");
	pair_add(&misc,"expire","0");
	pair_add(&misc,"change","0");
	pair_add(&misc,"passwd","*");
	pair_add(&misc,"home","/usr/local/surgemail");
	pair_add(&misc,"uid",_str(uid));
	pair_add(&misc,"gid",_str(gid));

	if (!netinfo_user_add(ni,"test",misc))
		printf("netinfo_user_add: %s\n",netinfo_error(ni,bf,BFSZ));
	else
		printf("netinfo_user_add: test created\n");

	netinfo_free(&ni);
}
#endif //TESTING_NETINFO
#endif // OSX
#endif // WIN32
