/*
 autoignore.c : irssi

    Copyright (C) 1999-2000 Timo Sirainen

    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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "module.h"
#include "modules.h"
#include "signals.h"
#include "commands.h"
#include "levels.h"
#include "misc.h"
#include "settings.h"

#include "irc-server.h"
#include "ignore.h"

#include "autoignore.h"

/* How often to check if there's anyone to be unignored in autoignore list */
#define AUTOIGNORE_TIMECHECK 10000

static int ignore_tag;

GSList *server_autoignores(IRC_SERVER_REC *server)
{
	MODULE_SERVER_REC *rec;

	g_return_val_if_fail(server != NULL, NULL);

	rec = MODULE_DATA(server);
	return rec->ignorelist;
}

static void autoignore_remove_rec(IRC_SERVER_REC *server, AUTOIGNORE_REC *rec)
{
	MODULE_SERVER_REC *mserver;

	g_return_if_fail(server != NULL);
	g_return_if_fail(rec != NULL);

	signal_emit("autoignore remove", 2, server, rec);

	g_free(rec->nick);
	g_free(rec);

	mserver = MODULE_DATA(server);
	mserver->ignorelist = g_slist_remove(mserver->ignorelist, rec);
}

static AUTOIGNORE_REC *autoignore_find(IRC_SERVER_REC *server, const char *mask)
{
	MODULE_SERVER_REC *mserver;
	GSList *tmp;

	g_return_val_if_fail(server != NULL, NULL);
	g_return_val_if_fail(mask != NULL, NULL);

	mserver = MODULE_DATA(server);
	for (tmp = mserver->ignorelist; tmp != NULL; tmp = tmp->next) {
		AUTOIGNORE_REC *rec = tmp->data;

		if (g_strcasecmp(rec->nick, mask) == 0)
			return rec;
	}

	return NULL;
}

/* timeout function: unignore old ignores.. */
static void autoignore_timeout_server(IRC_SERVER_REC *server)
{
	MODULE_SERVER_REC *mserver;
	GSList *tmp, *next;
	time_t t;

	g_return_if_fail(server != NULL);

	mserver = MODULE_DATA(server);
	t = time(NULL);
	t -= mserver->ignore_lastcheck;

	for (tmp = mserver->ignorelist; tmp != NULL; tmp = next) {
		AUTOIGNORE_REC *rec = tmp->data;

		next = tmp->next;
		if (rec->timeleft > t)
			rec->timeleft -= t;
		else
			autoignore_remove_rec(server, rec);
	}

	mserver->ignore_lastcheck = time(NULL);
}

static int autoignore_timeout(void)
{
	g_slist_foreach(servers, (GFunc) autoignore_timeout_server, NULL);
	return 1;
}

static void autoignore_init_server(IRC_SERVER_REC *server)
{
	MODULE_SERVER_REC *mserver;

	g_return_if_fail(server != NULL);

	mserver = MODULE_DATA(server);
	mserver->ignorelist = NULL;
	mserver->ignore_lastcheck = time(NULL)-AUTOIGNORE_TIMECHECK;
}

static void autoignore_deinit_server(IRC_SERVER_REC *server)
{
	MODULE_SERVER_REC *mserver;

	g_return_if_fail(server != NULL);

	mserver = MODULE_DATA(server);
	while (mserver->ignorelist != NULL)
		autoignore_remove_rec(server, (AUTOIGNORE_REC *) mserver->ignorelist->data);
}

IGNORE_REC *ignore_find_server(IRC_SERVER_REC *server, const char *mask)
{
	GSList *tmp;

	for (tmp = ignores; tmp != NULL; tmp = tmp->next) {
		IGNORE_REC *rec = tmp->data;

		if (rec->servertag != NULL &&
		    g_strcasecmp(rec->mask, mask) == 0 &&
		    g_strcasecmp(rec->servertag, server->tag) == 0)
			return rec;
	}

	return NULL;
}

void autoignore_add(IRC_SERVER_REC *server, const char *nick, int level)
{
	MODULE_SERVER_REC *mserver;
	AUTOIGNORE_REC *rec;
	IGNORE_REC *irec;
	int igtime;

	g_return_if_fail(server != NULL);
	g_return_if_fail(nick != NULL);
	if (level == 0) return;

	igtime = settings_get_int("autoignore_time");
	if (igtime <= 0) return;

	irec = ignore_find_server(server, nick);
	if (irec == NULL) {
		irec = g_new0(IGNORE_REC, 1);
		irec->servertag = g_strdup(server->tag);
		irec->mask = g_strdup(nick);
		irec->level = level;
		ignore_add_rec(irec);
	} else {
		irec->level |= level;
		ignore_update_rec(irec);
	}

	rec = autoignore_find(server, nick);
	if (rec != NULL) {
		/* already being ignored */
		rec->timeleft = igtime;
		return;
	}

	rec = g_new(AUTOIGNORE_REC, 1);
	rec->nick = g_strdup(nick);
	rec->timeleft = igtime;
	rec->level = level;

	mserver = MODULE_DATA(server);
	mserver->ignorelist = g_slist_append(mserver->ignorelist, rec);

	signal_emit("autoignore new", 2, server, rec);
}

int autoignore_remove(IRC_SERVER_REC *server, const char *mask, int level)
{
	AUTOIGNORE_REC *rec;
	IGNORE_REC *irec;

	g_return_val_if_fail(server != NULL, FALSE);
	g_return_val_if_fail(mask != NULL, FALSE);

	irec = ignore_find_server(server, mask);
	if (irec != NULL) {
		irec->level &= ~level;
		ignore_update_rec(irec);
	}

	rec = autoignore_find(server, mask);
	if (rec != NULL && (level & rec->level)) {
		rec->level &= ~level;
		if (rec->level == 0) autoignore_remove_rec(server, rec);
		return TRUE;
	}

	return FALSE;
}

static void sig_flood(IRC_SERVER_REC *server, const char *nick, const char *host, gpointer levelp)
{
	int level, check_level;

	level = GPOINTER_TO_INT(levelp);
	check_level = level2bits(settings_get_str("autoignore_levels"));

	if (level & check_level)
		autoignore_add(server, nick, level);
}

static void autoignore_remove_level(const char *nick, int level)
{
	AUTOIGNORE_REC *rec;
	GSList *tmp;

	g_return_if_fail(nick != NULL);

	for (tmp = servers; tmp != NULL; tmp = tmp->next) {
		IRC_SERVER_REC *server = tmp->data;

		rec = autoignore_find(server, nick);
		if (rec != NULL && (rec->level & level)) {
			rec->level &= ~level;
			if (rec->level == 0) autoignore_remove_rec(server, rec);
		}
	}
}

static void sig_ignore_destroyed(IGNORE_REC *ignore)
{
	if (ignore->mask != NULL)
		autoignore_remove_level(ignore->mask, MSGLEVEL_ALL);
}

static void sig_ignore_changed(IGNORE_REC *ignore)
{
	if (ignore->mask != NULL)
		autoignore_remove_level(ignore->mask, ~ignore->level);
}

void autoignore_init(void)
{
	settings_add_int("flood", "autoignore_time", 300);
	settings_add_str("flood", "autoignore_levels", "");

	ignore_tag = g_timeout_add(AUTOIGNORE_TIMECHECK, (GSourceFunc) autoignore_timeout, NULL);

	signal_add("server connected", (SIGNAL_FUNC) autoignore_init_server);
	signal_add("server disconnected", (SIGNAL_FUNC) autoignore_deinit_server);
	signal_add("flood", (SIGNAL_FUNC) sig_flood);
	signal_add("ignore destroyed", (SIGNAL_FUNC) sig_ignore_destroyed);
	signal_add("ignore changed", (SIGNAL_FUNC) sig_ignore_changed);
}

void autoignore_deinit(void)
{
	g_source_remove(ignore_tag);

	signal_remove("server connected", (SIGNAL_FUNC) autoignore_init_server);
	signal_remove("server disconnected", (SIGNAL_FUNC) autoignore_deinit_server);
	signal_remove("flood", (SIGNAL_FUNC) sig_flood);
	signal_remove("ignore destroyed", (SIGNAL_FUNC) sig_ignore_destroyed);
	signal_remove("ignore changed", (SIGNAL_FUNC) sig_ignore_changed);
}