#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
{
    NXSize	imageSize;
    float	scale;

    [self getSize:&imageSize];

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

    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;
 
    [self setSize:&size];
    return size;
}
@end


@implementation IconView : View

static id	scaledHilite;

+ initialize
{
    scaledHilite = nil;
    [self resetCachedImages];
    return self;
}

+ resetCachedImages
{
    [scaledHilite free];
    scaledHilite = [[NXImage findImageNamed:"hilite"]
    			copyFromZone:[self zone]];
    return self;
}

+ hilite
{
    return scaledHilite;
}


/*
 *  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];
}


/*
 *  Initialize a new IconView.  The only thing sneaky about this
 *  method is that we may interpret the size of the frame as the
 *  max size for the frame.
 */
- initFrame:(const NXRect *) newFrame
		image:anImage
		data:(const void *) someData
		andLength:(unsigned int) newLength
		useSize:(BOOL) sizeValid
{
    NXSize	imageSize = {48, 48};

    selected = NO;
    ghost = NO;
  
    hilite = [IconView hilite];
    [hilite getSize:&hiliteMax];

    image = anImage;
    imageMax = imageSize;

    /*
     *  Copy the data, with null termination if it's not already.
     */
    if (newLength > 0 && *((char *)someData + newLength - 1))
    	length = newLength + 1;
    else
	length = newLength;

    data = NXZoneMalloc([self zone], length);
    bcopy(someData, data, length);
    *((char *)data + length - 1) = '\0';

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

    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;
	NXRect	aRect = { {0,0}, {0,0} };

	[hilite getSize:&imageSize];
	[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];

    return self;
}


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

    [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];

    return self;
}


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


- (NXCoord) cellHeight
{
    NXSize	cellSize;
    [titleCell calcCellSize:&cellSize];
    return cellSize.height;
}


- getImagePoint:(NXPoint *) imageLoc andHilitePoint:(NXPoint *) hiliteLoc
{
    NXCoord	cellHeight;
    NXSize	hiliteSize, imageSize;

    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 (hiliteLoc) {
	[hilite getSize:&hiliteSize];
	hiliteLoc->x = (bounds.size.width - hiliteSize.width) / 2;
	hiliteLoc->y = imageLoc->y -
			(hiliteSize.height - imageSize.height) / 2;
    }

    return self;
}


- (void) setImageSize
{
    NXSize	imageSize;

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

    imageSize = bounds.size;
    imageSize.height -= [self cellHeight];

    imageSize = [hilite scaleToFitInside:imageSize max:hiliteMax];
    (void) [image scaleToFitInside:imageSize max:imageMax];
}


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


- drawSelf:(const NXRect *) rects :(int) rectCount
{
    NXPoint	imagePoint, hilitePoint;
    NXRect	cellRect;

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

    if (NXBrightnessComponent([superview backgroundColor]) < NX_DKGRAY + 0.01)
	[titleCell setTextGray:[self isGhost] ? NX_LTGRAY : NX_WHITE];
    else
	[titleCell setTextGray:[self isGhost] ? NX_DKGRAY : NX_BLACK];

    NXSetRect(&cellRect, 0, hilitePoint.y - [self cellHeight],
    			    bounds.size.width, [self cellHeight]);
    [titleCell drawInside:&cellRect inView:self];

    if ([self isGhost]) {
        id tempImage = [[NXImage alloc] initSize:&bounds.size];
	if ([tempImage lockFocus]) {
	     PSsetalpha(0);
	     NXRectFill(&bounds);
	     [image dissolve:0.6666 toPoint:&imagePoint];
	     [tempImage unlockFocus];
	}
	[tempImage composite:NX_SOVER toPoint:&bounds.origin];
	[tempImage free];
    }
    else {
	if ([self state])
	    [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];
    }
    
    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;
}


- (int) state
{
    return selected;
}


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

@end
