#include <stdio.h>
#include <ntt.h>
#include <niterror.h>
#include <nwbindry.h>
#include <nwmisc.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "acct.h"

#define PROG_NAME		"ACCTRECS"
#define DEFAULT_DIR		"\\SYSTEM"
#define DEFAULT_ACCT_FILE	"\\NET$ACCT.DAT"
#define	MAX_BINDERY_OBJ_LEN	48
#define COMMENT_OFFSET          26

void Usage(void);
void ProcessRecord(ACCT_RECORD *recPtr);
void DisplayDateAndServerClientIDs(ACCT_RECORD *record);

void main(int argc, char **argv)
{
	char	acctFilePath[MAX_DIR_ENTRY] = "";
	char	*path;
	char	*userName;
	int	userSpecified = FALSE;
	long	userID;
	int	ccode;
	FILE	*acctFile;
	int	itemsRead;
	ACCT_RECORD	*record;

	while (argc-- > 1) {
		if ((argv[argc][0] == '/') && (argv[argc][1] == 'a')) {
			/* found /a */
			path = &argv[argc][2];
			strcpy(acctFilePath, path);

		} else if ((argv[argc][0] == '/') && (argv[argc][1] == 'u')) {
			/* found /u */
			userName = &argv[argc][2];
			ccode = GetBinderyObjectID(userName, OT_USER, &userID);
			if (ccode != SUCCESSFUL) {
				printf("Unknown user: %s\n", userName);
				exit(1);
			}
			userSpecified = TRUE;

		} else {
			/* unrecognized arg */
			Usage();
			exit(1);
		}
	}

	if (strlen(acctFilePath) == 0)
		sprintf(acctFilePath, "%s%s", DEFAULT_DIR, DEFAULT_ACCT_FILE);

	acctFile = fopen(acctFilePath, "rb");
	if (acctFile == NULL) {
		printf("Unable to open %s\n", acctFilePath);
		exit(1);
	}

	do {
		itemsRead = fread(record, sizeof(WORD), 1, acctFile);
		if (itemsRead == 1) {
			itemsRead = fread(&(record->serverID), IntSwap(record->length), 1, acctFile);
			if (itemsRead != 1) {
				printf("Unexpected end of file %s\n", acctFilePath);
				break;
			}
			if (userSpecified) {
				if (LongSwap(record->clientID) == userID)
					ProcessRecord(record);
			} else {
				ProcessRecord(record);
			}
		}
	} while(itemsRead);
	fclose(acctFile);
}

void Usage(void)
{
	printf("Usage:\n\n");
	printf("   %s [/aAcctFilePath] [/uUserName]\n\n", PROG_NAME);
	printf("   /a     Specify accounting data file path\n");
	printf("          Default accounting file is %s%s\n", DEFAULT_DIR, DEFAULT_ACCT_FILE);
	printf("   /u     Display only accounting records pertaining to UserName\n");
	printf("          Defaults to display data for all users\n\n");
}

void ProcessRecord(ACCT_RECORD *record)
{
	int	i;
	BYTE	*bytePtr;

	if (record->recordType == CHARGE_RECORD) {
		switch (IntSwap(record->chargeOrNote.charge.chargeType)) {
			case CONNECT_TIME_CHARGE_NOTE	:       DisplayDateAndServerClientIDs(record);
								printf("\tConnect time charge:\n");
								printf("\t\tCharge amount: %lu\n", LongSwap(record->chargeOrNote.charge.amount));
								printf("\t\tCompletion code: ");
								if (record->completionCode == SUCCESSFUL)
									printf("SUCCESSFUL (%X)\n", SUCCESSFUL);
								else if (record->completionCode == ACCT_CREDIT_LIMIT_EXCEEDED)
									printf("ACCT_CREDIT_LIMIT_EXCEEDED (%X)\n", ACCT_CREDIT_LIMIT_EXCEEDED);
								else if (record->completionCode == NO_ACCT_BALANCE)
									printf("NO_ACCT_BALANCE (%X)\n", NO_ACCT_BALANCE);
								else
									printf("%X\n", record->completionCode);
								printf("\t\tConnected %lu minutes\n",
									LongSwap(record->chargeOrNote.charge.chargeComment.connectTimeCharge.connectTime));
								printf("\t\tClient sent %lu request packets\n",
									LongSwap(record->chargeOrNote.charge.chargeComment.connectTimeCharge.requestCount));
								printf("\t\t%4.0X%4.0X%4Xh bytes read\n",
									record->chargeOrNote.charge.chargeComment.connectTimeCharge.bytesReadHi,
									record->chargeOrNote.charge.chargeComment.connectTimeCharge.bytesRead2,
									record->chargeOrNote.charge.chargeComment.connectTimeCharge.bytesReadLo);
								printf("\t\t%4.0X%4.0X%4Xh bytes written\n\n",
									record->chargeOrNote.charge.chargeComment.connectTimeCharge.bytesWrittenHi,
									record->chargeOrNote.charge.chargeComment.connectTimeCharge.bytesWritten2,
									record->chargeOrNote.charge.chargeComment.connectTimeCharge.bytesWrittenLo);
								break;

			case DISK_STORAGE_CHARGE_NOTE	:       DisplayDateAndServerClientIDs(record);
								printf("\tDisk storage charge:\n");
								printf("\t\tCharge amount: %lu\n", LongSwap(record->chargeOrNote.charge.amount));
								printf("\t\tCompletion code: ");
								if (record->completionCode == SUCCESSFUL)
									printf("SUCCESSFUL (%X)\n", SUCCESSFUL);
								else if (record->completionCode == ACCT_CREDIT_LIMIT_EXCEEDED)
									printf("ACCT_CREDIT_LIMIT_EXCEEDED (%X)\n", ACCT_CREDIT_LIMIT_EXCEEDED);
								else
									printf("%X\n", record->completionCode);
								printf("\t\tNumber of disk blocks owned: %ld\n",
									LongSwap(record->chargeOrNote.charge.chargeComment.diskStorageTimeCharge.blocksOwned));
								printf("\t\tFor %ld half hours.\n\n",
									LongSwap(record->chargeOrNote.charge.chargeComment.diskStorageTimeCharge.numberOfHalfHours));
								break;

			default	:                               DisplayDateAndServerClientIDs(record);
								printf("\tUnknown charge record:\n");
								printf("\t\tCharge amount: %lu\n", LongSwap(record->chargeOrNote.charge.amount));
								printf("\t\tCompletion code: ");
								if (record->completionCode == SUCCESSFUL)
									printf("SUCCESSFUL (%X)\n", SUCCESSFUL);
								else if (record->completionCode == ACCT_CREDIT_LIMIT_EXCEEDED)
									printf("ACCT_CREDIT_LIMIT_EXCEEDED (%X)\n", ACCT_CREDIT_LIMIT_EXCEEDED);
								else
									printf("%X\n", record->completionCode);
								printf("\t\tComment: ");
								bytePtr = &(record->chargeOrNote.charge.chargeComment.unknownCharge.comment);
								for (i=0; i<(record->length - COMMENT_OFFSET); i++)
									printf("%X", *bytePtr++);
								printf("\n\n");
								break;
		}

	} else if (record->recordType == NOTE_RECORD) {
		switch (IntSwap(record->chargeOrNote.note.noteType)) {
			case LOGIN_NOTE			:       DisplayDateAndServerClientIDs(record);
								printf("\tLogin note:\n");
								printf("\t\t Net: %lX\n",
									LongSwap(record->chargeOrNote.note.noteComment.loginNote.net));
								printf("\t\tNode: %lX%X\n\n",
									LongSwap(record->chargeOrNote.note.noteComment.loginNote.nodeHigh),
									IntSwap(record->chargeOrNote.note.noteComment.loginNote.nodeLow));
								break;

			case LOGOUT_NOTE		:       DisplayDateAndServerClientIDs(record);
								printf("\tLogout note:\n");
								printf("\t\t Net: %lX\n",
									LongSwap(record->chargeOrNote.note.noteComment.loginNote.net));
								printf("\t\tNode: %lX%X\n\n",
									LongSwap(record->chargeOrNote.note.noteComment.loginNote.nodeHigh),
									IntSwap(record->chargeOrNote.note.noteComment.loginNote.nodeLow));
								break;

			case ACCOUNT_LOCKED_NOTE	:       DisplayDateAndServerClientIDs(record);
								printf("\tAccount locked note:\n");
								printf("\t\t Net: %lX\n",
									LongSwap(record->chargeOrNote.note.noteComment.loginNote.net));
								printf("\t\tNode: %lX%X\n\n",
									LongSwap(record->chargeOrNote.note.noteComment.loginNote.nodeHigh),
									IntSwap(record->chargeOrNote.note.noteComment.loginNote.nodeLow));
								break;

			case SERVER_TIME_MODIFIED_NOTE	:       DisplayDateAndServerClientIDs(record);
								printf("\tServer time modified note:\n");
								printf("\t\tNew time: %02d:%02d:%02d %02d/%02d/%02d\n\n",
									record->chargeOrNote.note.noteComment.serverTimeNote.hour,
									record->chargeOrNote.note.noteComment.serverTimeNote.minute,
									record->chargeOrNote.note.noteComment.serverTimeNote.second,
									record->chargeOrNote.note.noteComment.serverTimeNote.month,
									record->chargeOrNote.note.noteComment.serverTimeNote.day,
									record->chargeOrNote.note.noteComment.serverTimeNote.year);
								break;

			default	:                               DisplayDateAndServerClientIDs(record);
								printf("\tUnknown note record:\n");
								printf("\t\tComment: ");
								bytePtr = &(record->chargeOrNote.note.noteComment.unknownNote.comment);
								for (i=0; i<(record->length - COMMENT_OFFSET); i++)
									printf("%X", *bytePtr++);
								printf("\n\n");
								break;
		}

	} else {
		DisplayDateAndServerClientIDs(record);
		printf("\tUnknown record type: %d\n\n", record->recordType);
	}
}

void DisplayDateAndServerClientIDs(ACCT_RECORD *record)
{
	int	ccode;
	char	objectName[MAX_BINDERY_OBJ_LEN];

	/* print date and time record was submitted */
	printf("%02d:%02d:%02d %02d/%02d/%02d\n", record->timeStamp[3], record->timeStamp[4],
						record->timeStamp[5], record->timeStamp[1],
						record->timeStamp[2], record->timeStamp[0]);

	/* print server name and ID */
	ccode = GetBinderyObjectName(LongSwap(record->serverID), objectName, (WORD *)NULL);
	if (ccode)
		strcpy(objectName, "(unknown server)");
	printf("\tServer name (ID): %s (%lX)\n", objectName, LongSwap(record->serverID));

	/* print client name and ID */
	ccode = GetBinderyObjectName(LongSwap(record->clientID), objectName, (WORD *)NULL);
	if (ccode)
		strcpy(objectName, "(unknown user)");
	printf("\tClient name (ID): %s (%lX)\n", objectName, LongSwap(record->clientID));
}