/************************************************************************
*   IRC - Internet Relay Chat, src/whowas.c
*   Copyright (C) 1990 Markku Savela
*
*   This program is free software; you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation; either version 2, or (at your option)
*   any later version.
*
*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License
*   along with this program; if not, write to the Free Software
*   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
 * $Id: whowas.c,v 1.4 2003/06/14 13:55:52 tr-ircd Exp $ 
 */

#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "h.h"

static void add_whowas_to_clist(aWhowas **, aWhowas *);
static void del_whowas_from_clist(aWhowas **, aWhowas *);
static void add_whowas_to_list(aWhowas **, aWhowas *);
static void del_whowas_from_list(aWhowas **, aWhowas *);

aWhowas WHOWAS[NICKNAMEHISTORYLENGTH];
aWhowas *WHOWASHASH[WW_MAX];

int whowas_next = 0;

unsigned int hash_whowas_name(char *name)
{
  unsigned int h = 0;

  while (*name)
    {
      h = (h << 4) - (h + (unsigned char)ToLower(*name++));
    }
  return(h & (WW_MAX - 1));
}

void add_history(aClient *cptr, int online)
{
    aWhowas *new;

    new = &WHOWAS[whowas_next];

    if (new->hashv != -1) {
	if (new->online)
	    del_whowas_from_clist(&(new->online->whowas), new);
	del_whowas_from_list(&WHOWASHASH[new->hashv], new);
    }
    new->hashv = hash_whowas_name(cptr->name);
    new->logoff = NOW;
    strlcpy_irc(new->name, cptr->name, NICKLEN);
    strlcpy_irc(new->username, cptr->user->username, USERLEN);
    strlcpy_irc(new->hostname, cptr->user->fakehost, HOSTLEN);
    strlcpy_irc(new->morehost, cptr->user->host, HOSTLEN);
    strlcpy_irc(new->realname, cptr->info, REALLEN);
    DupString(new->servername, cptr->user->server);

    if (online) {
	new->online = cptr;
	add_whowas_to_clist(&(cptr->whowas), new);
    } else
	new->online = NULL;
    add_whowas_to_list(&WHOWASHASH[new->hashv], new);
    whowas_next++;
    if (whowas_next == NICKNAMEHISTORYLENGTH)
	whowas_next = 0;
}

void off_history(aClient *cptr)
{
    aWhowas *temp, *next;

    for (temp = cptr->whowas; temp; temp = next) {
	next = temp->cnext;
	temp->online = NULL;
	del_whowas_from_clist(&(cptr->whowas), temp);
    }
}

aClient *get_history(char *nick, time_t timelimit)
{
    aWhowas *temp;
    int blah;

    timelimit = NOW - timelimit;
    blah = hash_whowas_name(nick);
    temp = WHOWASHASH[blah];
    for (; temp; temp = temp->next) {
	if (irc_strcmp(nick, temp->name))
	    continue;
	if (temp->logoff < timelimit)
	    continue;
	return temp->online;
    }
    return NULL;
}

void count_whowas_memory(int *wwu, u_long *wwum)
{
    aWhowas *tmp;
    int i;
    int u = 0;
    u_long um = 0;

    /*
     * count the number of used whowas structs in 'u' 
     * count up the memory used of whowas structs in um 
     */

    for (i = 0, tmp = &WHOWAS[0]; i < NICKNAMEHISTORYLENGTH; i++, tmp++)
	if (tmp->hashv != -1) {
	    u++;
	    um += sizeof(aWhowas);
	}
    *wwu = u;
    *wwum = um;
    return;
}

void initwhowas()
{
int i;

    for (i = 0; i < NICKNAMEHISTORYLENGTH; i++) {
	memset((char *) &WHOWAS[i], '\0', sizeof(aWhowas));
	WHOWAS[i].hashv = -1;
    }
    for (i = 0; i < WW_MAX; i++)
	WHOWASHASH[i] = NULL;
}

static void add_whowas_to_clist(aWhowas ** bucket, aWhowas * whowas)
{
    whowas->cprev = NULL;
    if ((whowas->cnext = *bucket) != NULL)
	whowas->cnext->cprev = whowas;
    *bucket = whowas;
}

static void del_whowas_from_clist(aWhowas ** bucket, aWhowas * whowas)
{
    if (whowas->cprev)
	whowas->cprev->cnext = whowas->cnext;
    else
	*bucket = whowas->cnext;
    if (whowas->cnext)
	whowas->cnext->cprev = whowas->cprev;
}

static void add_whowas_to_list(aWhowas ** bucket, aWhowas * whowas)
{
    whowas->prev = NULL;
    if ((whowas->next = *bucket) != NULL)
	whowas->next->prev = whowas;
    *bucket = whowas;
}

static void del_whowas_from_list(aWhowas ** bucket, aWhowas * whowas)
{
    if (whowas->prev)
	whowas->prev->next = whowas->next;
    else
	*bucket = whowas->next;
    if (whowas->next)
	whowas->next->prev = whowas->prev;
}