#import "Fiend.h"
#import "IconView.h"
#import "ShelfView.h"
#import "Controller.h"

#import <appkit/workspaceRequest.h>
#import <appkit/appkit.h>
#import <mach/mach.h>
#import <bsd/sys/file.h>


#define	IMAGE_OFFSET	2


@implementation NXImage(UsefulMethod)
- (NXSize)scaleToFitInside:(NXSize)size max:(NXSize)maxSize
                     onDock:(BOOL)dockFlag isApp:(BOOL)isApp
{
    NXSize	imageSize;
    float	scale;

    [self getSize:&imageSize];

    scale = MIN(size.width/imageSize.width, size.height/imageSize.height);
	scale *= 0.98;

    size.width = scale * imageSize.width;
    size.height = scale * imageSize.height;
    if (size.width > maxSize.width)
    	size.width = maxSize.width;
    if (size.height > maxSize.height)
    	size.height = maxSize.height;

	size.width = (int)size.width;
	size.height = (int)size.height;

    [self setSize:&size];
    return size;
}
@end


@implementation IconView : View

static id	processTable;
static id	scaledDockHilite;
static id	scaledShelfHilite;
static id	scaledShelfAppHilite;
static id	scaledAppTile;
static id	scaledDotsTile;

+ initialize
{
    scaledDockHilite = scaledShelfHilite = scaledShelfAppHilite = nil;
    [self resetCachedShelfImages];
	[self resetCachedDockImages];
    return self;
}

+ resetCachedShelfImages
{
	[scaledShelfHilite free];
    scaledShelfHilite = [[NXImage findImageNamed:"hilite"] setScalable:YES];

    [scaledShelfAppHilite free];
    scaledShelfAppHilite = [[[NXImage findImageNamed:"hilite"] copy] setScalable:YES];

    return self;
}

+ resetCachedDockImages
{
    [scaledDockHilite free];
    scaledDockHilite = [[[NXImage findImageNamed:"hilite"] copy] setScalable:YES];

	[scaledAppTile free];
	scaledAppTile = [[[NXImage findImageNamed:"NXAppTile"] copy] setScalable:YES];

	[scaledDotsTile free];
	scaledDotsTile = [[NXImage findImageNamed:"dots"] setScalable:YES];

    return self;
}

+ shelfHilite
{
    return scaledShelfHilite;
}

+ dockHilite
{
    return scaledDockHilite;
}

+ shelfAppHilite
{
    return scaledShelfAppHilite;
}

+ getImageForPath:(const char *)path fileIcon:(BOOL)flag
{
	id		newImage = nil;
	const char *const *typeArray = [NXImage imageFileTypes];

	if (!flag)	{
		newImage = nil;
		do	{
			if ((strstr(path, *typeArray) - path) == (strlen(path) - strlen(*typeArray++)))	{
				newImage = [[NXImage alloc] initFromFile:path];
				break;
			}
		}	while (*typeArray != NULL);

		if (newImage != nil)	{
			if ([newImage lockFocus])	{
				[newImage unlockFocus];
				return newImage;
			}
			[newImage free];
			newImage = nil;
		}
	}

	if (newImage == nil)
		newImage = [[Application workspace] getIconForFile:path];

	[newImage setScalable:YES];

	return newImage;
}

+ processTable
{
	return processTable;
}

+ resetProcessTable
{
	int			pid;
	char		junk[25];
	char		appName[100];
	char		inLine[1000];
	NXAtom		atom;
	FILE		*pfile;
	static char	*cmd = "/bin/ps axc";

	if (!processTable)
		processTable = [[HashTable alloc] initKeyDesc:"%" valueDesc:"i"];

	[processTable empty];
	if ((pfile = popen(cmd, "r")) == NULL)
		return nil;
	while(fgets(inLine, 999, pfile) != NULL)	{
		sscanf(inLine, "%d %s %s %s %s", &pid, junk, junk, junk, appName);
		atom = NXUniqueString(appName);
		[processTable insertKey:(const void *)atom value:(void *)pid];
	}
	fclose(pfile);

	return self;
}

+ (int)getLastPidFor:(char *)aName
{
	int		pid;
	NXAtom	atom = NXUniqueString(aName);

	if ([processTable isKey:atom])	{
		pid = (int)[processTable valueForKey:atom];
		return pid;
	}
	else
		return -1;
}

+ (BOOL)pidExists:(int)testPid
{
	const int	value;
	const void	*key;
	NXHashState	state = [processTable initState];

	while ([processTable nextState:&state key:&key value:(void *)&value])	{
		if (testPid == value)
			return YES;
	}
	return NO;
}

/*
 *  Create a copy of an IconView (or an IconView subclass).  Note that this
 *  method cheats by potentially changing the class of the copied image.
 */
+ copyIconView:aView
{
    NXRect		aFrame;
    void		*oldData;
    unsigned int	len;

    [aView getFrame:&aFrame];
    [aView getData:&oldData andLength:&len];

    return [[self allocFromZone:[aView zone]] initFrame:&aFrame
			image:[[aView image] copy]
			data:oldData andLength:len
			useSize:YES onDock:NO];
}

- initFrame:(const NXRect *) newFrame image:anImage	data:(const void *) someData
  andLength:(unsigned int) newLength useSize:(BOOL) sizeValid onDock:(BOOL)dockFlag;
{
	id			hilite;
	int			count;
	char		hostname[40];
	NXScreen	*screens;
    NXSize		imageSize = {400, 400};

	[NXApp getScreens:&screens count:&count];
	screenSize = screens[0].screenBounds.size;

    if (newLength > 0 && *((char *)someData + newLength - 1))
    	length = newLength + 1;
    else
		length = newLength;

    data = (char **)malloc(length+1);
    bcopy(someData, data, length+1);

    ghost = NO;
    selected = NO;
	isLaunched = NO;
	onDock = dockFlag;

	isApp = !strcmp(data+strlen((char *)data)-4, ".app");
	if (isApp)	{
		char	appName[100];
		char	*appFileName = strrchr((char *)data, '/');

		strncpy(appName, appFileName+1, strlen(appFileName)-4);
		appName[strlen(appFileName)-5] = '\0';

		appPid = [IconView getLastPidFor:appName];
		if (appPid != -1)
			isLaunched = YES;
	}

    shelfHilite = [IconView shelfHilite];
    dockHilite = [IconView dockHilite];
	shelfAppHilite = [IconView shelfAppHilite];
	hilite = (onDock) ? dockHilite : ((isApp) ? shelfAppHilite : shelfHilite);

	hiliteMax.width = 400;
	hiliteMax.height = 400;

    image = anImage;
    imageMax = imageSize;

    /*
     *  Allocate a cell, and slam the filename into it.  Make sure to use
     *  only the last component of the path.
     */
	if (!isApp)	{
		titleCell = [[TextFieldCell allocFromZone:[self zone]] init];
		[titleCell setAlignment:NX_CENTERED];
		[titleCell setBackgroundTransparent:YES];

		if (!strcmp(data, "/"))	{
			gethostname(hostname, 39);
			[titleCell setStringValue:hostname];
		}
		else if (rindex(data, '/'))
			[titleCell setStringValue:rindex(data, '/') + 1];
		else
			[titleCell setStringValue:data];
	}

    /*
     *  If there's no frame, make one that's the right size to hold
     *  everything.
     */
    if (!sizeValid) {
    	NXSize	titleSize = {0.0, 0.0};
		NXRect	aRect = {{0, 0}, {0, 0}};

		[hilite getSize:&imageSize];
		if (!isApp)
			[titleCell calcCellSize:&titleSize];

        if (newFrame)
			aRect.origin = newFrame->origin;
		aRect.size.height = imageSize.height + titleSize.height;
		aRect.size.width = MAX(titleSize.width, imageSize.width);
		[super initFrame:&aRect];
    }
    else
		[super initFrame:newFrame];

    [self setImageSize];
    [self getImagePoint:&imagePoint andHilitePoint:&hilitePoint];

    return self;
}

- initFromDragContext:(id <NXDraggingInfo>)context andSize:(NXSize *)aSize onDock:(BOOL)dockFlag;
{
    char			*pbData;
    unsigned int	len;
    NXImage			*copiedImage = [context draggedImageCopy];
    Pasteboard		*pb = [Pasteboard newName:NXDragPboard];
    NXRect			aRect = {{0, 0}, {0, 0}};
    NXRect			*rectPtr = NULL;

    [pb readType:NXFilenamePboardType data:&pbData length:&len];

    if (aSize != NULL) {
    	rectPtr = &aRect;
		aRect.size = *aSize;
    }

    [self initFrame:rectPtr image:copiedImage data:pbData andLength:len+1
	 useSize:aSize != NULL onDock:dockFlag];

	[self display];

    return self;
}


- free
{
    free(data);
    [image free];
	if (!isApp)
		[titleCell free];
    return [super free];
}

- getAppPid
{
	char	appName[100];
	char	*appFileName = strrchr((char *)data, '/');

	strncpy(appName, appFileName+1, strlen(appFileName)-4);
	appName[strlen(appFileName)-5] = '\0';
	appPid = [IconView getLastPidFor:appName];
	return self;
}

- (NXCoord) cellHeight
{
    NXSize	cellSize = {0.0, 0.0};

	if (!isApp)
		[titleCell calcCellSize:&cellSize];

    return cellSize.height;
}


- setImage:anImage
{
	id	tempImage = image;

	image = anImage;
	return tempImage;
}	

- getImagePoint:(NXPoint *)imageLoc andHilitePoint:(NXPoint *)hiliteLoc
{
    NXSize	imageSize;
	id		hilite = (onDock) ? dockHilite : ((isApp) ? shelfAppHilite : shelfHilite);
	NXSize	hiliteSize = {48.0, 48.0};
    NXCoord	cellHeight = 0.0;

	if (!isApp)
		cellHeight = [self cellHeight];

    /*
     *  Determine where the image and cell go in the new view.
     */
    if (imageLoc) {
		[image getSize:&imageSize];
		imageLoc->x = (bounds.size.width - imageSize.width) / 2;
		imageLoc->y = cellHeight + (bounds.size.height - imageSize.height - cellHeight) / 2;
		if (!isApp)
			imageLoc->y -= 4.0;
    }

    if (hiliteLoc && imageLoc) {
		[hilite getSize:&hiliteSize];
		hiliteLoc->x = (bounds.size.width - hiliteSize.width) / 2;
		hiliteLoc->y = imageLoc->y - (hiliteSize.height - imageSize.height) / 2;
    }

    return self;
}


- (void) setImageSize
{
	float	fontSize;
    NXSize	imageSize;
	NXSize	hMax = {416, 416};
	NXSize	iMax = {400, 400};
	BOOL	sizeFont = !strcmp(NXGetDefaultValue([NXApp appName], SIZE_FONT), "YES");
	id		hilite = (onDock) ? dockHilite : ((isApp) ? shelfAppHilite : shelfHilite);

    [image setScalable:YES];
    [hilite setScalable:YES];

    imageSize = bounds.size;
	imageSize = [hilite scaleToFitInside:imageSize max:hMax onDock:onDock isApp:isApp];
	imageSize = [image scaleToFitInside:imageSize max:iMax onDock:onDock isApp:isApp];

	if (!isApp && sizeFont)	{
		fontSize = [[Font userFontOfSize:0 matrix:NX_FLIPPEDMATRIX] pointSize];
		fontSize *= (imageSize.height/48.0);
		fontSize = 2.0 * ((int)fontSize/2);
		fontSize = (fontSize < 8.0) ? 8.0 : fontSize;
		fontSize = (fontSize > 18.0) ? 18.0 : fontSize;
		[titleCell setFont:[Font userFontOfSize:fontSize matrix:NX_FLIPPEDMATRIX]];
	}
}

- (BOOL)isApp
{
	return isApp;
}

- sizeTo:(NXCoord) width :(NXCoord) height
{
    [super sizeTo:width :height];
    [self setImageSize];
    [self getImagePoint:&imagePoint andHilitePoint:&hilitePoint];

    return self;
}


- setDockMgrView:aView
{
	onDock = YES;
	dockMgrView = aView;
	return self;
}

- dockMgrView
{
	return dockMgrView;
}

- drawSelf:(const NXRect *) rects :(int) rectCount
{
    NXRect	cellRect;
	id		hilite = (onDock) ? dockHilite : ((isApp) ? shelfAppHilite : shelfHilite);

	if (onDock)	{
		[scaledAppTile setSize:&bounds.size];
		[scaledAppTile composite:NX_SOVER toPoint:&frame.origin];

		if (autoLaunch)	{
			PSsetlinewidth(0.05*NX_HEIGHT(&frame));
			if ([self shouldDrawColor])
				NXSetColor(NX_COLORRED);
			else
				PSsetgray(NX_BLACK);
			PSmoveto(0.2*NX_WIDTH(&frame), 0.92*NX_HEIGHT(&frame));
			PSrlineto(0.6*NX_WIDTH(&frame), 0.0);
			PSstroke();
			PSsetlinewidth(0.0);
			PSsetgray(NX_BLACK);
		}

		if (isApp && !isLaunched)	{
			[scaledDotsTile setSize:&bounds.size];
			[scaledDotsTile composite:NX_SOVER toPoint:&frame.origin];
		}
	}

	if (!isApp)	{
		if (NXBrightnessComponent(NXReadPixel(&bounds.origin)) < NX_DKGRAY + 0.01)
			[titleCell setTextGray:ghost ? NX_LTGRAY : NX_WHITE];
		else
			[titleCell setTextGray:ghost ? NX_DKGRAY : NX_BLACK];
	}

    if (ghost || (onDock && selected)) {
		id tempImage = [[NXImage alloc] initSize:&bounds.size];
		if (!onDock)	{
			if ([tempImage lockFocus]) {
				PSsetalpha(0);
				NXRectFill(&bounds);
				[image dissolve:0.666 toPoint:&imagePoint];
				[tempImage unlockFocus];
			}
		}
		else	{
			if ([tempImage lockFocus])	{
				[scaledAppTile composite:NX_SOVER toPoint:&frame.origin];
				[image composite:NX_SOVER toPoint:&imagePoint];
				PSsetgray(NX_WHITE);
				PSsetalpha(0.666);
				PScompositerect(2.0, 2.0, NX_WIDTH(&frame)-4.0, NX_HEIGHT(&frame)-4.0, NX_SOVER);
				[tempImage unlockFocus];
			}
		}
		[tempImage composite:NX_SOVER toPoint:&bounds.origin];
		[tempImage free];
    }
    else {
		if (selected)	{
			[hilite composite:NX_SOVER toPoint:&hilitePoint];
		}
		else {
			NXRect	aRect;
			[hilite getSize:&aRect.size];
			aRect.origin = hilitePoint;
			[self convertRect:&aRect toView:superview];
			[superview lockFocus];
			[superview drawSelf:&aRect :1];
			[superview unlockFocus];
		}
		[image composite:NX_SOVER toPoint:&imagePoint];
    }

	if (preDelete)	{
		PSsetlinewidth(5.0);
		if ([self shouldDrawColor])
			NXSetColor(NX_COLORRED);
		else
			PSsetgray(NX_BLACK);
		PSmoveto(0.0, 0.0);
		PSlineto(NX_MAXX(&frame), NX_MAXY(&frame));
		PSmoveto(NX_MAXX(&frame), 0.0);
		PSlineto(0.0, NX_MAXY(&frame));
		PSstroke();
		PSsetgray(NX_BLACK);
	}

	if (!isApp)	{
		NXSetRect(&cellRect, 0, 3 + hilitePoint.y - [self cellHeight],
			  bounds.size.width, [self cellHeight]);
		[titleCell drawInside:&cellRect inView:self];
	}

    return self;
}

- image
{
    return image;
}

- getData:(void **)aPtr andLength:(unsigned int *)aLength
{
    *aPtr = data;
    *aLength = length;
    return self;
}

- setGhost:(BOOL) newGhost
{
    ghost = newGhost;
    return self;
}

- (BOOL) isGhost
{
    return ghost;
}

- setPreDelete:(BOOL)flag
{
	preDelete = flag;
    return self;
}

- (BOOL)preDelete
{
    return preDelete;
}

- setAutoLaunch:(BOOL)flag
{
	autoLaunch = flag;
	return self;
}

- (BOOL)autoLaunch
{
	return autoLaunch;
}

- (int) state
{
    return selected;
}

- setState:(int) flag
{
    if (flag ^ selected) {
		selected = flag ? YES : NO;
    	[self display];
    }
    return self;
}

- (BOOL)onDock
{
	return onDock;
}

- (BOOL)isLaunched
{
	return isLaunched;
}

- setLaunched:(BOOL)flag
{
	isLaunched = flag;
	if (!flag)
		appPid = -1;
	return self;
}

- (int)appPid
{
	return appPid;
}

@end
