/*	MiscDGWriter.m

	Copyright 1996 Uwe Hoffmann.

	This notice may not be removed from this source code.
	The use and distribution of this software is governed by the
	terms of the MiscKit license agreement.  Refer to the license
	document included with the MiscKit distribution for the terms.

	Version 2 (August 1996)
*/

#import <MiscFoundation/MiscLayoutTree.h>

#import "MiscDGWriter.h"
#import "MiscTreeDiagram.h"
#import "MiscTreeDiagramView.h"
#import "MiscDiagramTree.h"
#import "MiscNodeStyle.h"
#import "MiscTreeStyle.h"
#import "MiscDiagramShape.h"
#import "MiscDG.h"

@interface MiscLayoutTree (DGArchiving)

- (int)DGNodeTag;
- (void)setDGNodeTag:(int)aTag;
- (int)DGVertexTagAt:(int)index;
- (void)setDGVertexTag:(int)aTag at:(int)index;

@end

@interface MiscDGWriter (PrivateMethods)

- (void)_internalWriteProlog;
- (void)_internalTagNodesAndWriteSymbols;
- (void)_internalWriteVertices;
- (void)_internalWriteLines;
- (void)_internalWriteEpilog;
- (int)_internalInsertColor:(NSColor *)aColor;
- (void)_internalWriteColor:(NSColor *)aColor;

@end


@implementation MiscDGWriter

+ dgWriter
{
	return [[[MiscDGWriter alloc] init] autorelease];
}

- init
{
	[super init];
	dgLastSymbolNumber = 0;
	td = nil;
	path = nil;
	errorMessage = @"";
	colorList = nil;
	diagramText = nil;
        useColorList = NO;
        usePrintInfo = NO;
	return self;
}


- (BOOL)writeTreeDiagram:(MiscTreeDiagram *)aTreeDiagram toDirectory:(NSString *)aPath
{
	NSString *fileComponentName;
	NSFileManager *fm = [NSFileManager defaultManager];
	BOOL isDir;

	NSParameterAssert(aTreeDiagram != nil && aPath != nil);

	td = aTreeDiagram;
	tree = [td root];
	path = aPath;
	dgLastSymbolNumber = 0;
	errorMessage = @"";
        if(useColorList)
		colorList = [[[NSColorList alloc] initWithName:@"DGColors"] autorelease];
        else
            	colorList = nil;
	diagramText = [NSMutableData data];

	if([fm fileExistsAtPath:path isDirectory:&isDir]){
                if(![fm removeFileAtPath:aPath handler:nil]){
                        errorMessage = @"Could not remove old directory";
                        NSBeep();
                        return NO;
                }
        }
        if(![fm createDirectoryAtPath:path attributes:nil]){
                errorMessage = @"Could not create directory";
                NSBeep();
                return NO;
        }
	
	[self _internalWriteProlog];
	if(tree){
		[self _internalTagNodesAndWriteSymbols];
		[self _internalWriteVertices];
		[self _internalWriteLines];
	}
	[self _internalWriteEpilog];
	
	fileComponentName = [NSString stringWithFormat:@"%@/DiagramText", path];
	if(![diagramText writeToFile:fileComponentName atomically:YES]){
		errorMessage = @"Could not write DiagramText";
                NSBeep();
                return NO;
	}
	
        if(useColorList){	
		fileComponentName = [NSString stringWithFormat:@"%@/Colors.clr", path];
		if(![colorList writeToFile:fileComponentName]){
			errorMessage = @"Could not write Colors.clr";
                	NSBeep();
                	return NO;
		}	
	}

        if(usePrintInfo){        
		fileComponentName = [NSString stringWithFormat:@"%@/PrintInfo", path];
		if(![NSArchiver archiveRootObject:[td printInfo] toFile:fileComponentName]){
			errorMessage = @"Could not write PrintInfo";
                	NSBeep();
                	return NO;
		}
	}

        return YES;
}
	
- (NSString *)errorMessage
{
	return errorMessage;
}

@end

@implementation MiscDGWriter (PrivateMethods)

- (void)_internalWriteProlog
{
	MiscTreeDiagramView *treeView;
	NSRect frame;

	[diagramText appendBytes:dgDocuBegin length:strlen(dgDocuBegin)];
	if(useColorList && tree){
		sprintf(buffer,"colorFile \"Colors.clr\"\n");
		[diagramText appendBytes:buffer length:strlen(buffer)];
	}
	if(usePrintInfo){
		sprintf(buffer,"printInfoFile \"PrintInfo\"\n");
		[diagramText appendBytes:buffer length:strlen(buffer)];
	}
	treeView = [td view];
	if(treeView){
		frame = [treeView frame];
		sprintf(buffer,"viewSize %.2f %.2f\n", frame.size.width, frame.size.height);
		[diagramText appendBytes:buffer length:strlen(buffer)];
		sprintf(buffer,"canvasSize %d %d\n", [treeView canvasWidthNumberOfPages], 
			[treeView canvasHeightNumberOfPages]);
		[diagramText appendBytes:buffer length:strlen(buffer)];
	}
}

- (void)_internalTagNodesAndWriteSymbols
{
	NSEnumerator *enumerator;
	MiscDiagramTree *node;
	MiscNodeStyle *nodeStyle;
	NSPoint pos;
	NSSize size;
	MiscTreeStyle *treeStyle;
	BOOL shadow;
	int shadowColorIndex = 0;
        NSColor *shadowColor;
	NSAttributedString *label;

	[diagramText appendBytes:dgSymbolComment length:strlen(dgSymbolComment)];

	treeStyle = [td treeStyle];
	
	shadow = [treeStyle shadow];
        shadowColor = [treeStyle shadowColor];
	if(shadow && useColorList)
		shadowColorIndex = [self _internalInsertColor:[treeStyle shadowColor]];

	enumerator = [tree depthEnumerator];
	while((node = (MiscDiagramTree *)[enumerator nextObject])){

		nodeStyle = [node nodeStyle];
		pos = [node pos];
		size = [node size];

		[node setDGNodeTag:++dgLastSymbolNumber];

		sprintf(buffer,"symbol %d\n", dgLastSymbolNumber);
		[diagramText appendBytes:buffer length:strlen(buffer)];
		sprintf(buffer,"\tshape \"%s\"\n", dgShapeSymbolName[[node shapeType]]);
		[diagramText appendBytes:buffer length:strlen(buffer)];
		sprintf(buffer,"\tlocation %.2f %.2f\n",pos.x,pos.y);
		[diagramText appendBytes:buffer length:strlen(buffer)];
		sprintf(buffer,"\tsize %.2f %.2f\n",size.width,size.height);
		[diagramText appendBytes:buffer length:strlen(buffer)];

		if([nodeStyle outline])
			[diagramText appendBytes:"\tframed\n" length:8];
		if([nodeStyle fill])
			[diagramText appendBytes:"\tfilled\n" length:8];
		if(shadow)
			[diagramText appendBytes:"\tshadowed\n" length:10];
		
		if([nodeStyle outline]){
                    	if(useColorList){
				sprintf(buffer,"\tframeColor colorIndex %d\n", 
					[self _internalInsertColor:[nodeStyle outlineColor]]);
				[diagramText appendBytes:buffer length:strlen(buffer)];
                        } else {
                            	sprintf(buffer,"\tframeColor ");
                            	[diagramText appendBytes:buffer length:strlen(buffer)];
                                [self _internalWriteColor:[nodeStyle outlineColor]];
                                [diagramText appendBytes:"\n" length:1];
                        }    
		}
		if([nodeStyle fill]){
                    	if(useColorList){
				sprintf(buffer,"\tfillColor colorIndex %d\n", 
					[self _internalInsertColor:[nodeStyle fillColor]]);
				[diagramText appendBytes:buffer length:strlen(buffer)];
                        } else {
                            	sprintf(buffer,"\tfillColor ");
                            	[diagramText appendBytes:buffer length:strlen(buffer)];
                                [self _internalWriteColor:[nodeStyle fillColor]];
                                [diagramText appendBytes:"\n" length:1];
                        }
		}
		if(shadow){
                    	if(useColorList){
				sprintf(buffer,"\tshadowColor colorIndex %d\n", shadowColorIndex);
				[diagramText appendBytes:buffer length:strlen(buffer)];
                        } else {
                            	sprintf(buffer,"\tshadowColor ");
                            	[diagramText appendBytes:buffer length:strlen(buffer)];
                                [self _internalWriteColor:shadowColor];
                                [diagramText appendBytes:"\n" length:1];
                        }        
		}

		label = [node label];
		if(label){
			[diagramText appendBytes:"\trtfText " length:9];
			[diagramText appendData:
				[label RTFFromRange:NSMakeRange(0,[label length]) documentAttributes:nil]];
			[diagramText appendBytes:"\n" length:1];
		}
		
		sprintf(buffer,"\ttextPlacement %s\n", dgTextPlacement[[nodeStyle textPlacement]]);
		[diagramText appendBytes:buffer length:strlen(buffer)];

		[diagramText appendBytes:dgEndName length:strlen(dgEndName)];
	}	
}

- (void)_internalWriteVertices
{
	NSEnumerator *enumerator;
	MiscDiagramTree *node;
	NSPoint p;
	MiscTreeStyle *treeStyle;

	[diagramText appendBytes:dgVertexComment length:strlen(dgVertexComment)];

	treeStyle = [td treeStyle];
	
	enumerator = [tree depthEnumerator];
	while((node = (MiscDiagramTree *)[enumerator nextObject])){
		if([node parent]){
			[node setDGVertexTag:++dgLastSymbolNumber at:0];
			sprintf(buffer,"vertex %d\n", dgLastSymbolNumber);
			[diagramText appendBytes:buffer length:strlen(buffer)];
			[diagramText appendBytes:dgEndName length:strlen(dgEndName)];

			[node setDGVertexTag:++dgLastSymbolNumber at:1];
			sprintf(buffer,"vertex %d\n", dgLastSymbolNumber);
			[diagramText appendBytes:buffer length:strlen(buffer)];
			[diagramText appendBytes:dgEndName length:strlen(dgEndName)];

			if([treeStyle lineType] == MiscBendLineType){
				p = [(MiscDiagramTree *)[node parent] parentEndPoint];
				[node setDGVertexTag:++dgLastSymbolNumber at:2];
				sprintf(buffer,"vertex %d\n", dgLastSymbolNumber);
				[diagramText appendBytes:buffer length:strlen(buffer)];
				sprintf(buffer,"\tlocation %.2f %.2f\n", p.x, p.y);
				[diagramText appendBytes:buffer length:strlen(buffer)];
				[diagramText appendBytes:dgEndName length:strlen(dgEndName)];
				
				p = [node childEndPoint];
				[node setDGVertexTag:++dgLastSymbolNumber at:3];
				sprintf(buffer,"vertex %d\n", dgLastSymbolNumber);
				[diagramText appendBytes:buffer length:strlen(buffer)];
				sprintf(buffer,"\tlocation %.2f %.2f\n", p.x, p.y);
				[diagramText appendBytes:buffer length:strlen(buffer)];
				[diagramText appendBytes:dgEndName length:strlen(dgEndName)];
			}
		}
	}	
}

- (void)_internalWriteLines
{
	NSEnumerator *enumerator;
	MiscDiagramTree *node;
	MiscNodeStyle *nodeStyle;
	MiscTreeStyle *treeStyle;

	[diagramText appendBytes:dgLineComment length:strlen(dgLineComment)];

	treeStyle = [td treeStyle];
	
	enumerator = [tree depthEnumerator];
	while((node = (MiscDiagramTree *)[enumerator nextObject])){
		if([node parent]){
			nodeStyle = [node nodeStyle];

			sprintf(buffer,"line %d\n",++dgLastSymbolNumber);
			[diagramText appendBytes:buffer length:strlen(buffer)];
			sprintf(buffer,"\tfrom %d\n",[node DGNodeTag]);
			[diagramText appendBytes:buffer length:strlen(buffer)];
			sprintf(buffer,"\tto %d\n",[[node parent] DGNodeTag]);
			[diagramText appendBytes:buffer length:strlen(buffer)];
			if([nodeStyle childEnding] != MiscNoEnding){
				switch([nodeStyle childEnding]){
					case MiscArrowEnding:
						sprintf(buffer,"\ttailType arrow\n");
						break;
					case MiscHollowEnding:
						sprintf(buffer,"\ttailType hollowArrow\n");
						break;
					case MiscDoubleEnding:
						sprintf(buffer,"\ttailType doubleArrow\n");
						break;
					case MiscSolidEnding:
						sprintf(buffer,"\ttailType solidCircle\n");
						break;
					case MiscCircleEnding:
						sprintf(buffer,"\ttailType circle\n");
						break;
					default:
						break;
				}
				[diagramText appendBytes:buffer length:strlen(buffer)];
			}
			if([[(MiscDiagramTree *)[node parent] nodeStyle] parentEnding] != MiscNoEnding){
				switch([[(MiscDiagramTree *)[node parent] nodeStyle] parentEnding]){
					case MiscArrowEnding:
						sprintf(buffer,"\theadType arrow\n");
						break;
					case MiscHollowEnding:
						sprintf(buffer,"\theadType hollowArrow\n");
						break;
					case MiscDoubleEnding:
						sprintf(buffer,"\theadType doubleArrow\n");
						break;
					case MiscSolidEnding:
						sprintf(buffer,"\theadType solidCircle\n");
						break;
					case MiscCircleEnding:
						sprintf(buffer,"\theadType circle\n");
						break;
					default:
						break;
				}
				[diagramText appendBytes:buffer length:strlen(buffer)];
			}
			sprintf(buffer,"\t%d\n",[node DGVertexTagAt:0]);
			[diagramText appendBytes:buffer length:strlen(buffer)];
			if([treeStyle lineType] == MiscBendLineType){
				sprintf(buffer,"\t%d\n",[node DGVertexTagAt:3]);
				[diagramText appendBytes:buffer length:strlen(buffer)];
				sprintf(buffer,"\t%d\n",[node DGVertexTagAt:2]);
				[diagramText appendBytes:buffer length:strlen(buffer)];
			}	
			sprintf(buffer,"\t%d\n",[node DGVertexTagAt:1]);
			[diagramText appendBytes:buffer length:strlen(buffer)];
			[diagramText appendBytes:dgEndName length:strlen(dgEndName)];
		}
	}	
}

- (void)_internalWriteEpilog
{

}

- (int)_internalInsertColor:(NSColor *)aColor
{
	NSArray *keys;
	unsigned int i,n;

	i = 0;
	keys = [colorList allKeys];
	n = [keys count];
	while(i < n && ![aColor isEqual:[colorList colorWithKey:[keys objectAtIndex:i]]])
		i++;
	if(i == n)
		[colorList setColor:aColor forKey:[NSString stringWithFormat:@"miscTDColorIndexed%d",i]];
	return (int)i;
}

- (void)_internalWriteColor:(NSColor *)aColor
{
    	NSColor *rgbColor;
    	float r,g,b;

    	rgbColor = [aColor colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
        [rgbColor getRed:&r green:&g blue:&b alpha:NULL];
        sprintf(buffer,"rgbColor %.2f %.2f %.2f", r, g, b);
	[diagramText appendBytes:buffer length:strlen(buffer)];
}

@end

@implementation MiscLayoutTree (DGArchiving)

- (int)DGNodeTag
{
	return DGNodeTag;
}

- (void)setDGNodeTag:(int)aTag
{
	DGNodeTag = aTag;
}

- (int)DGVertexTagAt:(int)index
{
	return DGVertexTag[index];
}

- (void)setDGVertexTag:(int)aTag at:(int)index
{
	DGVertexTag[index] = aTag;
}

@end
