/*
 * $Id: users.c,v 2.0 1993/08/06 05:02:35 marc Exp marc $
 */

#include <sys/types.h>
#include <sys/time.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/Table.h>

#include <string.h>
#include <pwd.h>
#include <stdio.h>
#ifdef HESIOD
#include <hesiod.h>
#define getpwnam hes_getpwnam
#endif
#include "xznol.h"
#include "mymalloc.h"
#include "util.h"
#include "zephyr.h"

static user *users = NULL;

static Widget userlist = NULL;

static XawTableLine *userlines = NULL;
static int nuserlines = 0;
static int auserlines = 0;

void add_user_to_userlist(user *u)
{
   if (userlines) {
      nuserlines++;
      if (nuserlines >= auserlines) {
	 auserlines *= 2;
	 userlines = (XawTableLine *)
	    myrealloc(userlines, auserlines*sizeof(XawTableLine));
      }
      userlines[nuserlines-1].element = u->line;
      userlines[nuserlines-1].nelem = LINE_USERBASE;

      if (userlist)
	 XawTableChange(userlist, userlines, nuserlines, 0, True);
   }
}   

Widget user_list_constructor(Widget parent, char *name)
{
   user **p;

   /* create the lines for the table */

   nuserlines = 0;
   auserlines = 4;
   userlines = (XawTableLine *)
      mymalloc(auserlines*sizeof(XawTableLine));

   for (p = &users; *p; p = &((*p)->next))
      add_user_to_userlist(*p);

   /* create the widget, instantiate the data in it. */

   userlist = XtCreateManagedWidget(name, tableWidgetClass, parent, NULL, 0);

   XawTableChange(userlist, userlines, nuserlines, 0, True);

   return(userlist);
}

void make_user_line(user *u, char *name, char *kname)
{
   struct passwd *pw;
   char *tmp;
   int i;

   pw = getpwnam(name);

   bzero(u->line, sizeof(u->line));

   u->line[LINE_USER] = name;
   u->line[LINE_KNAME] = strdup(kname);
   if (pw && *(pw->pw_gecos)) {
      u->line[LINE_FULL] = strdup(pw->pw_gecos);
      for (i=LINE_NICK; i<=LINE_HPHONE; i++) {
	 tmp = strchr(u->line[i-1],',');
	 if (tmp) {
	    *tmp = '\0';
	    u->line[i] = tmp+1;
	 } else {
	    u->line[i] = "";
	 }
      }
   } else {
      for (i=LINE_FULL; i<=LINE_HPHONE; i++)
	 u->line[i] = "";
   }
}

void free_user_line(user *u)
{
   if (*(u->line[LINE_FULL])) myfree(u->line[LINE_FULL]);
}

user *find_user_field(int field, char *name)
{
   user **p;

   for (p = &users; *p; p = &((*p)->next))
      if (strcasecmp((*p)->line[field],name) == 0)
	 return(*p);
   return(NULL);
}  

/* this is basically for xznol.c so it doesn't need to know about LINE_* */
user *find_user(char **line)
{
   return(find_user_field(LINE_KNAME, line[LINE_KNAME]));
}

char *get_user_fnum_text(user *u, int fnum)
{
   if (fnum < 0 || fnum >= LINE_USERBASE)
      return("(no such field)");
   else
      return(u->line[fnum]);
}   

char *get_user_field_text(user *u, char *field)
{
   int fnum;

   if (strcasecmp(field, "USER") == 0)
      fnum = LINE_USER;
   else if (strcasecmp(field, "KNAME") == 0)
      fnum = LINE_KNAME;
   else if (strcasecmp(field, "FULL") == 0)
      fnum = LINE_FULL;
   else if (strcasecmp(field, "NICK") == 0)
      fnum = LINE_NICK;
   else if (strcasecmp(field, "OFFICE") == 0)
      fnum = LINE_OFFICE;
   else if (strcasecmp(field, "OPHONE") == 0)
      fnum = LINE_OPHONE;
   else if (strcasecmp(field, "HPHONE") == 0)
      fnum = LINE_HPHONE;
   else
      fnum = atoi(field);

   return(get_user_fnum_text(u, fnum));
}

/* name and info must be free'd or used, here */

void add_user(table *t, char *name, char *info)
{
   user *u;
   char kname[1024];
   int tcheck;

   strcpy(kname,name);
   zephyr_fixuser(kname);

   /* check to see if the user is already in the list.  If so, 
      just add the table to the user's record */

   if (u = find_user_field(LINE_USER, name)) {
      u->ntables++;
      u->tables = (table **) myrealloc((void *) u->tables,
				       u->ntables*sizeof(table *));
      u->tables[u->ntables-1] = t;
      add_user_to_table(t, u, info);
   } else {
      user *n;

      n = (user *) mymalloc(sizeof(user));
      make_user_line(n, name, kname);
      n->tables = (table **) mymalloc(sizeof(table *));
      n->tables[0] = t;
      n->ntables = 1;
      n->lastloc = 0;
      n->recheck = 0;
      n->next = users;
      users = n;

      add_user_to_table(t, n, info);
      add_user_to_userlist(n);

      u = n;
   }

   if ((tcheck = table_recheck(t)) &&
       (!u->recheck ||
	(u->recheck > tcheck)))
      u->recheck = tcheck;
}

void redisplay_user(char *kname)
{
   user *u;
   int i;

   if (u = find_user_field(LINE_KNAME, kname))
      for (i=0; i<u->ntables; i++)
	 redisplay_table(u->tables[i]);
}

int login_user(char *kname, struct _ZLocations_t *zloc)
{
   user *u;
   int i;
   int changed = 0;

   if (u = find_user_field(LINE_KNAME, kname)) {
      for (i=0; i<u->ntables; i++)
	 changed += login_user_in_table(u->tables[i], u->line, zloc);
      u->lastloc = time(NULL);
   }

   return(changed);
}

int logout_user(char *kname, struct _ZLocations_t *zloc)
{
   user *u;
   int i;
   int changed = 0;

   if (u = find_user_field(LINE_KNAME, kname)) {
      for (i=0; i<u->ntables; i++)
	 changed += logout_user_in_table(u->tables[i], u->line, zloc);
      u->lastloc = time(NULL);
   }

   return(changed);
}

int replace_user(char *kname, struct _ZLocations_t *zloc, int nzloc)
{
   user *u;
   int i;
   int changed = 0;

   if (u = find_user_field(LINE_KNAME, kname)) {
      for (i=0; i<u->ntables; i++)
	 changed += replace_user_in_table(u->tables[i], u->line, zloc, nzloc);
      u->lastloc = time(NULL);
   }

   return(changed);
}

void locate_user(user *u)
{
   zephyr_locate(u->line[LINE_KNAME]);
}

void sub_user(user *u)
{
   zephyr_sub(u->line[LINE_KNAME]);
}

/* locate all users who haven't been heard about since "since" */
void locate_all_users(int force)
{
   user **p;
   time_t now;

   now = time(NULL);

   for (p = &users; *p; p = &((*p)->next)) {
      if (force || ((*p)->recheck && (now - (*p)->lastloc > (*p)->recheck))) {
	 process_xinput();
#ifdef RCDEBUG
	 if (force == 0)
	    printf("rechecking user %s\n", (*p)->line[LINE_KNAME]);
#endif
	 locate_user(*p);
      }
   }
}

void sub_all_users()
{
   user **p;

   for (p = &users; *p; p = &((*p)->next)) {
      process_xinput();
      sub_user(*p);
   }
}
