/*
 * tabkey.c: completion for /msg and public chats
 *
 * Copyright(c) 1997-2000 - All Rights Reserved
 *
 * See the COPYRIGHT file.
 */

#ifndef lint
static char rcsid[] = "@(#)$Id: tabkey.c,v 1.14 2000/07/31 22:33:56 kalt Exp $";
#endif

#include "os.h"

#include "struct.h"
#include "option.h"

extern struct server_ *server;

struct nlist_
{
  char		*nick;
  char		*uh;
  time_t	ts;
  struct nlist_	*nextn;
  struct server_ *svr;
};

static struct nlist_ *tablist = NULL, *tabnext = NULL;
static struct nlist_ *spacelist = NULL, *spacenext = NULL;

static time_t lastg = 0;

static void
nlist_collect(list)
  struct nlist_ **list;
{
  struct nlist_ **ntmp = list, *del;

  lastg = time(NULL);
  while (*ntmp)
    {
      if ((lastg - (del = (*ntmp))->ts) > 3600)
	{
	  /* no need to worry about nicknext here */
	  *ntmp = del->nextn;
	  free(del->nick);
	  if (del->uh)
	      free(del->uh);
	  free(del);
	}
      else
	  ntmp = &((*ntmp)->nextn);
    }
}

static char
real_add(list, next, nick, uh)
  char *nick, *uh;
  struct nlist_ **list, **next;
{
  struct nlist_ **ntmp = list, *top;
  char rc = 0;

  while (*ntmp)
    {
      if (!strcasecmp((*ntmp)->nick, nick) && (*ntmp)->svr == server)
	  break;
      ntmp = &((*ntmp)->nextn);
    }
  if (*ntmp)
    {
      (*ntmp)->nick = strdup(nick);
      top = *ntmp;
      *ntmp = top->nextn;
      if (uh && *uh && strcmp(top->uh, uh))
	{
	  free(top->uh);
	  top->uh = strdup(uh);
	  rc = 1;
	}
      else if ((time(NULL) - top->ts) > 600)
	  rc = 1;
    }
  else
    {
      top = (struct nlist_ *) malloc(sizeof(struct nlist_));
      top->nick = strdup(nick);
      top->uh = (uh) ? strdup(uh) : NULL;
      top->svr = server;
      rc = 1;
    }
  top->ts = time(NULL);
  top->nextn = *list;
  *list = top;
  *next = NULL;
  if ((time(NULL) - lastg) > 1800)
      nlist_collect(list);
  return rc;
}

/* tab_add: adds a nickname/servicename to the list.
 *	    uh is the nick's u@h, or "" if irrelevant (DCC, unknown)
 *	    uh is NULL for services.
 */
char
tab_add(nick, uh)
  char *nick, *uh;
{
  return real_add(&tablist, &tabnext, nick, uh);
}

/* space_add: adds a nickname to the list.
 *	      uh should be ""
 */
char
space_add(nick, uh)
  char *nick, *uh;
{
  assert (*uh == '\0');

  return real_add(&spacelist, &spacenext, nick, uh);
}

static char *
real_get(list, next)
  struct nlist_ *list, **next;
{
  struct nlist_ *loop = (*next) ? *next : list;
  int looped = 0;
  struct server_ *smatch = server;
  static char eresult[512];

  if (list == tablist && get_option(Z_ETABKEY, NULL))
      smatch = NULL;

  do
    {
      if (*next == loop)
	  looped += 1;
      if (*next)
	{
	  if ((*next = (*next)->nextn) == NULL)
	      *next = list;
	}
      else
	  *next = list;
    }
  while ((!*next || ((smatch != NULL && (*next)->svr != smatch)
		     && *((*next)->nick) != '='))
	 && looped <= 1);

  if (*next && (*next)->uh == NULL)
      strcpy(eresult, "@"); /* service */
  else
      eresult[0] = '\0'; /* nickname / dcc chat */

  if (smatch != NULL)
    {
      if (*next && ((*next)->svr == server || *((*next)->nick) == '='))
	  strcat(eresult, (*next)->nick);
      else
	  eresult[0] = '\0';
    }
  else
      if (*next)
	{
	  if (*eresult == '@')
	      strcat(eresult, (*next)->nick);
	  else if ((*next)->nick[0] == '=')
	      strcpy(eresult, (*next)->nick);
	  else
	      sprintf(eresult, "%s@[%s]", (*next)->nick, (*next)->svr->sname);
	}
      else
	  eresult[0] = '\0';

  return eresult;
}

/* tab_get: returns next nickname in the list. (services prefixed with @) */
char *
tab_get()
{
  return real_get(tablist, &tabnext);
}

/* space_get: returns next nickname in the list */
char *
space_get()
{
  return real_get(spacelist, &spacenext);
}
