#import "Fiend.h"
#import "IconDragView.h"
#import "Dock.h"

@implementation Dock

- initMgrView:theDockMgrView
{
	dockMgrView = theDockMgrView;
	[[dockMgrView window] getFrame:&frame];

	dockWindowList = [[List alloc] init];
	fileNameStore = [[Storage alloc] initCount:0 elementSize:sizeof(NXAtom) description:"%"];
	launchFlagStore = [[Storage alloc] initCount:0 elementSize:sizeof(BOOL) description:"s"];
	filePositionStore = [[Storage alloc] initCount:0
						 elementSize:sizeof(NXPoint) description:"{ff}"];
	nameWinHash = [[HashTable alloc] initKeyDesc:"%" valueDesc:"@"];

	return self;
}

- free
{
	[fileNameStore free];
	[launchFlagStore free];
	[filePositionStore free];
	[[dockWindowList freeObjects] free];
	[nameWinHash free];

	return [super free];
}


- getOffset:(NXPoint *)theOffset fromFrame:(NXRect *)theFrame
{
	NXRect	mainFrame = frame;

	theOffset->x = (NX_X(theFrame) - NX_X(&mainFrame))/NX_WIDTH(&frame);
	theOffset->y = (NX_Y(theFrame) - NX_Y(&mainFrame))/NX_HEIGHT(&frame);

	return self;
}

- createWindowWithFrame:(NXRect *)theFrame
{
	id		newWin;

	newWin = [[Window allocFromZone:[self zone]]
			  initContent:theFrame
			  style:NX_TOKENSTYLE
			  backing:NX_BUFFERED buttonMask:0 defer:NO];
	PSsetwindowlevel(NX_DOCKLEVEL+1, [newWin windowNum]);

	return newWin;
}

- (BOOL)containsPath:(NXAtom)aPath
{
	int		i;
	int		count = [fileNameStore count];

	for(i = 0; i < count; i++)	{
		if (strstr(*(NXAtom *)[fileNameStore elementAt:i], aPath))
			return YES;
	}
	return NO;
}

- createIconViewForPath:(const char *)path withFrame:(NXRect *)iconFrame
{
	id		iconView;
	id	   	newImage;

	newImage = [IconView getImageForPath:path fileIcon:NO];
	iconView = [[IconDragView allocFromZone:[self zone]]
				    initFrame:iconFrame
				        image:newImage
				         data:path andLength:strlen(path)
				      useSize:YES onDock:YES];
	if (dockMgrView != nil)
		[iconView setDockMgrView:dockMgrView];
	[[iconView setAutodisplay:NO] display];

	return iconView;
}

- createWindowsFromStores
{
	id		newWin;
	id	   	iconView;
	int		i;
	int		count;
	BOOL	autoLaunch;
	NXAtom	path;
	NXPoint	*theOffset;
	BOOL	hideIcons = !strcmp(NXGetDefaultValue([NXApp appName], HIDE_ICONS), "YES");

	[nameWinHash empty];
	count = [filePositionStore count];
	for(i = 0; i < count; i++)	{
		autoLaunch = *(BOOL *)[launchFlagStore elementAt:i];
		path = *(NXAtom *)[fileNameStore elementAt:i];
		theOffset = (NXPoint *)[filePositionStore elementAt:i];
		newWin = [self createWindowWithFrame:&frame];
		[newWin moveTo:-1000.0 :-1000.0];
		iconView = [self createIconViewForPath:path withFrame:&frame];
		[iconView setAutoLaunch:autoLaunch];
		if (autoLaunch && ![iconView isLaunched])	{
			[[Application workspace]
			 launchApplication:path showTile:!hideIcons autolaunch:YES];
			[iconView setLaunched:YES];
		}
		[nameWinHash insertKey:path value:newWin];
		[[newWin setContentView:iconView] free];

		[newWin setAvoidsActivation:YES];
		[newWin display];
		[newWin orderFront:self];
		[dockWindowList addObject:newWin];
	}
	return self;
}

- updateWindows:(NXPoint *)p usePlace:(BOOL)usePlace
{
	id		*win;
	int		i;
	int		count;
	NXPoint	*offset;
	NXRect	winRect;
	int		width = NX_WIDTH(&frame);
	int		height = NX_HEIGHT(&frame);
	id		*winListPtr = NX_ADDRESS(dockWindowList);

	count = [filePositionStore count];
	for(i = 0, win = winListPtr; i < count; i++, win++)	{
		offset = (NXPoint *)[filePositionStore elementAt:i];
		if (usePlace)	{
			NXSetRect(&winRect, p->x + offset->x * width, p->y + offset->y * height,
					  NX_WIDTH(&frame), NX_HEIGHT(&frame));
			[*win placeWindowAndDisplay:&winRect];
		}
		else
			[*win moveTo:p->x + offset->x * width :p->y + offset->y * height];
		[filePositionStore replaceElementAt:i with:(void *)offset];
	}
	return self;
}

- setDockMgrView:theMgrView
{
	int		i;
	int		count = [dockWindowList count];

	dockMgrView = theMgrView;

	for(i = 0; i < count; i++)
		[[[dockWindowList objectAt:i] contentView] setDockMgrView:dockMgrView];
	return self;
}

- viewAtPosition:(NXRect *)checkRect besides:testView
{
	id		*win;
	int		i;
	int		count;
	NXRect	winFrame;
	id		*winListPtr = NX_ADDRESS(dockWindowList);

	count = [dockWindowList count];
	for(i = 0, win = winListPtr; i < count; i++, win++)	{
		if (testView && testView == [*win contentView])
			continue;
		[*win getFrame:&winFrame];
		if (NXIntersectsRect(checkRect, &winFrame))
			return [*win contentView];
	}
	return nil;
}

- select:(BOOL)flag all:sender
{
	id		theView;
	int		i;
	int		count = [dockWindowList count];

	for(i = 0; i < count; i++)	{
		theView = [[dockWindowList objectAt:i] contentView];
		[theView setState:flag];
		[theView display];
	}
	return self;
}

- getNextFrame:(NXRect *)theFrame
{
	
	*theFrame = frame;
	do	{
		if (NX_X(theFrame) >= NX_WIDTH(theFrame) &&
			((NX_X(&frame) - NX_X(theFrame)) <= NX_WIDTH(&frame)*5.0))	{
			NX_X(theFrame) -= NX_WIDTH(theFrame);
		}
		else	{
			NX_X(theFrame) = NX_X(&frame);
			if (NX_Y(theFrame) >= NX_HEIGHT(theFrame) &&
				NX_Y(theFrame) <= NX_Y(&frame))
				NX_Y(theFrame) -= NX_HEIGHT(&frame);
			else if (NX_Y(theFrame) >= NX_HEIGHT(&frame))
				NX_Y(theFrame) += NX_HEIGHT(&frame);
			else
				NX_Y(theFrame) = NX_MAXY(&frame);
		}
	}	while([self viewAtPosition:theFrame besides:nil] != nil);

	return self;
}

- createWindowForPath:(const char *)thePath newFrame:(NXRect *)newFrame
{
	id			newWin;
	id			iconView;

	if (!thePath)
		return nil;

	[self getNextFrame:newFrame];

	newWin = [self createWindowWithFrame:newFrame];
	iconView = [self createIconViewForPath:thePath withFrame:newFrame];
	[nameWinHash insertKey:NXUniqueString(thePath) value:newWin];
	[[newWin setContentView:iconView] free];

	[newWin setAvoidsActivation:YES];
	[dockWindowList addObject:newWin];

	[newWin display];
	[newWin orderFront:self];

	return self;
}

- orderWindowsFront:(BOOL)flag
{
	int		i;
	int		count = [dockWindowList count];

	for(i = 0; i < count; i++)	{
		if (flag)
			[[dockWindowList objectAt:i] orderFront:self];
		else
			[[dockWindowList objectAt:i] orderOut:self];
	}
	return self;
}

- addWindowForPath:(NXAtom)pathAtom
{
	NXRect	newFrame;
	NXPoint	newOffset;

	[self createWindowForPath:pathAtom newFrame:&newFrame];
	[fileNameStore addElement:(void *)&pathAtom];
	[self getOffset:&newOffset fromFrame:&newFrame];
	[filePositionStore addElement:(void *)&newOffset];

	return self;
}

- deleteView:aView
{
	NXAtom	pathAtom;
	id		win = [aView window];
	int		index = [dockWindowList indexOf:win];

	if (index != NX_NOT_IN_LIST)	{
		pathAtom = *(NXAtom *)[fileNameStore elementAt:index];
		[nameWinHash removeKey:pathAtom];
		[fileNameStore removeElementAt:index];
		[filePositionStore removeElementAt:index];
		[win orderOut:self];
		[win perform:@selector(free) with:nil afterDelay:1000.0 cancelPrevious:YES];
		[dockWindowList removeObject:win];
	}
	return self;
}

- dockWindowList
{
	return dockWindowList;
}

- copy:sender andCut:(BOOL)cutFlag
{
	id		win;
	id		view;
	int		i;
	int		junk;
	char	*viewPath;
	char	cutString[10000];
	int		count = [dockWindowList count];
	id		pb = [Pasteboard newName:NXGeneralPboard];
	id		cutList = [[List alloc] init];
	BOOL	useSound = !strcmp(NXGetDefaultValue([NXApp appName], USE_SOUND), "YES");
	const char *types[] = {NXFilenamePboardType, NXAsciiPboardType};

	*cutString = '\0';
	for(i = 0; i < count; i++)	{
		win = [dockWindowList objectAt:i];
		view = [win contentView];
		if ([view state])	{
			[view getData:(void **)&viewPath andLength:&junk];
			strcat(cutString, viewPath);
			strcat(cutString, "\t");
			[cutList addObject:view];
		}
	}

	cutString[strlen(cutString)-1] = '\0';
	[pb declareTypes:types num:2 owner:nil];
	[pb writeType:NXFilenamePboardType data:cutString length:strlen(cutString)];
	for(i = 0; i < strlen(cutString); i++)	{
		if (cutString[i] == '\t')
			cutString[i] = '\n';
	}
	strcat(cutString, "\n");
	[pb writeType:NXAsciiPboardType data:cutString length:strlen(cutString)];

	if (cutFlag)	{
		count = [cutList count];
		if (count && useSound)
			[[Sound findSoundFor:"Destroy"] play:self];
		for(i = 0; i < count; i++)
			[self deleteView:[cutList objectAt:i]];
	}
	[cutList free];

	return self;
}


- paste:sender
{
	int		len;
	char	*path;
	char	*dragString;
	NXAtom	pathAtom;
	id		pb = [Pasteboard newName:NXGeneralPboard];
	BOOL	useSound = !strcmp(NXGetDefaultValue([NXApp appName], USE_SOUND), "YES");

	[self select:NO all:self];
	[pb readType:NXFilenamePboardType data:&dragString length:&len];
	if (!len)
		return self;
	path = strtok(dragString, "\t");
	while(path && strlen(path))	{
		pathAtom = NXUniqueString(path);
		if (![nameWinHash isKey:pathAtom])
			[self addWindowForPath:pathAtom];
		path = strtok(NULL, "\t");
	}
	[pb deallocatePasteboardData:dragString length:len];

	if (useSound)
		[[Sound findSoundFor:"Fiend"] play:self];

	return self;
}

- filePositionStore
{
	return filePositionStore;
}

- fileNameStore
{
	return fileNameStore;
}

- launchFlagStore
{
	return launchFlagStore;
}

- nameWinHash
{
	return nameWinHash;
}

- setFrame:(const NXRect *)aFrame
{
	frame = *aFrame;
	return self;
}

@end
