/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	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.

**********************************************************************/



#include	"memory_debug.h"
#include	"task.h"
#include	"lock_level.h"
#include	"long_char.h"
#include	"xl.h"

typedef struct _server_info {
	SERVER_INFO		i;
	unsigned int		w_time;
	unsigned int		r_time;
	struct _server_info *	next;
	struct _server_info * 	ip_next;
} _SERVER_INFO;

SEM	serverinfo_lock;
_SERVER_INFO * serverinfo_hash[SI_HASH_SIZE];
_SERVER_INFO * serverinfo_ip_hash[SI_HASH_SIZE];

void
init_serverinfo()
{
	serverinfo_lock = new_lock(LL_SERVERINFO);
}

unsigned int
get_si_key(char * name)
{
unsigned int ret;
	ret = 0;
	for ( ; *name ; name ++ )
		ret += *name;
	return ret % SI_HASH_SIZE;
}


_SERVER_INFO *
_search_serverinfo(char * name)
{
_SERVER_INFO * ret;
unsigned int key;
	key = get_si_key(name);
	for ( ret = serverinfo_hash[key] ; ret ; ret = ret->next ) {
		if ( ret->i.name == 0 )
			continue;
		if ( (ret->i.active_flags & SIA_NAME) == 0  )
			continue;
		if ( strcmp(ret->i.name,name) == 0 )
			return ret;
	}
	return 0;
}

_SERVER_INFO *
_search_serverinfo_ip(unsigned int ip)
{
unsigned int key;
_SERVER_INFO * ret;
	key = ip % SI_HASH_SIZE;
	for ( ret = serverinfo_ip_hash[key] ; ret ; ret = ret->ip_next ) {
		if ( !(ret->i.active_flags & SIA_IP) )
			continue;
		if ( ret->i.ip == ip )
			return ret;
	}
	return 0;
}

int
get_serverinfo(SERVER_INFO * si)
{
int key;
_SERVER_INFO * ret;
int er;
char * name;
	lock_task(serverinfo_lock);
	if ( si->active_flags & SIA_NAME ) {
		if ( si->name == 0 ) {
			er = -1;
			goto end;
		}
		ret = _search_serverinfo(si->name);
	}
	else if ( si->active_flags & SIA_IP ) {
		if ( si->ip == 0 ) {
			er = -1;
			goto end;
		}
		ret = _search_serverinfo_ip(si->ip);
	}
	else {
		er = -1;
		goto end;
	}
	if ( ret ) {
		ret->r_time = get_xltime();
		name = si->name;
		*si = ret->i;
		si->name = name;
		er = 0;
	}
	else	er = -1;
end:
	unlock_task(serverinfo_lock,"get_serverinfo");
	return er;
}

void
_insert_name_hash(_SERVER_INFO * si)
{
int key;
	key = get_si_key(si->i.name);
	si->next = serverinfo_hash[key];
	serverinfo_hash[key] = si;
}

void
_insert_ip_hash(_SERVER_INFO * si)
{
int key;
	key = si->i.ip % SI_HASH_SIZE;
	si->next = serverinfo_ip_hash[key];
	serverinfo_ip_hash[key] = si;
}

_SERVER_INFO *
_new_serverinfo(SERVER_INFO * si)
{
_SERVER_INFO * ret;
	ret = d_alloc(sizeof(*ret));
	memset(ret,0,sizeof(*ret));
	if ( (si->active_flags & (SIA_NAME|SIA_IP)) == 0 )
		return 0;
	ret->i.active_flags = si->active_flags & (SIA_NAME|SIA_IP);
	if ( si->name && (si->active_flags & SIA_NAME) )
		ret->i.name = copy_str(si->name);
	else	ret->i.name = 0;
	if ( si->ip && (si->active_flags & SIA_IP) )
		ret->i.ip = si->ip;
	else	ret->i.ip = 0;
	if ( !(ret->i.active_flags & SIA_STATUS) ) {
		ret->i.active_flags |= SIA_STATUS;
		ret->i.status = SIS_UNKNOWN;
	}

	ret->w_time = get_xltime();
	ret->r_time = 0;

	if ( ret->i.active_flags & SIA_NAME )
		_insert_name_hash(ret);
	if ( ret->i.active_flags & SIA_IP )
		_insert_ip_hash(ret);

	return ret;
}


int
set_serverinfo(SERVER_INFO * si,int cmd,int set_if)
{
int key;
_SERVER_INFO * ret;
int new;
int er;
	lock_task(serverinfo_lock);

	if ( si->active_flags & SIA_NAME ) {
		if ( si->name == 0 ) {
			er = -1;
			goto end;
		}
		ret = _search_serverinfo(si->name);
		if ( ret == 0 ) {
			if ( si->active_flags & SIA_IP )
				goto ip_search;
			er = -1;
			goto end;
		}
	}
	else if ( si->active_flags & SIA_IP ) {
	ip_search:
		if ( si->ip == 0 ) {
			er = -1;
			goto end;
		}
		ret = _search_serverinfo_ip(si->ip);
	}
	else {
		er = -1;
		goto end;
	}
	if ( ret == 0 ) {
		ret = _new_serverinfo(si);
		if ( ret == 0 ) {
			er = -1;
			goto end;
		}
		new = 1;
	}
	else	new = 0;
	er = 0;
	if ( !(ret->i.status & set_if) ) {
		er = -2;
		goto end;
	}
	if ( si->active_flags & SIA_STATUS )
		ret->i.status = si->status;
	if ( si->active_flags & SIA_IP )
		ret->i.ip = si->ip;
	if ( si->active_flags & SIA_THPUT ) {
		switch ( cmd ) {
		case SIC_SET:
			ret->i.thput = si->thput;
			break;
		case SIC_AVG:
			if ( new )
				ret->i.thput = si->thput;
			else if ( ret->i.thput < si->thput )
				ret->i.thput = si->thput;
			else	ret->i.thput =
					0.9*ret->i.thput +
					0.1*si->thput;
			break;
		default:
			er = -1;
			break;
		}
	}
	if ( si->active_flags & SIA_DATA ) {
		switch ( cmd ) {
		case SIC_SET:
			ret->i.data = si->data;
			break;
		case SIC_SET_IF_ZERO:
			if ( ret->i.data == 0 )
				ret->i.data = si->data;
			else 	er = -2;
			break;
		default:
			er = -1;
			break;
		}
	}
	ret->i.active_flags |= si->active_flags;
	ret->w_time = get_xltime();
/*
if ( ret->i.name )
printf("serverinfo(%x) = %x(%s) %i - %i\n",
ret,ret->i.ip,ret->i.name,ret->i.thput,new);
else
printf("serverinfo(%x) = %x %i - %i\n",ret,ret->i.ip,ret->i.thput,new);
*/
end:
	unlock_task(serverinfo_lock,"set_serverinfo");
	return er;
}

