/*
 *				popular.c
 *
 * Rough popularity estimator for rooms.
 */

#include "ctdl.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <time.h>
#include <proto/exec.h>
#include <dos/dos.h>
#include <pragmas/dos_pragmas.h>
#include "exec/memory.h"
#include "exec/ports.h"
#include "exec/exec.h"

/*
 *				history
 *
 * 90Apr27 HAW  Version 2.3 - sorted output.
 * 89Oct13 HAW  Version 2.2.
 * 86May12 HAW  Version 2.1.
 * 86Apr?? HAW  Version 2.0.
 * 86Apr07 HAW  Version 1.1.
 * 86Apr01 HAW  Created.
 */

/*
 *				contents
 *
 *	crashout()		irrecoverable error
 *	main()			Main controller for this program
 */
int  mPrintf(char *format, ...) {return 0; }  /* stub to quiet the linker */

#define LINE \
"------------------------------------------------------------------------------"

struct counts {
    int  forgot, num;
    int  messages;
    long bytes;
} *countTab;

int    messFlag = FALSE;
extern MessageBuffer  msgBuf; /* The -sole- message buffer	*/
extern FILE *msgfl, *msgfl2;
extern FILE *upfd;  /* file descriptor for the msg file	*/
int PublicRoomCount = 0, ForgetCount = 0;
int activeLogs = 0, activeForgots = 0;

extern rTable *roomTab;	/* RAM index of rooms	*/
extern aRoom  roomBuf;	/* room buffer	*/
extern FILE	*roomfl;	/* file descriptor for rooms    */
extern int	thisRoom;	/* room currently in roomBuf    */
extern logBuffer logBuf;	/* Log buffer of a person	*/
extern int	thisLog;	/* entry currently in logBuf    */
extern FILE	*logfl;	/* log file descriptor	*/
extern CONFIG    cfg;	/* A buncha variables	*/

void getCUdate(int *year, char **month, int *day, int *hours, int *minutes);
void flags(int argc, char *argv[]);
int mulchAcct(void);
void doMessages(void);
void display(void);
void handle(void);

/*
 * crashout()
 *
 * This handles the irrecoverable error.
 */
void crashout(str)
char *str;
{
    exit(printf(str));
}

/*
 * display()
 *
 * This function displays the results.
 */
void display()
{
    int rover;
    void ShowIt();
    int year, day, hours, minutes;
    char *month;
    int numcmp();
    static SListBase Sorted = { NULL, NULL, numcmp, NoFree, NULL };


    for (rover = 0;  rover < MAXROOMS;  rover++)
	if (roomTab[rover].rtflags.INUSE != 0 &&
	    roomTab[rover].rtflags.PUBLIC != 0) {
	    AddData(&Sorted, countTab + rover, NULL, FALSE);
	    countTab[rover].num = rover;
	}

    getCUdate(&year, &month, &day, &hours, &minutes);
    printf("%d%s%02d @ %d:%02d\n\n", year, month, day, hours, minutes);

    printf(
"Out of a log of %d entries, %d are in use; %d (%d%%) have used <Z>Forget.\n\n",
 cfg.MAXLOGTAB, activeLogs, activeForgots, (100* activeForgots) / activeLogs);

    printf("%48s%15s\n", "Total", "Forgotten");
    printf("%-25s%-15s%-13s%s", "Room name", "# Forgotten", "Percentage",
							"Percentage");
    if (messFlag) printf("%10s", "Messages");
    printf("\n%s\n", LINE);

    RunList(&Sorted, ShowIt);

    ForgetCount /= PublicRoomCount;
    printf("%s\n\n", LINE);
    printf("There are %d public rooms, ", PublicRoomCount);
    printf("an average %d forgetting them (%d%% and %d%%)\n\n",
							ForgetCount,
    (activeLogs != 0) ? (100 * ForgetCount) / activeLogs : 0,
    (activeForgots != 0) ? (100 * ForgetCount) / activeForgots : 0);
}

/*
 * numcmp()
 *
 * This function will compare two counts and return who's higher.  This is
 * used in list sorting.
 */
int numcmp(struct counts *s, struct counts *d)
{
    return d->forgot - s->forgot;
}

/*
 * ShowIt()
 *
 * This will show a room with forgotten stats.  This is used in list handling.
 */
void ShowIt(struct counts *s)
{
    PublicRoomCount++;
    ForgetCount += s->forgot;
    printf("%-30s%-15d%-9d%3d", roomTab[s->num].rtname,
							s->forgot,
   (activeLogs != 0) ? (100 * s->forgot) / activeLogs : 0,
   (activeForgots != 0) ? (100 * s->forgot) / activeForgots : 0);
    if (messFlag) printf("%11d (%ld)", s->messages,
		(s->messages == 0) ? 0 : s->bytes / (long) s->messages);
    printf("\n");
}

/*
 * doMessages()
 *
 * This function loops thru the msg file until finished.  It accumulates
 * statistics, etc.
 */
void doMessages()
{
    MSG_NUMBER msg, firstMessage;
    MSG_NUMBER total;	/* For stat keeping. */
    extern struct mBuf mFile1;

    fprintf(stderr, "Mulching...\n");
    InitMsgBase();
    startAt(msgfl, &mFile1, 0, 0);
    getMessage(getMsgChar, FALSE, TRUE, TRUE);
    msg = atol(msgBuf.mbId);
    fprintf(stderr, "%ld\n", msg);
    firstMessage = msg;
    handle();
    getMessage(getMsgChar, FALSE, TRUE, TRUE);
    msg = atol(msgBuf.mbId);
    total = 1;
    while (msg != firstMessage) {
	total++;
	fprintf(stderr, "%ld\r", msg);
	handle();
	getMessage(getMsgChar, FALSE, TRUE, TRUE);
	msg = atol(msgBuf.mbId);
    }
}

/*
 * flags()
 *
 * This function analyzes the command line arguments.  Very primitive.
 */
void flags(argc, argv)
int argc;
char *argv[];
{
    int i;

    for (i = 0; i < MAXROOMS; i++) {
	countTab[i].forgot = 0;
	countTab[i].messages = 0;
    }

    while (argc != 1) {
	argc--;
	if (strCmpU("-M", argv[argc]) == 0)
	    messFlag = TRUE;
    }
}

/*
 * getCUdate()
 *
 * This function retrieves the system date and returns in the parameters.
 */
void getCUdate(year, month, day, hours, minutes)
int *year, *day, *hours, *minutes;
char **month;
{
    int m;
    static char *monthTab[13] = {"", "Jan", "Feb", "Mar",
				"Apr", "May", "Jun",
				"Jul", "Aug", "Sep",
				"Oct", "Nov", "Dec" };

    getUtilDate(year, &m, day, hours, minutes);
    *month = monthTab[m];
    *year -= 1900;
}

/*
 * handle()
 *
 * This function searches the room base for the current msg's room, increments
 * the appropriate counters when found.
 */
void handle()
{
    int rover;
    int MsgLen(void);

    for (rover = 0; rover < MAXROOMS; rover++)
	if (strCmpU(roomTab[rover].rtname, msgBuf.mbroom) == 0) {
	    countTab[rover].messages++;
	    countTab[rover].bytes += MsgLen();
	    break;
	}
}

/*
 * MsgLen()
 *
 * This function figures out the byte usage of the message.
 */
int MsgLen()
{
    return  (int) (	strLen(msgBuf.mbtext)  + strLen(msgBuf.mbauth) +
                    strLen(msgBuf.mbdate)  + strLen(msgBuf.mbtime) +
                    strLen(msgBuf.mbId)    + strLen(msgBuf.mboname) +
                    strLen(msgBuf.mborig)  + strLen(msgBuf.mbroom) +
                    strLen(msgBuf.mbsrcId) + strLen(msgBuf.mbto) +
                    strLen(msgBuf.mbaddr)  + strLen(msgBuf.mbOther) );
}

/*
 * main()
 *
 * This is the main controller.
 */
int main(int, char **);
int main(argc, argv)
int argc;
char *argv[];
{
    SYS_FILE temp;
    int Index;

    cfg.weAre = UTILITY;
    printf("\nRoom Popularity Estimator (Version 3.41)\n%s\n", COPYRIGHT);
    fprintf(stderr, "Munching...\n");
    printf("\n");
    if (readSysTab(FALSE, TRUE)) {
	initRoomBuf(&roomBuf);
	countTab = (struct counts *) GetDynamic(MAXROOMS * sizeof *countTab);
	flags(argc, argv);
	mvToHomeDisk(&cfg.homeArea);
	makeSysName(temp, "ctdlroom.sys", &cfg.roomArea);
	openFile(temp, &roomfl);
	makeSysName(temp, "ctdllog.sys", &cfg.logArea);
	openFile(temp, &logfl);
	initLogBuf(&logBuf);

	for (Index = 0; Index < cfg.MAXLOGTAB; Index++) {
	    getLog(&logBuf, Index);
	    if (logBuf.lbflags.L_INUSE) {
		activeLogs++;
		activeForgots += mulchAcct();
	    }
	    fprintf(stderr, "%d\r", Index);
	}

	if (messFlag)
	    doMessages();

	display();
    }
    return 0;
}

/*
 * mulchAcct()
 *
 * This goes through all the rooms, checking them against the current acct
 * for statistics gathering.
 */
int mulchAcct()
{
    int i, j, g, usedForget;

    usedForget = 0;
    for (i = 0;  i < MAXROOMS;  i++) {
	if (roomTab[i].rtflags.PUBLIC != 0) {
	    if ((logBuf.lbgen[i] >> GENSHIFT) != roomTab[i].rtgen)  {
		j = roomTab[i].rtgen - (logBuf.lbgen[i] >> GENSHIFT);
		if (j < 0)
		    g = -j;
		else
		    g = j;
		if (g == FORGET_OFFSET) {
		    usedForget = 1;
		    countTab[i].forgot++;
		}
	    }
	}
    }
    return usedForget;
}
