/* $Id$ */
/* 統計今日、週、月、年熱門話題 */

#include "bbs.h"
#include "fnv_hash.h"

char *myfile[] =
{"day", "week", "month", "year"};
int mycount[4] =
{7, 4, 12};
int mytop[] =
{10, 50, 100, 100};
char *mytitle[] =
{"日十", "週五十", "月百", "年度百"};


#define HASHSIZE 1024
#define TOPCOUNT 200


struct postrec
{
    char author[13];		/* author name */
    char board[13];		/* board name */
    char title[66];		/* title name */
    time4_t date;		/* last post's date */
    int number;			/* post number */
    struct postrec *next;	/* next rec */
}
*bucket[HASHSIZE];


/* 100 bytes */
struct posttop
{
    char author[13];		/* author name */
    char board[13];		/* board name */
    char title[66];		/* title name */
    time4_t date;		/* last post's date */
    int number;			/* post number */
}
top[TOPCOUNT], *tp;


/* ---------------------------------- */
/* hash structure : array + link list */
/* ---------------------------------- */


void
search(t)
    struct posttop *t;
{
    struct postrec *p, *q, *s;
    int i, found = 0;

    i = fnv1a_32_str(t->title, FNV1_32_INIT) % HASHSIZE;
    q = NULL;
    p = bucket[i];
    while (p && (!found))
    {
	if (!strcmp(p->title, t->title) && !strcmp(p->board, t->board))
	    found = 1;
	else
	{
	    q = p;
	    p = p->next;
	}
    }
    if (found)
    {
	p->number += t->number;
	if (p->date < t->date)	/* 取較近日期 */
	    p->date = t->date;
    }
    else
    {
	s = (struct postrec *) malloc(sizeof(struct postrec));
	memcpy(s, t, sizeof(struct posttop));
	s->next = NULL;
	if (q == NULL)
	    bucket[i] = s;
	else
	    q->next = s;
    }
}


int
sort(pp, count)
    struct postrec *pp;
{
    int i, j;

    for (i = 0; i <= count; i++)
    {
	if (pp->number > top[i].number)
	{
	    if (count < TOPCOUNT - 1)
		count++;
	    for (j = count - 1; j >= i; j--)
		memcpy(&top[j + 1], &top[j], sizeof(struct posttop));

	    memcpy(&top[i], pp, sizeof(struct posttop));
	    break;
	}
    }
    return count;
}


void
load_stat(fname)
    char *fname;
{
    FILE *fp;

    if((fp = fopen(fname, "r")))
    {
	int count = fread(top, sizeof(struct posttop), TOPCOUNT, fp);
	fclose(fp);
	while (count)
	    search(&top[--count]);
    }
}


int
filter(board)
    char *board;
{
    boardheader_t bh;
    int bid;

    /* XXX: bid of cache.c's getbnum starts from 1 */
    bid = getbnum(board);
    if (get_record(".BRD", &bh, sizeof(bh), bid) == -1)
	return 1;
    if (bh.brdattr & BRD_NOCOUNT)
	return 1;

    /* 板主設定不列入記錄 */
    if (bh.brdattr & BRD_HIDE && !(bh.brdattr & BRD_BMCOUNT))
	return 1;
/*
  if (bh.brdattr & BRD_POSTMASK)
  return 0;
  return (bh.brdattr & ~BRD_POSTMASK) ||
  >= 32;
*/
    return 0;
}


void
poststat(mytype)
    int mytype;
{
    static char *logfile = ".post";
    static char *oldfile = ".post.old";

    FILE *fp;
    char buf[40], curfile[40] = "etc/day.0", *p;
    struct postrec *pp;
    int i, j;

    if (mytype < 0)
    {
	/* --------------------------------------- */
	/* load .post and statictic processing     */
	/* --------------------------------------- */

	remove(oldfile);
	Rename(logfile, oldfile);
	if ((fp = fopen(oldfile, "r")) == NULL)
	    return;
	mytype = 0;
	load_stat(curfile);

	while (fread(top, sizeof(struct posttop), 1, fp))
	    search(top);
	fclose(fp);
    }
    else
    {
	/* ---------------------------------------------- */
	/* load previous results and statictic processing */
	/* ---------------------------------------------- */

	i = mycount[mytype];
	p = myfile[mytype];
	while (i)
	{
	    sprintf(buf, "etc/%s.%d", p, i);
	    sprintf(curfile, "etc/%s.%d", p, --i);
	    load_stat(curfile);
	    Rename(curfile, buf);
	}
	mytype++;
    }

/* ---------------------------------------------- */
/* sort top 100 issue and save results            */
/* ---------------------------------------------- */

    memset(top, 0, sizeof(top));
    for (i = j = 0; i < HASHSIZE; i++)
    {
	for (pp = bucket[i]; pp; pp = pp->next)
	{
#ifdef  DEBUG
	    printf("Title : %s, Board: %s\nPostNo : %d, Author: %s\n"
		   ,pp->title
		   ,pp->board
		   ,pp->number
		   ,pp->author);
#endif

	    j = sort(pp, j);
	}
    }

    p = myfile[mytype];
    sprintf(curfile, "etc/%s.0", p);
    if((fp = fopen(curfile, "w")))
    {
	fwrite(top, sizeof(struct posttop), j, fp);
	fclose(fp);
    }

    sprintf(curfile, "etc/%s", p);
    if((fp = fopen(curfile, "w")))
    {
	int max, cnt;

	fprintf(fp, "\t\t[1;34m-----[37m=====[41m 本%s大熱門話題 [40m=====[34m-----[0m\n\n", mytitle[mytype]);

	max = mytop[mytype];
	p = buf + 4;
	for (i = cnt = 0; (cnt < max) && (i < j); i++)
	{
	    tp = &top[i];
	    if (filter(tp->board))
		continue;

	    strcpy(buf, ctime4(&(tp->date)));
	    buf[20] = 0;
	    fprintf(fp,
		    "[1;31m%3d. [33m看板 : [32m%-16s[35m《 %s》[36m%4d 篇[33m%16s\n"
		    "     [33m標題 : [0;44;37m%-60.60s[40m\n"
		    ,++cnt, tp->board, p, tp->number, tp->author, tp->title);
	}
	fclose(fp);
    }

/* free statistics */

    for (i = 0; i < HASHSIZE; i++)
    {
	struct postrec *pp0;

	pp = bucket[i];
	while (pp)
	{
	    pp0 = pp;
	    pp = pp->next;
	    free(pp0);
	}

	bucket[i] = NULL;
    }
}


int main(argc, argv)
    char *argv[];
{
    time4_t now;
    struct tm *ptime;

    attach_SHM();
    if (argc < 2)
    {
	printf("Usage:\t%s bbshome [day]\n", argv[0]);
	return (-1);
    }
    chdir(argv[1]);

    if (argc == 3)
    {
	poststat(atoi(argv[2]));
	return (0);
    }
    now = (time4_t)time(NULL);
    ptime = localtime4(&now);
    if (ptime->tm_hour == 0)
    {
	if (ptime->tm_mday == 1)
	    poststat(2);

	if (ptime->tm_wday == 0)
	    poststat(1);
	poststat(0);
    }
    poststat(-1);
    return 0;
}
