
#import "EDBConnectInspector.h"
#import "EDBConnector.h"
#import "ConnectBrowserCell.h"
#import "MColCell.h"
#import "EDBOutlet.h"
#import <assert.h>

#define	NOLDEBUG

static	EDBConnectInspector		*onlyInspector = nil;

static int	sortFunction(id *ob1,id *ob2)
{
	int		ret = strcmp([*ob1 outletName],[*ob2 outletName]);

	if (!ret)
		ret = [*ob1 sequence]-[*ob2 sequence];
	return ret;
}

@implementation EDBConnectInspector

+ obtainEDBConnectInspector
{
	if (onlyInspector != nil)
		return onlyInspector;
	return (onlyInspector=[[[self class] alloc] init]);
}

- init
{
	char			buf [MAXPATHLEN + 1];
	NXBundle		*bundle;
	NXSize			size,size1;
	NXSplitView		*splitView;
	NXRect			splitFrame;
	NXImage			*image;
	MColCell		*mcell;

#ifdef LDEBUG
	fprintf(stderr,"%s init\n",[self name]);
#endif

	if (onlyInspector && [self class]==[EDBConnectInspector class])
	{
		[super free];
		return onlyInspector;
	}

	if (![super init])
		return nil;

    bundle = [NXBundle bundleForClass:[self class]];
    [bundle getPath:buf
            forResource:"EDBConnectInspector"
            ofType:"nib"];
	if (![NXApp loadNibFile:buf owner:self withNames:NO fromZone:[self zone]])
	{
		[super free];
		return nil;
	}

	[[window contentView] getBounds:&splitFrame];
	splitView = [[NXSplitView alloc] initFrame:&splitFrame];
	[splitView setDelegate:self];
	[upperBox removeFromSuperview];
	[lowerBox removeFromSuperview];
	[splitView addSubview:upperBox];
	[splitView addSubview:lowerBox];
	[[window contentView] addSubview:splitView];
	[splitView display];
	[lowerBox getBounds:&splitFrame];
	minHeight = splitFrame.size.height;

	connections = [[ExtList alloc] init];

	mcell = [[MColCell alloc] init];
	[mcell addColWithOffset:0];
	[mcell addColWithOffset:150];
	[connectMatrix setPrototype:mcell];
	[connectMatrix getCellSize:&size];
	size.width = 300;
	[connectMatrix setCellSize:&size];
	[connectMatrix addCol];
	[connectMatrix setMode:NX_RADIOMODE];
	[connectMatrix setEmptySelectionEnabled:YES];

	[outletBrowser setDoubleAction:@selector(doubleClickOutlet:)];
	[outletBrowser setCellClass:[ConnectBrowserCell class]];

	[actionMatrix setTarget:self];
	[actionMatrix setAction:@selector(clickAction:)];
	[actionMatrix setCellClass:[TextFieldCell class]];
	[actionMatrix getCellSize:&size];
	[actionScrollView getContentSize:&size1];
	size.width = size1.width;
	[actionMatrix setCellSize:&size];
	[actionMatrix addCol];
	[actionMatrix sizeToCells];
	[[actionMatrix cellAt:0:0] setSelectable:YES];
	[[actionMatrix cellAt:0:0] setEditable:YES];

    bundle = [NXBundle bundleForClass:[self class]];
    [bundle getPath:buf
            forResource:"EDBdimple_nib"
            ofType:"tiff"];
	image = [[NXImage alloc] initFromFile:buf];
	[image setName:"EDBdimple_nib"];
    [bundle getPath:buf
            forResource:"EDBdimpleH_nib"
            ofType:"tiff"];
	image = [[NXImage alloc] initFromFile:buf];
	[image setName:"EDBdimpleH_nib"];

	outletCount = 0;
	outlets = NULL;

	refreshOnRevert = YES;

	onlyInspector = self;
	return self;
}

- free
{
	[connections free];
	[outlets free];
	if (self == onlyInspector)
		onlyInspector = nil;
	return [super free];
}

- revert:sender
{
#ifdef LDEBUG
	fprintf(stderr,"%s revert:\n",[self name]);
#endif

	if (!refreshOnRevert)
	{
		refreshOnRevert = YES;
		return [super revert:sender];
	}

	outlets = [object getOutlets];
	outletCount = [outlets count];
	[self getConnections:connections];
	[self displayConnections:connections];
	[outletBrowser loadColumnZero];
	[self selectOutlet];

	return [super revert:sender];
}

- ok:sender
{
	NXAtom		outletName = NXUniqueString([[[outletBrowser matrixInColumn:0]
											selectedCell] stringValue]);
	int			count = [connections count];
	int			ind;
	id			connector;
	int			outletNr;
	id			matrix = [outletBrowser matrixInColumn:0];

#ifdef LDEBUG
	fprintf(stderr,"%s ok\n",[self name]);
#endif

	if (![NXApp isConnecting])
		return self;

	outletNr = [[outletBrowser matrixInColumn:0] selectedRow];
	ind = [connectMatrix selectedRow];
	if (ind>=0 && ind<count)		// remove Connection
	{
		connector = [connections objectAt:ind];
		[[NXApp activeDocument] removeConnector:connector];
		[connections removeObjectAt:ind];
		if ([[outlets objectAt:outletNr] multiUse])
			[self removeSequenceNumberOf:connector];
		[connector free];
		[self displayConnections:connections];
		if (![[outlets objectAt:outletNr] multiUse] &&
			![[outlets objectAt:outletNr] isTarget])
		{
			matrix = [outletBrowser matrixInColumn:0];
			[self outletAt:[matrix selectedCell] inMatrix:matrix
				setConnected:NO andDisplay:YES];
		}
		[connectMatrix selectCellAt:-1:-1];
		[okButton setTitle:"Connect"];
	}
	else						// add Connection
	{
		connector = [self createConnectorForOutlet:outletName withNr:outletNr
			andSel:([[outlets objectAt:outletNr] isTarget]) ?
				[[actionMatrix cellAt:0:0] stringValue] : NULL];
#ifdef LDEBUG
	fprintf(stderr,"%s createConnector: source=%lx (%s),dest=%lx (%s),"
		"outlet=%s,sel=%s\n",[self name],(u_long)[NXApp connectSource],
		[[NXApp connectSource] name],(u_long)[NXApp connectDestination],
		[[NXApp connectDestination] name],outletName,[connector selName]);
#endif
		ind = [connections insertOrdered:connector usingFunc:
			(SortFunc *)sortFunction];
		[[NXApp activeDocument] addConnector:connector];
		[self displayConnections:connections];
		if (![[outlets objectAt:outletNr] multiUse] &&
			![[outlets objectAt:outletNr] isTarget])
			[self outletAt:[matrix selectedCell] inMatrix:matrix
				setConnected:YES andDisplay:YES];
		[connectMatrix selectCellAt:ind:0];
		[okButton setTitle:"Disonnect"];
	}

	return self;
}

- createConnectorForOutlet:(NXAtom)outletName withNr:(int)outletNr
	andSel:(const char *)sel
{
	id		connector;

	connector = [[EDBConnector alloc] initWithSource:
		[NXApp connectSource] andDestination:[NXApp connectDestination]
		forOutlet:outletName andSel:sel];
	if ([[outlets objectAt:outletNr] multiUse])
		[self setSequenceNumberFor:connector];

	return connector;
}

- (BOOL)wantsButtons
{
#ifdef LDEBUG
	fprintf(stderr,"%s wantsButtons\n",[self name]);
#endif

	return YES;
}

- outletAt:cell inMatrix:matrix setConnected:(BOOL)flag andDisplay:(BOOL)disp
{
	if (flag)
	{
		[cell setBranchImage:[NXImage findImageNamed:"EDBdimple_nib"]];
		[cell setBranchImageH:[NXImage findImageNamed:"EDBdimpleH_nib"]];
	}
	else
	{
		[cell setBranchImage:nil];
		[cell setBranchImageH:nil];
	}

	if (disp)
		[matrix drawCell:cell];

	return self;
}

- setSequenceNumberFor:connector
{
	NXAtom	outletName = [connector outletName];
	int		ind;
	int		count = [connections count];
	int		maxNum;

	for (ind=maxNum=0; ind<count; ind++)
	{
		if (outletName==[[connections objectAt:ind] outletName] &&
			[[connections objectAt:ind] sequence]>maxNum)
			maxNum = [[connections objectAt:ind] sequence];
	}

	[connector setSequence:maxNum+1];
	return self;
}

- removeSequenceNumberOf:connector
{
	NXAtom	outletName = [connector outletName];
	int		ind;
	int		count = [connections count];
	int		seqNum = [connector sequence];
	int		k;

	for (ind=0; ind<count; ind++)
	{
		if (outletName==[[connections objectAt:ind] outletName] &&
			(k=[[connections objectAt:ind] sequence])>seqNum)
			[[connections objectAt:ind] setSequence:k-1];
	}

	return self;
}

- clickAction:sender
{
#ifdef LDEBUG
	fprintf(stderr,"%s clickAction:\n",[self name]);
#endif

	[okButton performClick:self];
    return self;
}

- clickConnection:sender
{
	int		count = [connections count];
	int		ind = [sender selectedRow];
	int		outletInd;

#ifdef LDEBUG
	fprintf(stderr,"%s clickConnection:\n",[self name]);
#endif

	assert(ind<count);

	[NXApp displayConnectionBetween:[[connections objectAt:ind] source] and:
		[[connections objectAt:ind] destination]];
	refreshOnRevert = NO;
	outletInd = [self indexOfOutlet:[[connections objectAt:ind] outletName]];
	[[outletBrowser matrixInColumn:0] selectCellAt:outletInd:0];
	[self fillTargetMatrixForConnection:[connections objectAt:ind]
		andOutlet:outletInd];
	[okButton setEnabled:YES];
	[okButton setTitle:"Disconnect"];

	return self;
}

- fillTargetMatrixForConnection:connection andOutlet:(int)outletInd
{
	if (outletInd>=0 && outletInd<outletCount &&
		[[outlets objectAt:outletInd] isTarget])
	{
		[[actionMatrix cellAt:0:0] setStringValue:[connection selName]];
		[actionMatrix setEnabled:YES];
		[actionMatrix selectCellAt:0:0];
	}
	else
	{
		[[actionMatrix cellAt:0:0] setStringValueNoCopy:""];
		[actionMatrix setEnabled:NO];
	}

	return self;
}

- clickOutlet:sender
{
	NXAtom		outletName = NXUniqueString([[[sender matrixInColumn:0]
											selectedCell] stringValue]);
	int			count = [connections count];
	int			ind;
	int			outletNr;

#ifdef LDEBUG
	fprintf(stderr,"%s clickOutlet:\n",[self name]);
#endif

	outletNr = [[sender matrixInColumn:0] selectedRow];
	for (ind=0; ind<count &&
		[[connections objectAt:ind] outletName]!=outletName; ind++);
	if (![[outlets objectAt:outletNr] multiUse] && ind<count)
	{
		[NXApp displayConnectionBetween:[[connections objectAt:ind] source]
			and:[[connections objectAt:ind] destination]];
		refreshOnRevert = NO;
		[okButton setTitle:"Disconnect"];
		[okButton setEnabled:YES];
		[connectMatrix selectCellAt:ind:0];
		[self fillTargetMatrixForConnection:[connections objectAt:ind]
			andOutlet:outletNr];
	}
	else
	{
		[okButton setTitle:"Connect"];
		[okButton setEnabled:[NXApp isConnecting]];
		[connectMatrix selectCellAt:-1:-1];
		[self fillTargetMatrixForConnection:nil andOutlet:outletNr];
	}

	return self;
}

- doubleClickOutlet:sender
{
#ifdef LDEBUG
	fprintf(stderr,"%s doubleClickOutlet:\n",[self name]);
#endif

	[okButton performClick:self];
	return self;
}

- getConnections:list
{
#ifdef LDEBUG
	fprintf(stderr,"%s getConnections:\n",[self name]);
#endif

	[[NXApp activeDocument] listConnectors:list forSource:object];
	[list sortUsingFunc:(SortFunc *)sortFunction];

	return self;
}

- displayConnections:list
{
	int			count = [list count];
	int			rows,cols;
	char		name	[256];
	const char	*p;
	int			outletInd;

#ifdef LDEBUG
	fprintf(stderr,"%s displayConnections\n",[self name]);
#endif

	[connectMatrix getNumRows:&rows numCols:&cols];
	for (rows--; rows>=0; rows--)
		[connectMatrix removeRowAt:rows andFree:YES];

	for (rows=0; rows<count; rows++)
	{
		[connectMatrix addRow];
		outletInd = [self indexOfOutlet:[[list objectAt:rows] outletName]];
		if (outletInd>=0 && [[outlets objectAt:outletInd] multiUse])
		{
			sprintf(name,"%s-%03d",
				[[outlets objectAt:outletInd] outletName],
				[[list objectAt:rows] sequence]);
			[[connectMatrix cellAt:rows:0] setStringValue:name
				at:0 copy:YES];
		}
		else
			[[connectMatrix cellAt:rows:0] setStringValue:
				[[list objectAt:rows] outletName] at:0 copy:NO];
		[[NXApp activeDocument] getNameIn:name
			for:[[list objectAt:rows] destination]];
		if (name[0] == '\0')
		{
			p = [[[list objectAt:rows] destination] name];
			if (p == NULL)
				p = "First Responder";
			[[connectMatrix cellAt:rows:0] setStringValue:p at:1 copy:YES];
		}
		else
			[[connectMatrix cellAt:rows:0] setStringValue:name at:1 copy:YES];
	}

	[connectMatrix sizeToCells];
	[connectScrollView display];

	return self;
}

- selectOutlet
{
	int			count = [connections count];
	int			ind;
	id			dest = [NXApp connectDestination];
	int			outletNr;

	[revertButton setEnabled:NO];

	if ([NXApp isConnecting])
	{
		[okButton setEnabled:YES];
		for (ind=0; ind<count &&
			[[connections objectAt:ind] destination]!=dest; ind++);
		if (ind < count)			// Connection line for existing Connection
		{
			outletNr = [self indexOfOutlet:[[connections objectAt:ind]
				outletName]];
			[[outletBrowser matrixInColumn:0] selectCellAt:outletNr:0];
			[connectMatrix selectCellAt:ind:0];
			[okButton setTitle:"Disconnect"];
			[self fillTargetMatrixForConnection:[connections objectAt:ind]
				andOutlet:outletNr];
		}
		else						// Select next free outlet
		{
			outletNr = [self indexOfFirstFreeOutlet];
			[[outletBrowser matrixInColumn:0] selectCellAt:outletNr:0];
			[connectMatrix selectCellAt:-1:-1];
			[okButton setTitle:"Connect"];
			[self fillTargetMatrixForConnection:nil andOutlet:outletNr];
		}
	}
	else
	{
		[okButton setEnabled:NO];
		[okButton setTitle:"Connect"];
		[self fillTargetMatrixForConnection:nil andOutlet:-1];
	}

	return self;
}

- (int)indexOfOutlet:(NXAtom)name
{
	int				ind;

#ifdef LDEBUG
	fprintf(stderr,"%s indexOfOutlet:%s\n",[self name],name);
#endif

	for (ind=0; ind<outletCount; ind++)
	{
		if ([[outlets objectAt:ind] outletName] == name)
			return ind;
	}

	return -1;
}

- (int)indexOfFirstFreeOutlet
{
	int				ind;
	int				indConns;
	int				countConns = [connections count];

#ifdef LDEBUG
	fprintf(stderr,"%s indexOfFirstFreeOutlet\n",[self name]);
#endif

	for (ind=0; ind<outletCount; ind++)
	{
		if ([[outlets objectAt:ind] multiUse])
			return ind;
		for (indConns=0; indConns<countConns &&
			[[connections objectAt:indConns] outletName]!=
			[[outlets objectAt:ind] outletName]; indConns++);
		if (indConns >= countConns)
			return ind;
	}

	return -1;
}

- (BOOL)outletIsConnected:(int)ind
{
	int				indConns;
	int				countConns = [connections count];

#ifdef LDEBUG
	fprintf(stderr,"%s outletIsConnected\n",[self name]);
#endif

	if ([[outlets objectAt:ind] multiUse])
		return NO;

	for (indConns=0; indConns<countConns &&
		[[connections objectAt:indConns] outletName]!=
		[[outlets objectAt:ind] outletName]; indConns++);
	if (indConns >= countConns)
		return NO;

	return YES;
}

- outletAddedToObject:sender
{
	if (object != sender)
		return self;

	[self revert:self];
	return self;
}

- outlet:(NXAtom)name removedFromObject:sender
{
	List	*conns;
	int		count,
			ind;
	id		connector;

	if (object != sender)
		return self;

	conns = [[List alloc] initCount:[connections count]];
	[[NXApp activeDocument] listConnectors:conns forSource:object];
	count = [conns count];
	for (ind=0; ind<count; ind++)
	{
		connector = [conns objectAt:ind];
		if ([connector outletName] == name)
		{
			[[NXApp activeDocument] removeConnector:connector];
			[connector free];
		}
	}

	[self revert:self];
	[conns free];
	return self;
}

- outletOfObject:sender renamedFrom:(NXAtom)from to:(NXAtom)to
{
	List	*conns;
	int		count,
			ind;
	id		connector;

	if (object != sender)
		return self;

	conns = [[List alloc] initCount:[connections count]];
	[[NXApp activeDocument] listConnectors:conns forSource:object];
	count = [conns count];
	for (ind=0; ind<count; ind++)
	{
		connector = [conns objectAt:ind];
		if ([connector outletName] == from)
			[connector setOutletName:to];
	}

	[self revert:self];
	[conns free];
	return self;
}

- (int)browser:sender fillMatrix:matrix inColumn:(int)column
{
	int				indOuts;
	int				countConns = [connections count];
	int				indConns;

#ifdef LDEBUG
	fprintf(stderr,"%s browser:fillMatrix:inColumn:%d\n",[self name],column);
#endif

	for (indOuts=0; indOuts<outletCount; indOuts++)
	{
		[matrix addRow];
		[[matrix cellAt:indOuts:0] setStringValueNoCopy:
			[[outlets objectAt:indOuts] outletName]];
		[[matrix cellAt:indOuts:0] setLoaded:YES];
		[[matrix cellAt:indOuts:0] setLeaf:YES];
		if ([[outlets objectAt:indOuts] isTarget])
		{
			[[matrix cellAt:indOuts:0] setBranchImage:
				[NXImage findImageNamed:"NXmenuArrow"]];
			[[matrix cellAt:indOuts:0] setBranchImageH:
				[NXImage findImageNamed:"NXmenuArrowH"]];
		}
		for (indConns=0; indConns<countConns &&
			[[connections objectAt:indConns] outletName]!=
				[[outlets objectAt:indOuts] outletName];
			indConns++);
		if (![[outlets objectAt:indOuts] multiUse] && indConns<countConns)
		{
			if (![[outlets objectAt:indOuts] isTarget])
				[self outletAt:[matrix cellAt:indOuts:0] inMatrix:matrix
					setConnected:YES andDisplay:NO];
			else
			{
				[[actionMatrix cellAt:0:0] setStringValue:
					[[connections objectAt:indConns] selName]];
				[actionMatrix selectTextAt:0:0];
			}
		}
	}

	[matrix sizeToCells];
	return outletCount;
}

- splitView:sender getMinY:(NXCoord *)minY maxY:(NXCoord *)maxY
  ofSubviewAt:(int)offset
{
   NXRect    rect;
   
	offset = 0; 
	[sender getBounds:&rect];
    *minY = minHeight;
    *maxY = rect.size.height - minHeight;
	if (*maxY < minHeight)
		*maxY = minHeight;

    return self;
}

@end
