static char *RCSid = "$Header: news.c,v 1.5 89/11/14 23:03:23 mr-frog Exp $";

/*
 * news.c
 *
 * server in-memory version of news stuff.
 * We keep track of a list of free news articles,
 * and use them when a news request arrives.
 *
 * from PSL Empire, 1985
 * Dave Pare, 1989
 */

#include "misc.h"
#include "news.h"
#include "file.h"
#include <fcntl.h>

struct free {
	struct free *next;
	int id;
};

struct	free *freelist;

addfree(n)
	int	n;
{
	struct	free *fp;

	fp = (struct free *) malloc(sizeof(*fp));
	fp->next = freelist;
	fp->id = n;
	freelist = fp;
}

/*
 * snoop through the news articles looking
 * for articles which have timed out.  Only
 * called when no free items left.
 */
findfree()
{
	register time_t oldnewstime;
	register int n;
	struct	nwsstr news;
	time_t	newstime;

	(void) time(&newstime);
	oldnewstime = newstime - NEWS_PERIOD;
	for (n=0; getnews(n, &news); n++) {
		if (news.nws_when < oldnewstime)
			addfree(n);
	}
	if (freelist == 0) {
		makefree(250);
		findfree();
	}
}

int
nextfree()
{
	struct	free *fp;
	int	id;

	if (freelist == 0)
		findfree();
	fp = freelist;
	freelist = fp->next;
	id = fp->id;
	free(fp);
	return id;
}

/*
* create some new (free) news articles.
* articles with page = 0 are considered "free"
*/
makefree(n)
	int	n;
{
	int	start;
	struct	nwsstr news;

	start = ef_nelem(EF_NEWS);
	news.nws_ano = 0;
	news.nws_vrb = 0;
	news.nws_vno = 0;
	news.nws_ntm = 0;
	news.nws_when = 0;
	while (n > 0) {
		addfree(start);
		ef_nbwrite(EF_NEWS, start, (char *)&news);
		start++;
		n--;
	}
	/* reopen the file */
	ef_close(EF_NEWS);
	ef_open(EF_NEWS, O_RDWR, 0);
}

#define SLOTS	5

struct newscache {
	struct nwsstr news;
	int id;
};


struct newscache *
ncache(now, actor, event, victim, times)
	time_t	now;
	int	actor;
	int	event;
	int	victim;
	int	times;
{
	static	struct newscache cache[MAXNOC][SLOTS];
	register struct newscache *np;
	int	i;
	int	oldslot;
	time_t	oldtime;

	oldslot = -1;
	oldtime = 0x7fffffff;
	for (i=0; i<SLOTS; i++) {
		np = &cache[actor][i];
		if (np->news.nws_when < oldtime) {
			oldslot = i;
			oldtime = np->news.nws_when;
		}
		if (np->id == 0)
			continue;
		if ((now - np->news.nws_when) > minutes(5))
			continue;
		if (np->news.nws_vrb == event && np->news.nws_vno == victim &&
		    np->news.nws_ntm + times <= 127) {
			np->news.nws_ntm += times;
			return np;
		}
	}
	if (oldslot < 0) {
		logerror("internal error; ncache oldslot < 0");
		return &cache[actor][0];
	}
	np = &cache[actor][oldslot];
	np->news.nws_ano = actor;
	np->news.nws_vno = victim;
	np->news.nws_when = now;
	np->news.nws_vrb = event;
	np->news.nws_ntm = times;
	np->id = nextfree();
	return np;
}

nreport(actor, event, victim, times)
	int	actor;
	int	event;
	int	victim;
	int	times;
{
	struct	newscache *np;
	time_t	now;

	time(&now);
	if (event == N_WON_SECT && victim == 0)
		event = N_TOOK_UNOCC;
	np = ncache(now, actor, event, victim, times);
	ef_nbwrite(EF_NEWS, np->id, (char *)&np->news);
}
