// IconWellControl.m
//
// Free software created 1 Feb 1992
// by Paul Burchard <burchard@math.utah.edu>.

#import "IconWellControl.h"
#import "IconWell.h"
#import "DragDrop.h"
#import <appkit/appkit.h>
#import <objc/Storage.h>
#import <objc/HashTable.h>

const char *newIconName = "IconWellControl-tmp";
const char *blankIconName = "Blank";

@implementation IconWellControl

static id wellSpeaker;
static id convertWindowToControl;

+ initialize
{
    if(self == [IconWellControl class])
    {
    	wellSpeaker = [[Speaker alloc] init];
	convertWindowToControl =
	    [[HashTable alloc] initKeyDesc:"@" valueDesc:"@"];
    }
    return self;
}

+ controlFor:theWindow
{
    return (id)[convertWindowToControl valueForKey:(void *)theWindow];
}

- listen:sender
{
    // Tell WorkSpace we want icon-dragging.
    if(isListening) return self;
    if(!window) return nil;
    NXConvertWinNumToGlobal([window windowNum], &globalWindowNum);
    if(![wellSpeaker setSendPort:NXPortFromName(NX_WORKSPACEREQUEST, NULL)])
	return nil;
    if(0 != [wellSpeaker registerWindow:globalWindowNum
	toPort:[wellListener listenPort]])
	return nil;
    [wellListener setDelegate:self];
    isListening = YES;
    return self;
}

- initWindow:theWindow
{
    char portName[1024];
    
    [super init];
    window = theWindow;
    wellListener = [[Listener alloc] init];
    sprintf(portName, "IconWellControlPort-%ld", (long)[self self]);
    if(0 != [wellListener checkInAs:portName]) return nil;
    [wellListener addPort];
    newIconPath = [[Storage alloc] initCount:0 elementSize:sizeof(char) description:"c"];
    [convertWindowToControl insertKey:(void *)theWindow value:(void *)self];
    [self listen:self];
    return self;
}

- free
{
    if(window) [convertWindowToControl removeKey:(void *)window];
    [wellListener free];
    [newIconPath free];
    [newIconImage free];
    return [super free];
}

- (const char *)newIconPath
{
    return (const char *)[newIconPath elementAt:0];
}

- (int)iconEntered:(int)windowNum at:(double)x :(double)y
    iconWindow:(int)iconWindowNum iconX:(double)iconX iconY:(double)iconY
    iconWidth:(double)iconWidth iconHeight:(double)iconHeight
    pathList:(char *)pathList
{
    int i, n;
    id list;
    NXSize size;

    // Create temp NXImage to hold dragged icon.
    // NOTE: newIconImage is only freed beforehand if it still has temp name
    // (i.e. was not used last drag).
    if(!pathList) return 0;
    size.width = size.height = 48.0;
    [[NXImage findImageNamed:newIconName] free];
    newIconImage = [[NXImage alloc] initSize:&size];
    [newIconImage lockFocus];
    copyIconPicture(iconWindowNum,
    	(float) iconX, (float) iconY, (float) iconWidth, (float) iconHeight);
    [newIconImage unlockFocus];
    [newIconImage setName:newIconName];
    
    // Temporarily stash new path.
    [newIconPath setNumSlots:(strlen(pathList)+1)];
    strcpy((char *)[newIconPath elementAt:0], pathList);

    // Accept message, and forward to all IconWells in this Window.
    list = [IconWell wellListFor:window];
    n = [list count];
    for(i=0; i<n; i++)
    	[[list objectAt:i] iconEntered:windowNum at:x :y
	    iconWindow:iconWindowNum iconX:iconX iconY:iconY
	    iconWidth:iconWidth iconHeight:iconHeight
	    pathList:pathList];
    return 0;
}

- (int)iconMovedTo:(double)x :(double)y
{
    int i, n;
    id list;
    
    // Accept message, and forward to all IconWells in this Window.
    list = [IconWell wellListFor:window];
    n = [list count];
    for(i=0; i<n; i++)
    	[[list objectAt:i] iconMovedTo:x :y];
    return 0;
}

- (int)iconExitedAt:(double)x :(double)y
{
    int i, n;
    id list;
    
    // Accept message, and forward to all IconWells in this Window.
    list = [IconWell wellListFor:window];
    n = [list count];
    for(i=0; i<n; i++)
    	[[list objectAt:i] iconExitedAt:x :y];
    
    // Get rid of temp icon and path.
    [[NXImage findImageNamed:newIconName] free];
    newIconImage = nil;
    [newIconPath setNumSlots:0];
    return 0;
}

- (int)iconReleasedAt:(double)x :(double)y ok:(int *)flag
{
    int i, n, accepted;
    id list;
    
    // Accept message, and forward to all IconWells in this Window.
    list = [IconWell wellListFor:window];
    n = [list count];
    for(i=0, *flag=0, accepted=0; i<n; i++, accepted=0)
    {
    	[[list objectAt:i] iconReleasedAt:x :y ok:&accepted];
	if(accepted != 0) *flag = 1;
    }

    // Get rid of temp path.
    // Temp icon image is only freed if it was not accepted.
    [newIconPath setNumSlots:0];
    if(*flag == 0)
    {
	[[NXImage findImageNamed:newIconName] free];
	newIconImage = nil;
    }
    return 0;
}

@end
