#include <stdio.h>
#include <sys/types.h>
#include <ctype.h>
#include "network.h"
#include "network2.h"

#define INPUT_LEN   256
#define DATE_LENGTH 15
#define FILE_SIZE   2024 
#define ERROR       -1

typedef struct summary {
    char data[INPUT_LEN];
    int  total;
    int  clients;
    int  mac;
    int  unix_c;
    int  dos;
    int  xwin;
    int  cms;
    int  dialup;
    int  foreign;
    int  telnet;
    int  goph;
    int  www;
    int  t_sendfile;
    int  t_getfile;
    int  meta;
    int  ftelnet;
    int  ltelnet;
    int  ftsearch;
    int  ksearch;
    int  wnew;
} SUMMARY;

int
    get_node(SUMMARY *summary_ptr, char *date, int *slot_no);
compute_record(char *msg,SUMMARY *summary_ptr);
print_report(SUMMARY *summary, int size);

main (int argc, char *argv[])
{
    
    FILE    *datafile = stdin;
    char    *more;
    char     msg[INPUT_LEN];
    SUMMARY  summary[FILE_SIZE];
    int      ctr = -1;
    int      rc;
    char     increment;
    
    /* which type of report are we running */
    if ((argv[1] != NULL && !strncmp ("-h", argv[1], 2)))
	dousage(argv[0]); /* never returns */
    
    if (argc < 2)
	increment = 'W';    /* Default interval size */
    else 
	increment = islower(*(argv[1])) ? toupper(*(argv[1])) : (*(argv[1]));
    if (!index("WMQ", increment)) {
	fprintf (stderr, "Increment must be one of WMQ\n");
	exit (-21);
    }
    
    more = fgets(msg,INPUT_LEN,datafile);
    
    /* handle each record in input file */
    while(more) {
	
	/* which slot does this record get put */
	rc = get_node(summary, msg, &ctr); 
	if (rc == ERROR) {
	    /* error condition, let's get out of here */
	    fflush(stdout);
	    exit(1);
	}
	
	compute_record(msg,(SUMMARY *)&summary[rc]);
	
	/* get next record */
	more = fgets(msg,INPUT_LEN,datafile);
    }
    
    switch(increment) {
      case 'W':
	print_weekly_report(summary,ctr);
	break;
      case 'M':
	print_monthly_report(summary,ctr);
	break;
      case 'Q':
	print_quarterly_report(summary,ctr);
	break;
    }
    
}

/* find the array position to do the computations */
int get_node(SUMMARY *summary_ptr, char *date, int *end) {
    
    int i;
    
    /* search list for existing record */
    for (i=0; i <= *end; i++)
	if (!strncmp(summary_ptr[i].data,date,DATE_LENGTH))
	    return(i);
    
    (*end)++;
    
    /* check array size */
    if (*end >= FILE_SIZE) {
	printf("line %d: Storage Exceeded\n",*end);
	return(ERROR);
    }
    else {
	/* add record to the list */
	bzero(&(summary_ptr[*end]),sizeof(summary_ptr[0])); 
	strncpy(summary_ptr[*end].data,date,DATE_LENGTH);
	*(summary_ptr[*end].data + DATE_LENGTH) = NULL;
    }
    
    return(*end);
}

/* process record */
compute_record(char *msg,SUMMARY *summary_ptr)
{
    char    *sp;
    char     field_label;
    int      field_value;
    
    /* parse each record, which ends in a LF or NULL */
    for(sp=msg + DATE_LENGTH; *sp != '\n' && *sp != NULL; sp++) {
	
	/* find field label */
	if (!isspace(*sp) && !isdigit(*sp)) {
	    field_label = *sp;
	    field_value = atoi(++sp);
	    switch(field_label) {
		
	      case CONNECTION:
		summary_ptr -> total += field_value;
		break;
		
	      case MAC:
		summary_ptr -> mac = summary_ptr -> mac + field_value;
		break;
		
	      case UNIX:
		summary_ptr -> unix_c = summary_ptr -> unix_c + field_value;
		break;
		
	      case DOS:
	      case MWL:
	      case WIN:
		summary_ptr -> dos = summary_ptr -> dos + field_value;
		break;
		
	      case XWIN:
		summary_ptr -> xwin = summary_ptr -> xwin + field_value;
		break;
		
	      case CMS:
		summary_ptr -> cms = summary_ptr -> cms + field_value;
		break;
		
	      case DIALUP:
		summary_ptr -> dialup = summary_ptr -> dialup + field_value;
		summary_ptr -> unix_c = summary_ptr -> unix_c - field_value;
		break;
		
	      case FOREIGN:
		summary_ptr -> foreign = summary_ptr -> foreign + field_value;
		break;
		
	      case FTELNET:
		summary_ptr -> ftelnet = summary_ptr -> ftelnet + field_value;
		summary_ptr -> telnet = summary_ptr -> telnet + field_value;
		summary_ptr -> unix_c = summary_ptr -> unix_c - field_value;
		break;
		
	      case LTELNET:
		summary_ptr -> ltelnet = summary_ptr -> ltelnet + field_value;
		summary_ptr -> telnet = summary_ptr -> telnet + field_value;
		summary_ptr -> unix_c = summary_ptr -> unix_c - field_value;
		break;
		
	      case GOPH:
		summary_ptr -> goph += field_value;
		break;
		
	      case WWW:
		summary_ptr -> www += field_value;
		break;
		
	      case T_SENDFILE:
		summary_ptr -> t_sendfile = summary_ptr -> t_sendfile + field_value;
		break;
		
	      case T_GETFILE:
		summary_ptr -> t_getfile = summary_ptr -> t_getfile + field_value;
		break;
		
	      case T_ADDLINK:
	      case T_REPLACENODE:
	      case T_RMLINK:
		summary_ptr -> meta = summary_ptr -> meta + field_value;
		break;
	      case T_FULL_TXT_SEARCH:
		summary_ptr -> ftsearch += field_value;
		break;
	      case T_FIND:
		summary_ptr -> ksearch += field_value;
		break;
	      case  T_CHGD_SINCE:
		summary_ptr -> wnew += field_value;
		break;
	    }
	}
    }
    summary_ptr -> clients = (
			      summary_ptr->mac +
			      summary_ptr->unix_c +
			      summary_ptr->dos +
			      summary_ptr->xwin +
			      summary_ptr->cms +
			      summary_ptr->dialup +
			      summary_ptr->ftelnet +
			      summary_ptr->ltelnet 
			      );
}

print_weekly_report(SUMMARY *summary, int ctr)
{
    int i,x;
    SUMMARY  total;
    
    /* print control information */
    for (i=ctr,x=1; i >= 0 && x <= 7; i--, x++) {
    }
    printf("%s -> %s\n",summary[++i].data, summary[ctr].data);
    
    print_conn_heading();
    
    /* print report */
    bzero(&total,sizeof(total));
    for (i=ctr,x=1; i >= 0 && x <= 7; i--, x++) {
	printf("%s   %4d  %4d  %3d  %3d  %3d   %3d  %3d   %3d   %3d    %3d\n",
	       summary[i].data,
	       summary[i].total,
	       summary[i].clients,
	       summary[i].unix_c,
	       summary[i].mac,
	       summary[i].dos,
	       summary[i].xwin,
	       summary[i].cms,
	       summary[i].dialup,
	       summary[i].ltelnet,
	       summary[i].ftelnet
	       );
	
	/* keep a running total */
	total.total = total.total + summary[i].total;
	total.clients+=summary[i].clients;
	total.unix_c = total.unix_c + summary[i].unix_c;
	total.mac = total.mac + summary[i].mac;
	total.dos = total.dos + summary[i].dos;
	total.xwin = total.xwin + summary[i].xwin;
	total.cms = total.cms + summary[i].cms;
	total.dialup = total.dialup + summary[i].dialup;
	total.ltelnet = total.ltelnet + summary[i].ltelnet;
	total.ftelnet = total.ftelnet + summary[i].ftelnet;
    }
    
    /* print totals */
    printf("\n          Total   %4d  %4d %4d %4d %4d  %4d %4d  %4d  %4d   %4d\n",
	   total.total,
	   total.clients,
	   total.unix_c,
	   total.mac,
	   total.dos,
	   total.xwin,
	   total.cms,
	   total.dialup,
	   total.ltelnet,
	   total.ftelnet
	   );
    
    x--;
    if (x != 0) { /* avoid the core dumps*/
	printf("        Average   %4d  %4d %4d %4d %4d  %4d %4d  %4d  %4d   %4d\n",
	       total.total / x,
	       total.clients / x,
	       total.unix_c / x,
	       total.mac / x,
	       total.dos / x,
	       total.xwin / x,
	       total.cms / x,
	       total.dialup / x,
	       total.ltelnet / x,
	       total.ftelnet / x
	       );
    }
    
    /* print report */
    print_activity_heading();
    for (i=ctr,x=1; i >= 0 && x <= 7; i--, x++) {
	printf("%s   %4d  %3d  %3d   %3d  %3d  %3d         %3d   %3d   %4d\n",
	       summary[i].data,
	       summary[i].t_sendfile,
	       summary[i].t_getfile,
	       summary[i].meta,
	       summary[i].ftsearch,
	       summary[i].ksearch,
	       summary[i].wnew,
	       summary[i].goph,
	       summary[i].www,
	       summary[i].foreign
	       );
	
	/* keep a running total */
	total.t_sendfile = total.t_sendfile + summary[i].t_sendfile;
	total.t_getfile = total.t_getfile + summary[i].t_getfile;
	total.meta = total.meta + summary[i].meta;
	total.ftsearch += summary[i].ftsearch;
	total.ksearch += summary[i].ksearch;
	total.wnew += summary[i].wnew;
	total.goph += summary[i].goph;
	total.www += summary[i].www;
	total.foreign += summary[i].foreign;
    }
    
    printf("\n          Total  %5d  %3d  %3d   %3d  %3d  %3d        %4d  %4d  %5d\n",
	   total.t_sendfile,
	   total.t_getfile,
	   total.meta,
	   total.ftsearch,
	   total.ksearch,
	   total.wnew,
	   total.goph,
	   total.www,
	   total.foreign
	   );
    x--;
    if (x != 0) { /*avoid core dumps I was getting*/
	printf("        Average   %4d  %3d  %3d   %3d  %3d  %3d        %4d  %4d  %5d\n",
	       total.t_sendfile / x,
	       total.t_getfile / x,
	       total.meta / x,
	       total.ftsearch / x,
	       total.ksearch / x,
	       total.wnew / x,
	       total.goph / x,
	       total.www / x,
	       total.foreign / x
	       );
    }
}

dousage(char *pgm)
{
    fprintf (stderr,
	     "Usage: %s [<increment>] \n\n\
Increment must be one of W,M (default=W)\n\
(Week, Month)\n",pgm);

exit (-1);
}

print_monthly_report(SUMMARY *summary, int ctr)
{
    int      i;
    char     curr_month[4];
    char     curr_year[5];
    SUMMARY  total;
    char     label[12];
    
    printf("%s -> %s\n",summary[0].data, summary[ctr].data);
    
    /* print the connection stuff */
    print_conn_heading();
    
    for (i=ctr; i >= 0; i--) {
	
	if (i == ctr || strncmp(curr_month,summary[i].data + 4,3)) {
	    /* are we past the first record */
	    if (i != ctr) {
		/* print totals */
		sprintf(label,"%s %s",curr_month,curr_year);
		print_conn_record(label,&total);
	    }
	    
	    /* find a new current month */
	    strncpy(curr_month,summary[i].data + 4,3);
	    curr_month[3] = NULL;
	    strncpy(curr_year,summary[i].data + 11,4);
	    curr_year[4] = NULL;
	    bzero(&total,sizeof(total));
	}
	
	/* keep a running total */
	add_conn_record(&total,(SUMMARY *)&summary[i]);
	
    }  /* end for loop */
    
    /* print last month r*/
    sprintf(label,"%s %s",curr_month,curr_year);
    print_conn_record(label,&total);
    
    /* print the activity stuff */
    print_activity_heading();
    
    for (i=ctr; i >= 0; i--) {
	
	if (i == ctr || strncmp(curr_month,summary[i].data + 4,3)) {
	    /* are we past the first record */
	    if (i != ctr) {
		/* print totals */
		sprintf(label,"%s %s",curr_month,curr_year);
		print_activity_record(label,&total);
	    }
	    
	    /* find a new current month */
	    strncpy(curr_month,summary[i].data + 4,3);
	    curr_month[3] = NULL;
	    strncpy(curr_year,summary[i].data + 11,4);
	    curr_year[4] = NULL;
	    bzero(&total,sizeof(total));
	}
	
	/* keep a running total */
	add_active_record(&total,(SUMMARY *)&summary[i]);
	
    }  /* end for loop */
    
    /* print last month */
    sprintf(label,"%s %s",curr_month,curr_year);
    print_activity_record(label,&total);
    
}

/* a quarter is defined as the last three months */
print_quarterly_report(SUMMARY *summary, int ctr)
{
    int      i;
    char     curr_month[4];
    char     curr_year[5];
    SUMMARY  total;
    SUMMARY  quarter;
    char     label[12];
    short    qtr = 0;
    short    next_quarter = 1;
    
    /* print the start and end date range */
    strncpy(curr_month,summary[ctr].data + 4,3);
    curr_month[3] = NULL;
    for (i=ctr; i >= 0 && qtr <= 2; i--) {
	if ( strncmp(curr_month,summary[i].data + 4,3)) {
	    strncpy(curr_month,summary[i].data + 4,3);
	    curr_month[3] = NULL;
	    qtr++;
	}
    }
    i = i + 2;
    printf("%s -> %s\n",summary[i].data, summary[ctr].data);
    
    /* print the connection stuff */
    bzero(&quarter,sizeof(quarter));
    print_conn_heading();
    next_quarter = 1;
    qtr = 0;
    
    for (i=ctr; i >= 0 && next_quarter ; i--) {
	
	if (i == ctr || strncmp(curr_month,summary[i].data + 4,3)) {
	    /* are we past the first record */
	    if (i != ctr) {
		/* print totals */
		sprintf(label,"%s %s",curr_month,curr_year);
		print_conn_record(label,&total);
		
		qtr++;
		/* is this the start of a new quarter */
		if (qtr == 3) {
		    print_conn_record("total",&quarter);
		    printf("\n"); /* print a null line */
		    qtr = 0;
		    next_quarter = 0;  /* let's stop after one quarter */
		    bzero(&quarter,sizeof(quarter));
		}
	    }
	    
	    /* find a new current month */
	    strncpy(curr_month,summary[i].data + 4,3);
	    curr_month[3] = NULL;
	    strncpy(curr_year,summary[i].data + 11,4);
	    curr_year[4] = NULL;
	    bzero(&total,sizeof(total));
	}
	
	/* keep a running total */
	add_conn_record(&total,(SUMMARY *)&summary[i]);
	add_conn_record(&quarter,(SUMMARY *)&summary[i]);
	
    }  /* end for loop */
    
    /* print last month and quarter*/
    if (next_quarter != 0) {
	sprintf(label,"%s %s",curr_month,curr_year);
	print_conn_record(label,&total);
	print_conn_record("total",&quarter);
    }
    
    /* print the activity stuff */
    qtr = 0;
    next_quarter = 1;
    bzero(&quarter,sizeof(quarter));
    print_activity_heading();
    
    for (i=ctr; i >= 0 && next_quarter ; i--) {
	
	if (i == ctr || strncmp(curr_month,summary[i].data + 4,3)) {
	    /* are we past the first record */
	    if (i != ctr) {
		/* print totals */
		sprintf(label,"%s %s",curr_month,curr_year);
		print_activity_record(label,&total);
		
		qtr++;
		/* is this the start of a new quarter */
		if (qtr == 3) {
		    print_activity_record("total",&quarter);
		    printf("\n"); /* print a null line */
		    qtr = 0;
		    next_quarter = 0;  /* let's stop after one quarter */
		    bzero(&quarter,sizeof(quarter));
		}
	    }
	    
	    /* find a new current month */
	    strncpy(curr_month,summary[i].data + 4,3);
	    curr_month[3] = NULL;
	    strncpy(curr_year,summary[i].data + 11,4);
	    curr_year[4] = NULL;
	    bzero(&total,sizeof(total));
	}
	
	/* keep a running total */
	add_active_record(&total,(SUMMARY *)&summary[i]);
	add_active_record(&quarter,(SUMMARY *)&summary[i]);
	
    }  /* end for loop */
    
    /* print last month and quarter*/
    if (next_quarter != 0) {
	sprintf(label,"%s %s",curr_month,curr_year);
	print_activity_record(label,&total);
	print_activity_record("total",&quarter);
    }
    
}

/* add summary info to the total record for connections */
add_active_record(SUMMARY *total,SUMMARY *summary)
{
    total -> t_sendfile = total -> t_sendfile + summary -> t_sendfile;
    total -> t_getfile = total -> t_getfile + summary -> t_getfile;
    total -> meta = total -> meta + summary -> meta;
    total -> ftsearch += summary -> ftsearch;
    total -> ksearch += summary -> ksearch;
    total -> wnew += summary -> wnew;
    total -> foreign = total -> foreign + summary -> foreign;
    total -> telnet = total -> telnet + summary -> telnet;
    total -> goph = total -> goph + summary -> goph;
    total -> www = total -> www + summary -> www;
}

/* add summary info to the total record for activity */
add_conn_record(SUMMARY *total,SUMMARY *summary)
{
    total -> total = total -> total + summary -> total;
    total -> clients += summary->clients;
    total -> unix_c = total -> unix_c + summary -> unix_c; 
    total -> mac = total -> mac + summary -> mac;
    total -> dos = total -> dos + summary -> dos;
    total -> xwin = total -> xwin + summary -> xwin;
    total -> cms = total -> cms + summary -> cms;
    total -> dialup = total -> dialup + summary -> dialup;
    total -> ltelnet = total -> ltelnet + summary -> ltelnet;
    total -> ftelnet = total -> ftelnet + summary -> ftelnet;
}

print_conn_heading()
{
    /* print heading */
    printf("\nTechInfo Statistics\n\n");
    printf("LEGEND\n\n");
    printf("CMS   = CMS client connections\n");
    printf("Clnts = Total connectins from clients listed in CONNECTIONS group\n");
    printf("        More meaningful than Total which is inflated by gateways\n");
    printf("Dial  = Dial-up connections\n");
    printf("DOS   = DOS client connections, including Mewel and Windows\n");
    printf("Foth  = Foreign (outside MIT domain) clients (e.g. Mac, Unix, ...)\n");
    printf("Ftel  = Foreign (outside MIT domain) telnet connections\n");
    printf("FullS = Full text search transactions\n");
    printf("Goph  = Gopher to Techinfo gateway transaction\n");
    printf("KeyS  = Keyword search transactions\n");
    printf("Ltel  = Telnet connections from the MIT domain\n");
    printf("Mac   = Macintosh client connections\n");
    printf("Meta  = Additions, modifications or deletions of menu items\n");
    printf("New   = Transactions requesting ``What's New''\n");
    printf("Post  = Documents sent to TechInfo\n");
    printf("        Note, documents can be published from outside techinfo\n");
    printf("Read  = Documents requested from TechInfo\n");
    printf("Total = Total connections to the server\n");
    printf("        This is inflated by gateways where each transaction is a connection\n");
    printf("        See Clnts for a more meaningful measure of total connections\n");
    printf("Unix  = Unix client connections (Athena)\n");
    printf("XWIN  = X Windows client connections\n");
    printf("WWW   = Transactions from World Wide Web to Techinfo gateway\n");
    printf("\n")
	;
    printf("CONNECTIONS\n");
    printf("Date             Total Clnts Unix  Mac  DOS  XWIN  CMS  Dial  Ltel   Ftel\n\n");
    
}

print_activity_heading()
{
    /* print report */
    printf("\n");
    printf("ACTIVITY\n");
    printf("Date              Read Post Meta FullS KeyS  New        Goph   WWW   Foth\n\n");
}

print_conn_record(char *label,SUMMARY *record)
{
    
    /* print last month counts */
    printf("%15s  %5d %5d %4d %4d %4d  %4d %4d  %4d  %4d  %5d\n",
	   label,
	   record -> total,
	   record -> clients,
	   record -> unix_c,
	   record -> mac,
	   record -> dos,
	   record -> xwin,
	   record -> cms,
	   record -> dialup,
	   record -> ltelnet,
	   record -> ftelnet
	   );
}

print_activity_record(char *label,SUMMARY *record)
{ 
    printf("%15s %6d %4d %4d  %4d %4d %4d        %4d  %4d  %5d\n",
	   label,
	   record -> t_sendfile,
	   record -> t_getfile,
	   record -> meta,
	   record -> ftsearch,
	   record -> ksearch,
	   record -> wnew,
	   record -> goph,
	   record -> www,
	   record -> foreign
	   );
}

