#import <foundation/foundation.h>
#import <foundation/NSArchiver.h>

#import "TreeView.h"
#import "MiscDiagramTree.h"
#import "MiscUserPath.h"
#import "ScrollViewExtensions.h"
#import "uwFoundationAdds.h"

static const char *opSeparator = "\n----------------------------------------------------------------------\n";

static id createMiscTree(NXStream *stream);
static int getNextNodeLabel(char *buffer, int n, NXStream *stream);
 
@implementation TreeView

- initFrame:(const NXRect *)frameRect
{
	MiscLayoutTree *l, *r, *ll, *lr;
	
	[super initFrame:frameRect];
	[self setFlipped:YES];
	[window makeKeyAndOrderFront:nil];
	treeDiagram = [[MiscDiagramTree alloc] init];
	l = [MiscDiagramTree tree];
	r = [MiscDiagramTree tree];
	ll = [MiscDiagramTree tree];
	lr = [MiscDiagramTree tree];
	[l addBranch:ll];
	[l addBranch:lr];
	[treeDiagram addBranch:l];
	[treeDiagram addBranch:r];
	[treeDiagram setLayoutType:MiscVerticalTreeType];
	[treeDiagram layout];
	[self posTree];
	linePath = [[MiscUserPath alloc] init];
	[treeDiagram fillLines:linePath];
	return self;
}

- free
{
	[treeDiagram release];
	[linePath release];
	return [super free];
}

- (void)posTree
{
	NSRect r;
	NSPoint oldPos;
	NSPoint newPos;

	r = [treeDiagram branchBounds];
	oldPos = [treeDiagram pos];
	newPos.x = (bounds.size.width - r.size.width) / 2 + (oldPos.x - r.origin.x);
	newPos.y = (bounds.size.height - r.size.height) / 2 + (oldPos.y - r.origin.y);
	[treeDiagram setPos:newPos];
	[linePath resetFill];
	[treeDiagram fillLines:linePath];
}

- sizeTo:(NXCoord)width :(NXCoord)height
{	
	[super sizeTo:width :height];
	[self posTree];
	return self;
}

- drawSelf:(const NXRect *)r :(int)count
{   
	PSsetgray(NX_WHITE);
	NXRectFill(&bounds);
   	PSsetgray(NX_BLACK);
	if([treeDiagram needsUpdateLayout]){
		[treeDiagram layout];
		[linePath resetFill];
		[treeDiagram fillLines:linePath];
	}
	[linePath send:dps_ustroke cached:YES];
	PSsetgray(NX_WHITE);
	[treeDiagram drawFill];
	PSsetgray(NX_BLACK);
	[treeDiagram drawOutline];
	[treeDiagram drawCellsInView:self attachments:NO];    	         	         	     
   	return self;			
}

- depthFirst:sender
{
	NSEnumerator *enumerator;
	id node;
	
	[scrollView sprintf:opSeparator];
	enumerator = [treeDiagram depthEnumerator];
	while(node = [enumerator nextObject])
		[scrollView sprintf:"%s\n",[[node label] cString]];
	return self;
}

- breadthFirst:sender
{
	NSEnumerator *enumerator;
	id node;
	
	[scrollView sprintf:opSeparator];
	enumerator = [treeDiagram breadthEnumerator];
	while(node = [enumerator nextObject])
		[scrollView sprintf:"%s\n",[[node label] cString]];
	return self;
}

- open:sender
{
	const char *const *files;
	const char *directory;
	NSString *path;
	id unarchivedObject;
  	const char *const allowedType[2] = {"owood", NULL};
    id openpanel = [[OpenPanel new] allowMultipleFiles:NO];
	BOOL result = NO;
	
	if([openpanel runModalForTypes:allowedType]){
		files = [openpanel filenames];
		directory = [openpanel directory];
		if(files && *files){
			path = [NSString stringWithFormat:@"%s/%s",directory,*files];
			unarchivedObject = [NSUnarchiver unarchiveObjectWithFile:path];
			if(unarchivedObject){
				result = YES;
				[treeDiagram release];
				treeDiagram = [unarchivedObject retain];
			}
		}
		[scrollView sprintf:opSeparator];
		if(result)
			[scrollView sprintf:"Unarchiving succesful\n"];
		else
			[scrollView sprintf:"Unarchiving not succesful\n"];
	}
	if(result)
		[self display];
	return self;
}

- save:sender
{
	id savepanel;	
	NSString *path;
	BOOL result;
	
    savepanel = [SavePanel new];
	[savepanel setRequiredFileType:"owood"];
	if([savepanel runModal]){ 
		path = [NSString stringWithCString:[savepanel filename]];
		result = [NSArchiver archiveRootObject:treeDiagram toFile:path];
		[scrollView sprintf:opSeparator];
		if(result)
			[scrollView sprintf:"Archiving succesful\n"];
		else
			[scrollView sprintf:"Archiving not succesful\n"];
	}
	return self;
}

- openMMA:sender
{
	const char *fileName;
	id openpanel = [OpenPanel new];
	NXStream *aStream;
	const char *const fileTypes[] = {"tree", NULL};
	id unarchivedObject;

	[openpanel allowMultipleFiles:NO];
	if([openpanel runModalForTypes:fileTypes]){
		fileName = [openpanel filename];
		if((aStream = NXMapFile(fileName, NX_READONLY)) == NULL){
			NXRunAlertPanel(NULL, "File %s could not be opened for reading.", NULL, NULL, NULL, fileName);
			return nil;
		}
		unarchivedObject = createMiscTree(aStream);
		NXCloseMemory(aStream, NX_FREEBUFFER);
		if(unarchivedObject){
				[treeDiagram release];
				treeDiagram = unarchivedObject;
				[self posTree];
				[self display];
		}
		return self;
	}
	return nil;
}

- vertical:sender
{
	[treeDiagram setLayoutType:MiscVerticalTreeType];
	[self posTree];
	[self display];
	return self;
}

- horizontal:sender
{
	[treeDiagram setLayoutType:MiscHorizontalTreeType];
	[self posTree];
	[self display];
	return self;
}


@end

static int getNextNodeLabel(char *buffer, int n, NXStream *stream)
{
	int c, i = 0, blanks = 0;
	int tnums = 0;
	
	while(((c = NXGetc(stream)) != EOF) && (i < n-1) && (c == ' ' || c == '\t')){
		if(c == ' '){
			blanks++;
			if(blanks == 3){
				blanks = 0;
				tnums++;
			}	 
		} else	
			tnums++;
	}
	buffer[i++] = c;	
	while(((c = NXGetc(stream)) != EOF) && (i < n-1)){
		if(c == '\n')
			break;
		else
			buffer[i++] = c;
	}
	if(c == EOF)
		tnums = -1;
	buffer[i] = 0;	
	return tnums;
}


static id createMiscTree(NXStream *stream)
{
	int tnums, lastTNums = 0, i, tags = -1;
	id lastNode, node;
	char labelBuffer[MAXPATHLEN];
	
	tnums = getNextNodeLabel(labelBuffer,MAXPATHLEN,stream);
	lastNode = node = nil;
	tnums = getNextNodeLabel(labelBuffer,MAXPATHLEN,stream);
	while(tnums != -1){
		if(labelBuffer){
			node = [[MiscDiagramTree alloc] initWithLabel:[NSString stringWithCString:labelBuffer] 
						shapeType:MiscRectangleShapeType];
			[(MiscLayoutTree *)node setTag:++tags];
			if(lastNode){
				if(lastTNums >= tnums)
					for(i = 0; i <= lastTNums - tnums && [lastNode parent]; i++, lastNode = [lastNode parent]);
				[lastNode addBranch:node];
			}
			lastNode = node;
			lastTNums = tnums;
		}
		tnums = getNextNodeLabel(labelBuffer,MAXPATHLEN,stream);
	}
	while([node parent])
		node = [node parent];
	return node;
}	


