//	MiscHeadedMatrix.m
//Thu Feb 13 13:44:43 GMT+0100 1997

#import	<misckit/MiscHeadedMatrix.h>

#define	DefaultHeight	12.8

@implementation MiscHeadedMatrix

typedef struct {
	float	coord;
	float	extend;
	int		index;
	int		indexExtend;
} VectorCoord;

- (void)setupHeadStorage	//<i> error handling
{	[self setHeadHeight:DefaultHeight];
	hHeadPos=[[Storage alloc] initCount:0 elementSize:sizeof(VectorCoord) description:"ffii"];
	hHeadCells=[[List alloc] init];
}
- (VectorCoord *)rowCoordinatesForIndex:(int)index
{	int	min=0,mid=0,max=[hHeadPos count]-1;
	VectorCoord	*vect=0;

	while (min<=max && ((vect=[hHeadPos elementAt:mid=(min+max)/2])->index>index ||
		(vect->index+vect->indexExtend<=index)))
			if (vect->index<=index) min=mid+1;
				else max=mid-1;
	if (!(vect=[hHeadPos elementAt:mid])->indexExtend)
	{	for (; mid && !(vect=[hHeadPos elementAt:mid])->indexExtend; mid--);	//<o>binary
		if ((vect=[hHeadPos elementAt:mid])->indexExtend) mid++;
	}

	return [hHeadPos elementAt:mid];
}

- initFrame:(const NXRect *)frm mode:(int)aMode prototype:cellId 
			numRows:(int)rowsHigh numCols:(int)colsWide
{	return [self initFrame:frm mode:aMode prototype:cellId numRows:rowsHigh 
		numCols:colsWide headPrototype:nil];
}
- initFrame:(const NXRect *)frm mode:(int)aMode cellClass:factoryId 
			numRows:(int)rowsHigh numCols:(int)colsWide
{	if (!(self=[self initFrame:frm mode:aMode cellClass:factoryId numRows:rowsHigh 
		numCols:colsWide headClass:nil])) return self;
	return self;
}

- initFrame:(const NXRect *)frm mode:(int)aMode prototype:cellId 
			numRows:(int)rowsHigh numCols:(int)colsWide headPrototype:headId
{	if (!(self=[super initFrame:frm mode:aMode prototype:cellId numRows:rowsHigh 
		numCols:colsWide])) return self;

	hHeadPrototype=headId;
	[self setupHeadStorage];
	return self;
}
- initFrame:(const NXRect *)frm mode:(int)aMode cellClass:factoryId 
			numRows:(int)rowsHigh numCols:(int)colsWide headPrototype:headId;
{
	if (!(self=[super initFrame:frm mode:aMode cellClass:factoryId numRows:rowsHigh
		numCols:colsWide])) return self;

	hHeadPrototype=headId;
	[self setupHeadStorage];
	return self;
}
- initFrame:(const NXRect *)frm mode:(int)aMode cellClass:factoryId 
			numRows:(int)rowsHigh numCols:(int)colsWide headClass:headClassId
{
	if (!(self=[super initFrame:frm mode:aMode cellClass:factoryId numRows:rowsHigh
		numCols:colsWide])) return self;

	hHeadCellClass=headClassId;
	[self setupHeadStorage];
	return self;
}

- (void)setHorizHeaderCellClass:aCellClass
{
	hHeadCellClass=aCellClass;
}

- (void)addHorizHeaderWithTitle:(const char *)title
{	Cell		*newCell=0;
	VectorCoord	header,last=(VectorCoord){0,0,0,0};

	if (hHeadPrototype)
	{	newCell=[hHeadPrototype copy];
		[newCell setStringValue:title];
	} else if (hHeadCellClass)
		newCell=[[hHeadCellClass alloc] initTextCell:title];
	//else	NX_RAISE(noCellSource);

	if ([hHeadPos count]) last=*(VectorCoord *)[hHeadPos elementAt:[hHeadPos count]-1];
	header=(VectorCoord){last.coord+last.extend,
		[self headHeight],last.index+last.indexExtend,0};
	[hHeadPos addElement:&header];
	[hHeadCells addObject:newCell];
}
- (void)addRowAtHorizHeader:(int)headNo
{	VectorCoord	*head=[hHeadPos elementAt:headNo];
	int	i,mx;

	[self insertRowAt:head->index+head->indexExtend++];
	for (i=headNo+1, mx=[hHeadPos count]; i<mx; i++)
		((VectorCoord *)[hHeadPos elementAt:i])->index++;
}
- cellAt:(int)aRow :(int)aCol andHorizHeader:(int)headNo
{	VectorCoord	*head=[hHeadPos elementAt:headNo];
	return [self cellAt:head->index+aRow :aCol];
}

- (NXCoord)headHeight
{	return headHeight;
}
- (void)setHeadHeight:(NXCoord)aHeadHeight
{	headHeight=aHeadHeight;
}

- getCellFrame:(NXRect *)theRect at:(int)row:(int)col
{	VectorCoord	*head=[self rowCoordinatesForIndex:row];

	[super getCellFrame:theRect at:row :col];
	if (head) NX_Y(theRect)+=head->coord+head->extend;	
	return self;
}

- drawSelf:(const NXRect *)rects :(int)rectCount
{	int			i,mx;
	VectorCoord	*head;
	NXRect		drawRect,cellFrame;

	[super drawSelf:rects :rectCount];

	NX_X(&drawRect)=0,NX_WIDTH(&drawRect)=NX_WIDTH(&frame);
	for (i=0,mx=[hHeadPos count]; i<mx; i++)	//<i> optimize for rects
	{
		head=[hHeadPos elementAt:i];
		NX_HEIGHT(&drawRect)=head->extend;


		if (head->index)		//<b> in getCellFrame(MiscKit)
		{	[super getCellFrame:&cellFrame at:head->index :0];
			NX_Y(&drawRect)=NX_Y(&cellFrame)+head->coord;
		} else NX_Y(&drawRect)=head->coord;

		[[hHeadCells objectAt:i] drawInside:&drawRect inView:self];
	}
	return self;
}

- getRow:(int *)row andCol:(int *)col forPoint:(const NXPoint *)aPoint
	wantsGuess:(BOOL)shouldGuess
{	NXRect	cellFrame;
	int		index, upper, lower;
	
	*row = *col = -1;
	if ([self cellCount]==0) return NULL;

	if (aPoint->x <= bounds.origin.x)
	{	*col=0;
		if(!shouldGuess) return NULL;
	} else if (aPoint->x >= bounds.origin.x + bounds.size.width)
	{	*col=numCols - 1;
		if (!shouldGuess) return NULL;
	} else {
		lower = 0;
		upper = numCols;
		while (lower < upper)
		{			
			index = (lower + upper) / 2;
			[self getCellFrame:&cellFrame at:0 :index];
			if (aPoint->x < NX_X(&cellFrame))
				upper = index;			// lower, look to the left...
			else if (aPoint->x > (NX_X(&cellFrame) + NX_WIDTH(&cellFrame)))
				lower = index + 1;		// higher, look to the right...
			else {
				*col = index;					// hit it...
				break;
			}
		}
	}
	if(aPoint->y <= bounds.origin.y) {
		*row=0;
		if(!shouldGuess) return NULL;
	} else if(aPoint->y >= bounds.origin.y + bounds.size.height) {
		*row=numRows - 1;
		if (!shouldGuess) return NULL;
	} else {
		lower = 0;
		upper = numRows;
		while (lower < upper) {
			index = (lower + upper) / 2;
			[self getCellFrame:&cellFrame at:index :0];
			if (aPoint->y < NX_Y(&cellFrame))
				upper = index;				// lower, look to the left...
			else if (aPoint->y > ( NX_Y(&cellFrame) +  NX_HEIGHT(&cellFrame)))
				lower = index + 1;			// higher, look to the right...
			else {
				*row = index;				// hit it...
				break;
			}
		}  
	}
	return ((*row == -1) || (*col == -1)) ? nil : self;
}

@end
