/*
 * whowas.c   a linked list buffer of people who have left your channel 
 * mainly used for ban prot and stats stuff.
 * Should even speed stuff up a bit too.
 *
 * Written by Scott H Kilau
 *
 * Copyright(c) 1995
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
 *
 * Modified by Flier
 *
 * $Id: whowas.c,v 1.9 2002/01/23 18:48:10 f Exp $
 */

#include "irc.h"
#include "vars.h"
#include "ircaux.h"
#include "window.h"
#include "whois.h"
#include "hook.h"
#include "input.h"
#include "names.h"
#include "alias.h"
#include "output.h"
#include "numbers.h"
#include "status.h"
#include "screen.h"
#include "edit.h"
#include "config.h"
#include "list.h"

#include "whowas.h"
#include "myvars.h"

#define whowas_userlist_max 150
#define whowas_reg_max 300
#define whowas_chan_max 20

extern int  CheckChannel _((char *, char *));

WhowasList        *whowas_userlist_list=(WhowasList *) 0;
WhowasList        *whowas_reg_list=(WhowasList *) 0;
WhowasChanList    *whowas_chan_list=(WhowasChanList *) 0;

static int whowas_userlist_count=0;
static int whowas_reg_count=0;
static int whowas_chan_count=0;

extern WhowasList *check_whowas_buffer(nick,userhost,channel,unlink)
char *nick;
char *userhost;
char *channel;
int  unlink;
{
    WhowasList *tmp;
    WhowasList *last=(WhowasList *) 0;

    for (tmp=whowas_userlist_list;tmp;tmp=tmp->next) {
        if (!my_stricmp(tmp->nicklist->userhost,userhost) &&
            !my_stricmp(tmp->channel,channel)) {
            if (unlink) {
                if (last) last->next=tmp->next;
                else whowas_userlist_list=tmp->next;
                whowas_userlist_count--;
            }
            return(tmp);
        }
        last=tmp;
    }
    last=(WhowasList *) 0;
    for (tmp=whowas_reg_list;tmp;tmp=tmp->next) {
        if (!my_stricmp(tmp->nicklist->userhost,userhost) &&
            !my_stricmp(tmp->channel,channel)) {
            if (unlink) {
                if (last) last->next=tmp->next;
                else whowas_reg_list=tmp->next;
                whowas_reg_count--;
            }
            return(tmp);
        }
        last=tmp;
    }
    return((WhowasList *) 0);
}

void add_to_whowas_buffer(nicklist,channel)
NickList *nicklist;
char *channel;
{
    WhowasList *new;
    WhowasList **slot;

    if (!nicklist || !(nicklist->userhost)) return;
    if (nicklist->frlist) {
        if (whowas_userlist_count>=whowas_userlist_max) {
            whowas_userlist_count-=
                remove_oldest_whowas(&whowas_userlist_list,0,
                                     (whowas_userlist_max+1)-whowas_userlist_count);
        }
        new=(WhowasList *) new_malloc(sizeof(WhowasList));
        new->channel=(char *) 0;
        new->nicklist=nicklist;
        new->nicklist->next=(NickList *) 0;
        malloc_strcpy(&(new->channel),channel);
        new->time=time(NULL);
        /* we've created it, now put it in order */
        for (slot=&whowas_userlist_list;*slot;slot=&(*slot)->next)
            if ((*slot)->time>new->time) break;
        new->next=*slot;
        *slot=new;
        whowas_userlist_count++;
    }
    else {
        if (whowas_reg_count>=whowas_reg_max) {
            whowas_reg_count-=
                remove_oldest_whowas(&whowas_reg_list,0,
                                     (whowas_reg_max+1)-whowas_reg_count);
        }
        new=(WhowasList *) new_malloc(sizeof(WhowasList));
        new->channel=(char *) 0;
        new->nicklist=nicklist;
        new->nicklist->next=(NickList *) 0;
        malloc_strcpy(&(new->channel),channel);
        new->time=time(NULL);
        /* we've created it, now put it in order */
        for (slot=&whowas_reg_list;*slot;slot=&(*slot)->next)
            if ((*slot)->time>new->time) break;
        new->next=*slot;
        *slot=new;
        whowas_reg_count++;
    }
}

int remove_oldest_whowas(list,timet,count)
WhowasList **list;
time_t timet;
int count;
{
    WhowasList *tmp=(WhowasList *) 0;
    time_t t;
    int total=0;

    /* if no ..count.. then remove ..time.. links */
    if (!count) {
        t=time(NULL);
        while (*list && ((*list)->time+timet)<=t) {
            tmp=*list;
            new_free(&(tmp->nicklist->nick));
            new_free(&(tmp->nicklist->userhost));
            new_free(&(tmp->nicklist));
            new_free(&(tmp->channel));
            *list=tmp->next;
            new_free(&tmp);
            total++;
        }
    }
    else {
        while (*list && count) {
            tmp=*list;
            new_free(&(tmp->nicklist->nick));
            new_free(&(tmp->nicklist->userhost));
            new_free(&(tmp->nicklist));
            new_free(&(tmp->channel));
            *list=tmp->next;
            new_free(&tmp);
            total++;
            count--;
        }
    }
    return(total);
}

void clean_whowas_list() {
    whowas_userlist_count-=remove_oldest_whowas(&whowas_userlist_list,25*60,0);
    whowas_reg_count-=remove_oldest_whowas(&whowas_reg_list,15*60,0);
}

/* Used to rehash whowas listings for new users */
void synch_whowas_adduser(added)
struct friends *added;
{
    WhowasList *tmp;
    char user[mybufsize/2];

    for (tmp=whowas_userlist_list;tmp;tmp=tmp->next) {
        if (!tmp->nicklist) continue;
        if (CheckChannel(tmp->channel,added->channels)) {
            snprintf(user,sizeof(user),"%s!%s",tmp->nicklist->nick,tmp->nicklist->userhost);
            if (wild_match(added->userhost,user)) tmp->nicklist->frlist=added;
        }
    }
    for (tmp=whowas_reg_list;tmp;tmp=tmp->next) {
        if (!tmp->nicklist) continue;
        if (CheckChannel(tmp->channel,added->channels)) {
            snprintf(user,sizeof(user),"%s!%s",tmp->nicklist->nick,tmp->nicklist->userhost);
            if (wild_match(added->userhost,user)) tmp->nicklist->frlist=added;
        }
    }
}

/* Used to rehash whowas listings for removed userlist entries */
void synch_whowas_unuser(entry)
struct friends *entry;
{
    WhowasList *tmp;

    for (tmp=whowas_userlist_list;tmp;tmp=tmp->next) {
        if (!tmp->nicklist || !(tmp->nicklist->frlist)) continue;
        if (!my_stricmp(tmp->nicklist->frlist->userhost,entry->userhost) &&
            !my_stricmp(tmp->nicklist->frlist->channels,entry->channels))
                tmp->nicklist->frlist=(struct friends *) 0;
    }
    for (tmp=whowas_reg_list;tmp;tmp=tmp->next) {
        if (!tmp->nicklist || !(tmp->nicklist->frlist)) continue;
        if (!my_stricmp(tmp->nicklist->frlist->userhost,entry->userhost) &&
            !my_stricmp(tmp->nicklist->frlist->channels,entry->channels))
                tmp->nicklist->frlist=(struct friends *) 0;
    }
}

/* Used to rehash whowas listings for new shitlist entries */
void synch_whowas_addshit(added)
struct autobankicks *added;
{
    WhowasList *tmp;
    char user[BIG_BUFFER_SIZE+1];

    for (tmp=whowas_userlist_list;tmp;tmp=tmp->next) {
        if (!tmp->nicklist) continue;
        if (CheckChannel(tmp->channel,added->channels)) {
            snprintf(user,sizeof(user),"%s!%s",tmp->nicklist->nick,tmp->nicklist->userhost);
            if (wild_match(added->userhost,user)) tmp->nicklist->shitlist=added;
        }
    }
    for (tmp=whowas_reg_list;tmp;tmp=tmp->next) {
        if (!tmp->nicklist) continue;
        if (CheckChannel(tmp->channel,added->channels)) {
            snprintf(user,sizeof(user),"%s!%s",tmp->nicklist->nick,tmp->nicklist->userhost);
            if (wild_match(added->userhost,user)) tmp->nicklist->shitlist=added;
        }
    }
}

/* Used to rehash whowas listings for removed shitlist entries */
void synch_whowas_unshit(entry)
struct autobankicks *entry;
{
    WhowasList *tmp;

    for (tmp=whowas_userlist_list;tmp;tmp=tmp->next) {
        if (!tmp->nicklist || !(tmp->nicklist->shitlist)) continue;
        if (!my_stricmp(tmp->nicklist->shitlist->userhost,entry->userhost) &&
            !my_stricmp(tmp->nicklist->shitlist->channels,entry->channels))
                tmp->nicklist->shitlist=(struct autobankicks *) 0;
    }
    for (tmp=whowas_reg_list;tmp;tmp=tmp->next) {
        if (!tmp->nicklist || !(tmp->nicklist->shitlist)) continue;
        if (!my_stricmp(tmp->nicklist->shitlist->userhost,entry->userhost) &&
            !my_stricmp(tmp->nicklist->shitlist->channels,entry->channels))
                tmp->nicklist->shitlist=(struct autobankicks *) 0;
    }
}


/* BELOW THIS MARK IS THE CHANNEL WHOWAS STUFF */

extern WhowasChanList *check_whowas_chan_buffer(channel,unlink)
char *channel;
int unlink;
{
    WhowasChanList *tmp;
    WhowasChanList *last=(WhowasChanList *) 0;

    for (tmp=whowas_chan_list;tmp;tmp=tmp->next) {
        if (!my_stricmp(tmp->channellist->channel,channel)) {
            if (unlink) {
                if (last) last->next=tmp->next;
                else whowas_chan_list=tmp->next;
                whowas_chan_count--;
            }
            return(tmp);
        }
        last=tmp;
    }
    return((WhowasChanList *) 0);
}

int add_to_whowas_chan_buffer(channel)
ChannelList *channel;
{
    WhowasChanList *new;
    WhowasChanList **slot;

    if (check_whowas_chan_buffer(channel->channel,0)) return(0);
    if (whowas_chan_count>=whowas_chan_max) {
        whowas_chan_count-=
            remove_oldest_chan_whowas(&whowas_chan_list,0,
                                      (whowas_chan_max+1)-whowas_chan_count);
    }
    new=(WhowasChanList *) new_malloc(sizeof(WhowasChanList));
    new->channellist=channel;
    new->time=time(NULL);
    /* we've created it, now put it in order */
    for (slot=&whowas_chan_list;*slot;slot=&(*slot)->next)
        if ((*slot)->time>new->time) break;
    new->next=*slot;
    *slot=new;
    whowas_chan_count++;
    return(1);
}

int remove_oldest_chan_whowas(list,timet,count)
WhowasChanList **list;
time_t timet;
int count;
{
    WhowasChanList *tmp=(WhowasChanList *) 0;
    time_t t;
    int total=0;

    /* if no ..count.. then remove ..time.. links */
    if (!count) {
        t = time(NULL);
        while (*list && ((*list)->time+timet)<=t) {
            tmp=*list;
            new_free(&(tmp->channellist->channel));
            new_free(&(tmp->channellist->s_mode));
            new_free(&(tmp->channellist->key));
#ifdef EXTRAS
            new_free(&(tmp->channellist->modelock));
            new_free(&(tmp->channellist->topiclock));
#endif
            new_free(&(tmp->channellist->topicstr));
            new_free(&(tmp->channellist->topicwho));
            tmp->channellist->nicks=(NickList *) 0;
            tmp->channellist->banlist=(struct bans *) 0;
            new_free(&(tmp->channellist));
            *list=tmp->next;
            new_free(&tmp);
            total++;
        }
    }
    else {
        while (*list && count) {
            tmp=*list;
            new_free(&(tmp->channellist->channel));
            new_free(&(tmp->channellist->s_mode));
            new_free(&(tmp->channellist->key));
#ifdef EXTRAS
            new_free(&(tmp->channellist->modelock));
            new_free(&(tmp->channellist->topiclock));
#endif
            new_free(&(tmp->channellist->topicstr));
            new_free(&(tmp->channellist->topicwho));
            tmp->channellist->nicks=(NickList *) 0;
            tmp->channellist->banlist=(struct bans *) 0;
            new_free(&(tmp->channellist));
            *list=tmp->next;
            new_free(&tmp);
            total++;
            count--;
        }
    }
    return(total);
}

void clean_whowas_chan_list() {
    whowas_chan_count-=remove_oldest_chan_whowas(&whowas_chan_list,24*60*60,0);
}

/* Clean up memory used by whowas lists */
void CleanUpWhowas() {
    remove_oldest_whowas(&whowas_userlist_list,0,whowas_userlist_max);
    remove_oldest_whowas(&whowas_reg_list,0,whowas_reg_max);
    remove_oldest_chan_whowas(&whowas_chan_list,0,whowas_chan_max);
}

/*
void show_whowas() {
    int count=1;
    WhowasList *tmp;

    say("Scanning whowas userlist, flag thinks there is %d entries",whowas_userlist_count);
    for (tmp=whowas_userlist_list;tmp;tmp=tmp->next) {
        if (!tmp->nicklist) say("%d: NO NICKLIST!!! BUG!",count);
        else say("%d: %s %s",count,tmp->nicklist->nick,tmp->nicklist->userhost);
        if (!tmp->channel) say("%d: NO CHANNEL!!! BUG!",count);
        else say("%d: %s",count,tmp->channel);
        count++;
    }
    count=1;
    say("Scanning whowas reg, flag thinks there is %d entries",whowas_reg_count);
    for (tmp=whowas_reg_list;tmp;tmp=tmp->next) {
        if (!tmp->nicklist) say("%d: NO NICKLIST!!! BUG!",count);
        else say("%d: %s %s",count,tmp->nicklist->nick,tmp->nicklist->userhost);
        if (!tmp->channel) say("%d: NO CHANNEL!!! BUG!",count);
        else say("%d: %s",count,tmp->channel);
        count++;
        }
}*/
