#import "BrowserMatrix.h"
#import <objc/List.h>
#import <appkit/SelectionCell.h>
#import <sys/types.h>
#import <sys/stat.h>
#import <sys/file.h>
#import <math.h>

static int slaveRow;

extern int strcmp(), strlen(), stat();
extern char *strcat(), *rindex();

@implementation BrowserMatrix

+ newFrame:(NXRect *)r
{
	NXSize r1;
	float height;
	
	self = [super newFrame:r
		      mode:NX_RADIOMODE
		      cellClass:[SelectionCell class]
		      numRows:0
		      numCols:1];
	slaves = [List new];
	master = nil;
	alphabetize = YES;
	height = r->size.height;
	visEntries = (int) floor ((double)height / TEXTHEIGHT);
	cellSize.height = height / (float) visEntries;
	cellSize.width = r->size.width;
	intercell.width = intercell.height = 0.0;
	mFlags.allowEmptySel = YES;
	topRow = 0;
	return self;
}

- mouseDown:(NXEvent *)event
{
	int i, count;

	if (master != nil) return self;
	if ((count = [slaves count]) == 0) {
		[super mouseDown:event];
		return self;
		}
	[window disableFlushWindow];
	[super mouseDown:event];
	for (i = 0; i < count; i++) {
		[[slaves objectAt:i] selectEntryAt:selectedRow];
		}
	[window reenableFlushWindow];
	[window flushWindow];
	return self;
}

- selectEntryAt:(int)row
{
	if (row < numRows) {
		if (master == nil) [window disableFlushWindow];
		[self selectCellAt:row :0];
		[self sendAction];
		[slaves makeObjectsPerform:@selector(selectEntryAt:)
			with:(id)row];
		if (master == nil) {
			[window reenableFlushWindow];
			[window flushWindow];
			}
		}
	return self;
}

- clear
{
	int row, count;
	
	count = numRows;
	for (row = 0; row < count; row++) [self removeRowAt:0 andFree:YES];
	topRow = 0;
	[slaves makeObjectsPerform:@selector(clear)];
	return self;
}

- display
{
	[self sizeToCells];
	if ([slaves count] == 0) {
		[super display];
		return self;
		}
	[window disableFlushWindow];
	[super display];
	[slaves makeObjectsPerform:@selector(display)];
	[window reenableFlushWindow];
	[window flushWindow];
	return self;
}

- (BOOL)atTop
{
	return topRow == 0;
}

- (BOOL)atBottom
{
	return (topRow + visEntries) >= numRows;
}

- scrollDown:sender
{
	int bottomRow;
	
	if ((master != nil) && ([sender class] != [BrowserMatrix class]))
		return self;
	if ((bottomRow = ++topRow + visEntries - 1) >= numRows) {
		--topRow;
		--bottomRow;
		return self;
		}
	[self scrollCellToVisible:bottomRow :0];
	[self scrollCellToVisible:topRow :0];
	[slaves makeObjectsPerform:@selector(scrollDown:) with:self];
	return self;
}

- scrollUp:sender
{
	if ((master != nil) && ([sender class] != [BrowserMatrix class]))
		return self;
	if (--topRow < 0) {
		topRow = 0;
		return self;
		}
	[self scrollCellToVisible:topRow :0];
	[slaves makeObjectsPerform:@selector(scrollUp:) with:self];
	return self;
}

- addEntry:(const char *)text leaf:(BOOL)yn
{
	int row, count, i, selectable;
	id newCell, cells, slave;
	const char *name;
	
	if (master == nil) {
		if (alphabetize) {
			for (row = 0; row < numRows; row++) {
				name = [[cellList objectAt:row] stringValue];
				if (strcmp (name, text) >= 0) break;
				}
			}
		else row = numRows;
		[self insertRowAt:row];
		slaveRow = row;
		count = [slaves count];
		for (i = 0; i < count; i++) {
			slave = [slaves objectAt:i];
			[slave addEntry:"" leaf:YES];
			}
		selectable = YES;
		}
	else {
		row = slaveRow;
		[self insertRowAt:row];
		selectable = NO;
		}
	newCell = [self cellAt:row :0];
	[newCell setSelectable:selectable];
	[newCell setEditable:NO];
	[newCell setLeaf:yn];
	[newCell setStringValue:text];
	[self selectEntryAt:row];
	return self;
}

- removeEntry:aCell
{
	int row, col;
	
	if (master == nil)
		[self getRow:&row andCol:&col ofCell:aCell];
	else row = (int)aCell;
	[self removeRowAt:row andFree:YES];
	[slaves makeObjectsPerform:@selector(removeEntry:) with:(id)row];
	if (master == nil) [self display];
	return self;
}

- listFiles:(const char *)dir suffix:(const char *)sfx
{
	char command[128], *fgets(), *pt;
	FILE *pipe, *popen();
	void pclose();
	struct stat statbuf;
	BOOL oldalpha;
	
	oldalpha = alphabetize;
	alphabetize = NO;
	sprintf (command, "/bin/ls -1d 2>/dev/null %s/*", dir);
	if (sfx != NULL) {
		strcat (command, ".");
		strcat (command, sfx);
		}
	pipe = popen (command, "r");
	while (fgets (command, 128, pipe) != NULL) {
		command[strlen (command) - 1] = '\0';
		stat (command, &statbuf);
		if ((pt = rindex (command, '/')) != NULL) pt++;
		else pt = command;
		[self addEntry:pt leaf:!(statbuf.st_mode & S_IFDIR)];
		}
	pclose (pipe);
	alphabetize = oldalpha;
	return self;
}

- setMaster:anObject
{
	master = anObject;
	return self;
}

- addSlave:aBrowserMatrix
{
	if (master == nil) {
		[slaves addObjectIfAbsent:aBrowserMatrix];
		[aBrowserMatrix setMaster:self];
		}
	return self;
}

- setAlphabetize:(BOOL)yn
{
	alphabetize = yn;
	return self;
}

- free
{
	[slaves free];
	[super free];
	return self;
}

@end

