/*-
 * Copyright (c) 1998-2005 Joao Cabral
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      DHIS(c)  Dynamic Host Information System Release 5
 */

#include "dhisd.h"
#include "online.h"
#include "db.h"
#include "network.h"
#include "log.h"

online_t *onbase=NULL;

int on_whereis(int id) {

	online_t *p;

	p=onbase;
	while(p!=NULL) {
		if(p->id==id) 
			return(p->addr);		
		p=p->next;
	}
	return(0);
}

int on_add(int id,int addr,int port,int proto,int refresh) {

	online_t *p;
	struct in_addr in;
	unsigned char str[128];

	p=onbase;
	if(p!=NULL)
	while(p->next!=NULL) p=p->next;
	if(p==NULL) {
		onbase=(online_t *)malloc(sizeof(online_t));
		p=onbase;
	} else {
		p->next=(online_t *)malloc(sizeof(online_t));
		p=p->next;
	}
	if(p==NULL) return(0);
	p->next=NULL;
	p->id=id;
	p->addr=addr;
	if(proto>=4) p->port=port;
	else p->port=DHISD_PORT;
	p->proto=proto;
	p->check_fails=0;
	p->sid=0;

	if(refresh>=MIN_NEXT_CHECK && refresh <=MAX_NEXT_CHECK) {
		p->refresh=refresh;
	}
	else {

		if(refresh)
			DSYSLOG(1,(LOG_DEBUG,"on_add(): Refresh rate out of limits\n"));

		p->refresh=NEXT_CHECK;
	}
	if(p->proto>=4)
	p->ka=time(NULL) + p->refresh;
	else
	p->ka=time(NULL);

	srandom(time(NULL));
	while(!(p->sid=random()));
	
	DSYSLOG(1,(LOG_DEBUG,"on_add(): Marking Host %d Online\n",p->id));
	
	mark_online(p->id,p->addr);

	in.s_addr = p->addr;
	sprintf(str,"-> online %d [%s]",p->id,inet_ntoa(in));
	msg_log(str);
	return(p->sid);
}

int on_delete(int id) {

	online_t *p1,*p2;
	unsigned char str[128];
	struct in_addr in;


	p1=onbase;
	while(p1!=NULL) {
		if(p1->id==id) break;
		p2=p1;
		p1=p1->next;
	}

	if(p1==NULL) return(0);

	if(p1==onbase) onbase=p1->next;
	else p2->next=p1->next;

	DSYSLOG(1,(LOG_DEBUG,"on_add(): Marking Host %d Offline\n",p1->id));

	mark_offline(p1->id,p1->addr);

	in.s_addr = p1->addr;
	sprintf(str,"-> offline %d [%s]",p1->id,inet_ntoa(in));
	msg_log(str);

	free(p1);

	return(1);
}

int on_update(int id,int addr,int port,int proto,int refresh) {

	online_t *p;
	struct in_addr in;
	unsigned char str[128];
	int oaddr;

	p=onbase;
	while(p!=NULL) {
		if(p->id==id) break;
		p=p->next;
	}

	if(p==NULL) {
		return(on_add(id,addr,port,proto,refresh));
	}
	if(proto>=4) {
	p->ka=time(NULL)+p->refresh;
	p->check_fails=0;
	}
	else
	p->ka=time(NULL);

	if(p->addr==addr) return(p->sid);
	
	DSYSLOG(1,(LOG_DEBUG,"on_add(): Updating R3 host %d \n",p->id));

	oaddr= p->addr;
	p->addr=addr;
	mark_update(p->id,p->addr,oaddr);

	in.s_addr = p->addr;
	sprintf(str,"-> update %d [%s]",p->id,inet_ntoa(in));
	msg_log(str);

	return(p->sid);
}

int on_parse(void) {

	online_t *p1,*p2;
	time_t t;

	DSYSLOG(2,(LOG_DEBUG,"on_parse(): Executing.\n"));

	p1=onbase;
	t=time(NULL);
	while(p1!=NULL) {


		p2=p1->next;
		if(p1->proto==3) 
		if((t  - p1->ka) > KA_OFFLINE)
			on_delete(p1->id);
		if(p1->proto==4 || p1->proto ==5) {
		if(p1->check_fails > CHECK_FAILS)
			on_delete(p1->id);
		else {
			if (p1->proto == 4) {
			if(p1->ka < time(NULL)) {
			r4_check_req_t m;

			DSYSLOG(1,(LOG_DEBUG,"on_parse(): Sending R4_CHECK_REQ to %d\n",
			     p1->id)); 

			m.hdr.opcode=R4_CHECK_REQ;
			m.next_check=p1->refresh;
			net_write_message((msg_t *)&m,p1->addr,p1->port);
			p1->check_fails++;
			} /* end if ka */
			} /* end proto */
			if(p1->proto==5) {
			if(p1->ka < time(NULL)) {
			check_req_t m;

			DSYSLOG(1,(LOG_DEBUG,"on_parse(): Sending CHECK_REQ to %d\n",
			     p1->id));

			m.hdr.opcode=CHECK_REQ;
			m.hdr.hostid=p1->id;
			m.next_check=p1->refresh;
			net_write_message((msg_t *)&m,p1->addr,p1->port);
			p1->check_fails++;
			} /* end if ka */
			} /* end proto 5 */
		}}
		p1=p2;
	}
	return(1);
}

int on_getsid(int id) {

	online_t *p;
	p=onbase;
	while(p!=NULL) {
	if(p->id==id) return(p->sid);
	p=p->next;
		
	}
	return(0);
}

void on_free(void) {

	online_t *op;

	op=onbase;
	while(op!=NULL) {
		mark_offline(op->id,op->addr);
		op=op->next;
		free(onbase);
		onbase=op;
	}
}
