/*
 * counter.c: repeated message counters
 *
 * Copyright(c) 1998-2000 - All Rights Reserved
 *
 * See the COPYRIGHT file.
 */

#ifndef lint
static char rcsid[] = "@(#)$Id: counter.c,v 1.5 2000/07/31 22:33:55 kalt Exp $";
#endif

#include "os.h"

#include "struct.h"
#include "option.h"
#include "term.h"
#include "utils.h"
#include "window.h"

extern struct server_   *server;

#if defined(HAVE_REGEXP)
struct counter_
{
  void		*reference;
  char		*string;
  struct server_ *server;
  regmatch_t	pmatch[20];
  time_t	vfirst, first, last;
  int		tcount, count;
  char		ematch; /* exact matches */
};
 
static struct counter_ *counters;
static unsigned int cnt_max, cnt_inuse = 0;

# define CNT_PERIOD	600
# define CNT_EXPIRE	120

#endif
     
void
counter_init()
{
#if defined(HAVE_REGEXP)
  cnt_max = 100;
  counters = (struct counter_ *) malloc(cnt_max*sizeof(struct counter_));
  bzero(counters, cnt_max*sizeof(struct counter_));
#endif
}

#if defined(HAVE_REGEXP)
/* counter_expire: expires entries in counters array */
void
counter_expire(entry)
  int entry;
{
  if (entry >= 0)
    {
      time_t now = time(NULL);

      if (now - counters[entry].last > CNT_EXPIRE)
	{
	  free(counters[entry].string);
	  counters[entry].string = NULL;
	  cnt_inuse--;
	}
    }
  else
    {
      vsic_slog(LOG_DEBUG, "counter_expire(-1): cnt_inuse = %d / cnt_max = %d",
		cnt_inuse, cnt_max);
      for (entry = 0; entry < cnt_max; entry++)
	  if (counters[entry].string != NULL)
	      counter_expire(entry);
      vsic_slog(LOG_DEBUG, "finished: cnt_inuse = %d / cnt_max = %d",
		cnt_inuse, cnt_max);
    }
}

/* counter_check: is it time to display something? */
static void
counter_check(entry)
  int entry;
{
  time_t now = time(NULL);

  if (now - counters[entry].first > CNT_PERIOD)
    {
      char tbuf[40];
      
      counters[entry].tcount += counters[entry].count;
      if (counters[entry].vfirst == counters[entry].first)
	  vsic_slog(LOG_CLIENT,
		"--- Next message repeated %d time%s over last %d seconds.",
		    counters[entry].count,
		    (counters[entry].count > 1) ? "s" : "",
		    counters[entry].last - counters[entry].first);
      else
	  vsic_slog(LOG_CLIENT, "--- Next message repeated %d time%s over last %d seconds (%d times since %s).",
		    counters[entry].count,
		    (counters[entry].count > 1) ? "s" : "",
		    counters[entry].last - counters[entry].first,
		    counters[entry].tcount,
		    my_cftime(tbuf, 40, "%x %X", counters[entry].vfirst));
      
      counters[entry].count = 0;
      counters[entry].ematch = 1;
    }
}

/* counter_add: count */
int
counter_add(ref, string, attributes, pmatch, flags, channel)
  void *ref;
  char *string, *channel;
  char *attributes;
  regmatch_t *pmatch;
  unsigned int *flags;
{
  int i, j;

  for (i = 0; i < cnt_max; i++)
    {
      if (counters[i].reference != ref)
	  continue;
      if (counters[i].string == NULL)
	  continue;
      for (j = 1; pmatch[j].rm_so != -1; j++)
	  if (counters[i].server == server
	      && !strncasecmp(string + pmatch[j].rm_so,
			      counters[i].string + counters[i].pmatch[j].rm_so,
			      pmatch[j].rm_eo - pmatch[j].rm_so))
	      break;
      if (pmatch[j].rm_so != -1)
	  break;
    }
  if (i == cnt_max)
    {
      for (i = 0; i < cnt_max; i++)
	  if (counters[i].string == NULL)
	      break;
      if (i == cnt_max)
	{
	  counters = realloc(counters,
			     sizeof(struct counter_)*(cnt_max += 100));
	  bzero(counters+cnt_max-100, 100*sizeof(struct counter_));
	}
      cnt_inuse++;
      counters[i].reference = ref;
      counters[i].string = strdup(string);
      counters[i].server = server;
      counters[i].vfirst = counters[i].first = counters[i].last = time(NULL);
      bcopy(pmatch, counters[i].pmatch, 20*sizeof(regmatch_t));
      counters[i].tcount = counters[i].count = 1;
      counters[i].ematch = 1;
    }
  else
    {
      counters[i].last = time(NULL);
      if (counters[i].count++ == 0)
	  counters[i].first = counters[i].last;
      if (counters[i].ematch == 1 && strcasecmp(string, counters[i].string))
	  counters[i].ematch = 0;
    }
  j = counters[i].ematch;
  counter_check(i);
  if (counters[i].count > 0
      && !(counters[i].count == 1 && counters[i].vfirst == counters[i].first))
      /* ignore,
      ** unless do_repeat() just reseted the count (displaying a message),
      ** or this is the very first message of this kind received.
      */
      *flags |= LOG_IGNORE;
  if (j == 0
      && !(counters[i].count == 1 && counters[i].vfirst == counters[i].first)
      && get_option(Z_VCOUNT, channel))
    {
      for (j = 1; pmatch[j].rm_so != -1; j++)
	{
	  char *ch = attributes + pmatch[j].rm_so;

	  while (ch < attributes + pmatch[j].rm_eo)
	      *ch++ |= TERM_UNDERLINE;
	}
      return 1;
    }
  return 0;
}
#endif
