/* SortedList.m
 * Written By:  Thomas Burkholder
 *
 * You may freely copy, distribute, and reuse the code in this example.
 * NeXT disclaims any warranty of any kind, expressed or  implied, as to its
 * fitness for any particular use.
 */

#import "SortedList.h"
#import "SortedStorage.h"

@implementation SortedList:List

- initCount:(unsigned)numSlots
{
	[super initCount:numSlots];
	agent = nil;
	controlView = nil;
	return self;
}

- setAgent:anObject
{
	agent = anObject;
	return self;
}

- agent
{
	return agent;
}

- (id)subdirectoryForObjectAt:(int)at
{
	if (agent && [agent respondsTo:@selector(subdirectoryFor:sender:)])
		return [agent subdirectoryFor:[self objectAt:at] sender:self];
	return nil;  // default is no entries.
}

- (BOOL)isLeafAt:(int)at
{
	if (agent && [agent respondsTo:@selector(isLeaf:sender:)])
		return [agent isLeaf:[self objectAt:at] sender:self];
	return YES;  // default is YES.
}

- (const char *)displayStringForObjectAt:(int)at
{
	if (agent && [agent respondsTo:@selector(displayStringFor:sender:)])
		return [agent displayStringFor:[self objectAt:at] sender:self];
	return NULL;  // default is YES.
}

- (int)compareObjectAt:(int)at with:object
{
	if (agent && [agent respondsTo:@selector(compare:with:sender:)])
		return [agent compare:[self objectAt:at] with:object sender:self];
	return 1;  //By default, it's smaller, so it get's appended.
}

- (int)browser:sender fillMatrix:matrix inColumn:(int)column
{
	id c;
	int i,n;
	id candidate = self;

	controlView = sender;

	if (column != 0) {
		for(i=0;(i<column);i++) {
			candidate = [candidate subdirectoryForObjectAt:[[sender matrixInColumn:i] selectedRow]];
			if ((!candidate) || ((![candidate isKindOf:[SortedList class]]) && (![candidate isKindOf:[SortedStorage class]]))) return 0;
		}
	}

	for(i=0,n=[candidate count];i<n;i++) {
		[matrix addRow];
		c = [matrix cellAt:i :0];
		[c setLeaf:[candidate isLeafAt:i]];
		[c setLoaded:YES];
		if ([candidate isKindOf:[SortedList class]])
			[c setStringValue:[candidate displayStringForObjectAt:i]];
		else if ([candidate isKindOf:[SortedStorage class]])
			[c setStringValue:[candidate displayStringForElementAt:i]];
		else
			return 0;
	}
	return i;
}

- (const char *)browser:sender titleOfColumn:(int)column
{
	return [agent titleOfColumn:column];
}

- binaryInsert:anObject between:(int)lower and:(int)upper
{
	int r,c = lower + (1+upper-lower)/2;

	if (lower == upper) {  
		if ([self compareObjectAt:lower with:anObject]>0) // insert before
			return [super insertObject:anObject at:lower];
		else  // insert after
			return [super insertObject:anObject at:lower+1];
	}

	r = [self compareObjectAt:c with:anObject];
	if (r>0)  // Recursion is Good.
		return [self binaryInsert:anObject between:lower and:c-1];
	else if (r<0)  // In fact, Recursion is Holy.
		return [self binaryInsert:anObject between:c and:upper];
	else  // if equal insert right now.
		return [super insertObject:anObject at:c];
}

- addObject:anObject  // Assumes sorted list
{
	// Binary insertion...
	if (![self count])
		return [super addObject:anObject];
	[self binaryInsert:anObject between:0 and:[self count]-1];
	return self;
}

- sort:sender
{
	id l = [[List alloc] init];
	int i, n;

	// move our elements into plain old list.
	for(i=0,n=[self count];(i<n);i++)
		[l addObject:[self objectAt:i]];
	[self empty];


	// re-insert everything.
	for(i=0,n=[l count];(i<n);i++)
		[self addObject:[l objectAt:i]];
	[l empty];
	[l free];
	return self;
}

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

- awakeFromNib
{
	// This won't be called in Test Interface mode.
	[self sort:self];
	return self;
}

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

@end