//
//	MiscLogFile.m -- a generic class to simplify keeping a log file
//		Written by Don Yacktman (c) 1993 by Don Yacktman.
//				Version 1.0.  All rights reserved.
//
//		This notice may not be removed from this source code.
//
//		This is a free object!  Contact the author for the latest version.
//		Don Yacktman, 4279 N. Ivy Lane, Provo, UT, 84604
//		e-mail:  Don_Yacktman@byu.edu
//
//	This object is included in the MiscKit by permission from the author
//	and its use is governed by the MiscKit license, found in the file
//	"LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
//	for a list of all applicable permissions and restrictions.
//	

#import <misckit/misckit.h>
#import <stdio.h>

@interface MiscLogFile(private)

- _newLine;	// forces the log to start without any history.
- _printRepeats; // "Last line repeated x times."

@end

@implementation MiscLogFile

- init
{
	id ret = [super init];
	fileOpen = NO; special = NO;
	return ret;
}

- fileName { return fileName; }
- setFileName:aString
{
	if (fileOpen) [self closeFile];
	[lockFile free];
	lockFile = nil;
	special = NO;
	fileName = aString;
	return self;
}

- setLockFile:newLockFile
{
	[lockFile free];
	lockFile = newLockFile;
	return self;
}

- (BOOL)addLineToLogFile:aString
{
	if (![self openFile]) return NO;
	if (![self appendToLogFile:aString andFlush:NO]) return NO;
	if (![self closeFile]) return NO;
	return YES;
}

- _newLine	// forces the log to start without any history.
{
	repeats = 0;
	if (lastLine) {
		[lastLine free]; lastLine = nil;
	}
	return self;
}

- (BOOL)openFile
{
	if (special) return YES;	// stdin and stdout are open already
	if (fileOpen) return YES;	// file is already open
	if (lockFile) if (![lockFile lock]) return NO; // lock if needed
	file = fopen([fileName stringValue], "a");	// do the open
	if (!file) return NO;
	fileOpen = YES;
	[self _newLine];
	return YES;
}

- (BOOL)closeFile
{
	if (special) return YES;	// don't close stdin or stdout
	if (!fileOpen) return NO;	// can't close an unopened file
	[self _printRepeats];
	fclose(file);
	if (lockFile) if (![lockFile unlock]) return NO;	// unlock if needed
	fileOpen = NO;
	return YES;
}

- _printRepeats
{
	if (repeats) {
		fprintf(file, "Last line repeated %d time%s\n", repeats,
				((repeats == 1) ? "." : "s."));
	}
	[self _newLine];
	return self;
}

- (BOOL)appendToLogFile:aString andFlush:(BOOL)flushFlag
{
	if (!fileOpen) return NO;
	// if the file is open, then we should have the lock.
	// however, the user can get around our mechanism if they want, so
	// we'd best check anyway, just to be sure.
	if (lockFile && !special) if (![lockFile haveLock]) return NO;
	// flushing will force a write; not flushing allows us to "compress"
	// log messages by printing only number of repeats.
	if (lastLine && !flushFlag) { // have a previous line, see if it repeats...
		if ([lastLine isEqual:aString]) { // same as before
			repeats++;
			return YES; // don't print; keep tallying
	}	}
	// if got here, the log line is different than last time.
	[self _printRepeats];
	fprintf(file, "%s", [aString stringValue]);
	lastLine = [aString copy];
	if (flushFlag) if (fflush(file) == EOF) return NO;
	return YES;
}

- usestdout
{
	[self setFileName:[[MiscString alloc] initString:"stdout"]];
	special = YES;
	fileOpen = YES;
	file = stdout;
	return self;
}

- usestderr
{
	[self setFileName:[[MiscString alloc] initString:"stderr"]];
	special = YES;
	fileOpen = YES;
	file = stderr;
	return self;
}

- (FILE *)file { if (fileOpen) return file; else return NULL; }
- (BOOL)fileIsOpen { return fileOpen; }
- (BOOL)special { return special; }
- lockFile { return lockFile; }

- copy
{	// copies do not have locks or open logs... only original can!
	id myCopy = [[MiscLogFile alloc] init];
	[myCopy setFileName:fileName];
	[myCopy setLockFile:[lockFile copy]];
	return myCopy;
}

- read:(NXTypedStream *)stream
{
	[super read:stream];
	fileName = NXReadObject(stream);
	lockFile = NXReadObject(stream);
	return self;
}

- write:(NXTypedStream *)stream
{
	[super write:stream];
	NXWriteObject(stream, fileName);
	NXWriteObject(stream, lockFile);
	return self;
}

// NXTransport protocol implementation:
- encodeUsing:(id <NXEncoding>)portal
{
	[portal encodeObjectBycopy:fileName];
	[portal encodeObjectBycopy:lockFile];
	return self;
}

- decodeUsing:(id <NXDecoding>)portal
{
	fileName = [portal decodeObject];
	lockFile = [portal decodeObject];
	return self;
}

- encodeRemotelyFor:(NXConnection *)connection
	freeAfterEncoding:(BOOL *)flagp isBycopy:(BOOL)isByCopy
{
	if (isByCopy) {
		*flagp = NO; // object will copy.
		return self; //encode object (copy it)
	}
	*flagp = NO; // object will copy.
	// super will encode the proxy otherwise
	return [super encodeRemotelyFor:connection
				freeAfterEncoding:flagp isBycopy:isByCopy];
}

- free
{
	if (fileOpen) [self closeFile];
	[lockFile free];
	[fileName free];
	return [super free];
}

@end
