/* NeoStats - IRC Statistical Services 
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
**  Portions Copyright (c) 2000-2001 ^Enigma^
**
**  Portions Copyright (c) 1999 Johnathan George net@lite.net
**
**  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
**
** NeoStats CVS Identification
** $Id: server.c 1923 2004-07-21 14:02:29Z Fish $
*/

#include "stats.h"
#include "dl.h"
#include "hash.h"
#include "log.h"
#include "ircd.h"
#include "exclude.h"
#include "server.h"
#include "users.h"
#ifdef SQLSRV
#include "sqlsrv/rta.h"
#endif

hash_t *sh;

static Server *
new_server (const char *name)
{
	Server *s;
	hnode_t *sn;

	SET_SEGV_LOCATION();
	s = calloc (sizeof (Server), 1);
	bzero(s, sizeof(Server));
	strlcpy (s->name, name, MAXHOST);
	sn = hnode_create (s);
	if (!sn) {
		nlog (LOG_WARNING, LOG_CORE, "Eeek, Hash is broken\n");
	}
	if (hash_isfull (sh)) {
		nlog (LOG_WARNING, LOG_CORE, "Eeek, Server Hash is full!\n");
	} else {
		hash_insert (sh, sn, s->name);
	}
	return s;
}

Server *
AddServer (const char *name, const char *uplink, const char* hops, const char *numeric, const char *infoline)
{
	Server *s;
	char **av;
	int ac = 0;

	nlog (LOG_DEBUG1, LOG_CORE, "New Server: %s Uplink; %s", name, uplink);
	s = new_server (name);
	if(hops) {
		s->hops = atoi (hops);
	}
	if (uplink) {
		strlcpy (s->uplink, uplink, MAXHOST);
	}
	if(infoline) {
		strlcpy (s->infoline, infoline, MAXINFO);
	}
	if(numeric) {
		s->numeric =  atoi(numeric);
	}
	s->connected_since = me.now;

	if (!ircstrcasecmp(name, me.name)) {
		s->flags |= NS_FLAGS_ME;
	}

	/* check exclusions */
	ns_do_exclude_server(s);

	/* run the module event for a new server. */
	AddStringToList (&av, (char*)name, &ac);
	AddStringToList (&av, (char*)uplink, &ac);
	AddStringToList (&av, (char*)hops, &ac);
	AddStringToList (&av, (char*)numeric, &ac);
	AddStringToList (&av, (char*)infoline, &ac);
	ModuleEvent (EVENT_SERVER, av, ac);
	free (av);
	return(s);
}

static void del_server_leaves(Server* hub)
{
	Server *s;
	hscan_t ss;
	hnode_t *sn;

	nlog (LOG_DEBUG1, LOG_CORE, "del_server_leaves: %s", hub->name);
	hash_scan_begin (&ss, sh);
	while ((sn = hash_scan_next (&ss)) != NULL) {
		s = hnode_get (sn);
		if(ircstrcasecmp(hub->name, s->uplink) == 0) {
			nlog (LOG_DEBUG1, LOG_CORE, "del_server_leaves: del child %s", s->name);
			DelServer(s->name, hub->name);
		}
	}
}

void 
DelServer (const char *name, const char* reason)
{
	Server *s;
	hnode_t *sn;
	char **av;
	int ac = 0;

	if (!name) {
		return;
	}
	sn = hash_lookup (sh, name);
	if (!sn) {
		nlog (LOG_WARNING, LOG_CORE, "DelServer: squit from unknown server %s", name);
		return;
	}
	s = hnode_get (sn);
	del_server_leaves(s);
	if(ircd_srv.noquit) {
		QuitServerUsers (s);
	}
	/* run the event for delete server */
	AddStringToList (&av, s->name, &ac);
	if(reason) {
		AddStringToList (&av, (char*)reason, &ac);
	}
	ModuleEvent (EVENT_SQUIT, av, ac);
	free (av);

	hash_delete (sh, sn);
	hnode_destroy (sn);
	free (s);
}

#ifdef BASE64SERVERNAME
Server *
findserverbase64 (const char *num)
{
	Server *s;
	hscan_t ss;
	hnode_t *sn;

	hash_scan_begin (&ss, sh);
	while ((sn = hash_scan_next (&ss)) != NULL) {
		s = hnode_get (sn);
		if(strncmp(s->name64, num, BASE64SERVERSIZE) == 0) {
			nlog (LOG_DEBUG1, LOG_CORE, "findserverbase64: %s -> %s", num, s->name);
			return s;
		}
	}
	nlog (LOG_DEBUG3, LOG_CORE, "findserverbase64: %s not found!", num);
	return NULL;
}
#endif

Server *
findserver (const char *name)
{
	Server *s;
	hnode_t *sn;

	sn = hash_lookup (sh, name);
	if (sn) {
		s = hnode_get (sn);
		return s;
	}
	nlog (LOG_DEBUG3, LOG_CORE, "findserver: %s not found!", name);
	return NULL;
}

static void 
dumpserver (Server *s)
{
#ifdef BASE64SERVERNAME
	debugtochannel("Server: %s (%s)", s->name, s->name64);
#else
	debugtochannel("Server: %s", s->name);
#endif
	debugtochannel("Flags:  %lx", s->flags);
	debugtochannel("Uplink: %s", s->uplink);
	debugtochannel("========================================");
}

void
ServerDump (const char *name)
{
	Server *s;
	hscan_t ss;
	hnode_t *sn;

	debugtochannel("================SERVDUMP================");
	if (!name) {
		hash_scan_begin (&ss, sh);
		while ((sn = hash_scan_next (&ss)) != NULL) {
			s = hnode_get (sn);
			dumpserver (s);
		}
	} else {
		s = findserver (name);
		if (s) {
			dumpserver (s);
		} else {
			debugtochannel("ServerDump: can't find server %s", name);
		}
	}
}

#ifdef SQLSRV
COLDEF neo_serverscols[] = {
	{
		"servers",
		"name",
		RTA_STR,
		MAXHOST,
		offsetof(struct Server, name),
		RTA_READONLY,
		NULL,
		NULL,
		"The name of the server linked to the IRC network"
	},
	{
		"servers",
		"hops",
		RTA_INT,
		sizeof(int),
		offsetof(struct Server, hops),
		RTA_READONLY,
		NULL, 
		NULL,
		"The Number of hops away from the NeoStats Server"
	},
	{
		"servers",
		"connected",
		RTA_INT,
		sizeof(int),
		offsetof(struct Server, connected_since),
		RTA_READONLY,
		NULL,
		NULL,
		"The time the server connected to the IRC network"
	},
	{
		"servers",
		"last_ping",
		RTA_INT,
		sizeof(int),
		offsetof(struct Server, ping),
		RTA_READONLY,
		NULL,
		NULL,
		"The last ping time to this server from the NeoStats Server"
	},
	{
		"servers",
		"flags",
		RTA_INT,
		sizeof(int),
		offsetof(struct Server, flags),
		RTA_READONLY,
		NULL,
		NULL,
		"Flags that specify special functions for this Server"
	},
	{	
		"servers",
		"uplink",
		RTA_STR,
		MAXHOST,
		offsetof(struct Server, uplink),
		RTA_READONLY,
		NULL,
		NULL,
		"The uplink Server this server is connected to. if it = self, means the NeoStats Server"
	},
	{	
		"servers",
		"infoline",
		RTA_STR,
		MAXINFO,
		offsetof(struct Server, infoline),
		RTA_READONLY,
		NULL,
		NULL,
		"The description of this server"
	},
};

TBLDEF neo_servers = {
	"servers",
	NULL, 	/* for now */
	sizeof(struct Server),
	0,
	TBL_HASH,
	neo_serverscols,
	sizeof(neo_serverscols) / sizeof(COLDEF),
	"",
	"The list of Servers connected to the IRC network"
};
#endif /* SQLSRV */


int 
init_server_hash (void)
{
	sh = hash_create (S_TABLE_SIZE, 0, 0);
	if (!sh) {
		nlog (LOG_CRITICAL, LOG_CORE, "Create Server Hash Failed\n");
		return NS_FAILURE;
	}
	AddServer (me.name, NULL, 0, NULL, me.infoline);
#ifdef SQLSRV
	/* add the server hash to the sql library */
	neo_servers.address = sh;
	rta_add_table(&neo_servers);
#endif
	return NS_SUCCESS;
}


void
PingServers (void)
{
	Server *s;
	hscan_t ss;
	hnode_t *sn;

	if(!me.synced)
		return;
	nlog (LOG_DEBUG3, LOG_CORE, "Sending pings...");
	ping.ulag = 0;

	hash_scan_begin (&ss, sh);
	while ((sn = hash_scan_next (&ss)) != NULL) {
		s = hnode_get (sn);
		if (!strcmp (me.name, s->name)) {
			s->ping = 0;
			continue;
		}
		send_ping (me.name, me.name, s->name);
	}
}

void 
FreeServers ()
{
	Server *s;
	hnode_t *sn;
	hscan_t hs;

	hash_scan_begin(&hs, sh);
	while ((sn = hash_scan_next(&hs)) != NULL ) {
		s = hnode_get (sn);
		hash_delete (sh, sn);
		hnode_destroy (sn);
		free (s);
	}
	hash_destroy(sh);

}
