
//	Copyright H. Giesen, University of Koblenz-Landau 1996


#import "Controller.h"
#import "BrowserController.h"
#import "FindClass.h"
#import "MyBrowserCell.h"
#import "TeXClass.h"
#import "DragView.h"
#import "BibTexView.h"
#import "BibliographicFile.h"
#import "BibTeXObject.h"

	

@implementation FindClass


//	returns the list of all found objects

- findString:(const char *)pattern inFile:(BibliographicFile *)aFile
		ignoreCase:(BOOL)ignoreCaseflag  tolerance:(int)tol
{
	unsigned int charVector[256];	// characteristic vector
	unsigned int bit[32], bit_0, endPos, wildCard;
	unsigned int findSet, findSet1;
	NXStream	*memStream;
	int i, j, patLen;
	int len;

	if( listOfFoundObjects==nil ){
		listOfFoundObjects = [[List alloc] init];
	}
	[listOfFoundObjects empty];
	
	for( i=0; i<32; i++ ) bit[i] = 1<<(32-1-i);
	bit_0 = bit[0];

	patLen = strlen( pattern );
	if( patLen>32 ) return nil;
	endPos = bit[ patLen-1 ];
  
	wildCard = 0;
	for( j=0; j<patLen; j++ ){	// are there wildcards (only '?' allowed)
		if( pattern[j]=='?' ) wildCard |= bit[j];
	}

	for( i=0; i<256; i++ ){	// define characteristic vector
		charVector[i] = wildCard;	// set is empty
	}

	for( j=0; j<patLen; j++ )
			charVector[ (unsigned int)pattern[j] ] |= bit[j];

	if( ignoreCaseflag==YES ){
		for( i='A'; i<='Z'; i++ ){
			unsigned int cv;
			cv = charVector[i] | charVector[i-'A'+'a'];
			charVector[i] = charVector[i-'A'+'a'] = cv;
		}
	}

	len = [aFile fileSize];
	findSet = findSet1 = 0;

	memStream = [aFile stream];
	NXSeek( memStream, 0, NX_FROMSTART );
	lastSearchPos=0;

	for( j=1; j<=len; j++ ){
		unsigned int cv, theChar, h, w=findSet;

		theChar = NXGetc( memStream );
		lastSearchPos++;
		if( theChar==EOF ){
			lastSearchPos=0;
			return listOfFoundObjects;
		}
		cv = charVector[ theChar ];
		h = (findSet>>1) | bit_0;
		findSet = h & cv;

		if( tol )	// one error is allowed !!
			findSet1 = ((findSet1>>1) & cv) | h | (findSet>>1) | w;
		else 	findSet1 = findSet;

		if( findSet1 & endPos ){
			id	obj;
			//[theText setSel:lastSearchPos-patLen :lastSearchPos]; // test
			//[theText scrollSelToVisible];						// test
			obj = [aFile objectAtCharpos:lastSearchPos-patLen
					andSelectInMatrix:nil];
			if( obj ){
				if( obj!=[listOfFoundObjects lastObject] )
					[listOfFoundObjects addObject:obj];
			}
		}
    }
	// we will never reach this part
	lastSearchPos = -1;
	return listOfFoundObjects;
}



- init
{
	[super init];
	inspector = [BibTexView new];
	findPBoard = [Pasteboard newName:NXFindPboard];
	[preViewButton setEnabled:NO];
	[selectButton setEnabled:NO];
	return self;
}



// aList is a list of matrixcells of class MyBrowserCell

- (void)deleteEntries:(List *)aList
{
	int		i, j, rows, cols;
	char	buffer[64];

	if( [listOfFoundPatterns count]==0 ) return;

	if( findMatrix==nil ) return;

	for( j=0; j<[aList count]; j++ ){
		id	delObj = [[aList objectAt:j] object];	// entry_object to delete
		[findMatrix getNumRows:&rows numCols:&cols];
		for( i=rows-1; i>=0; i-- ){
			id	anObj = [[findMatrix cellAt:i :0] object];
			if( anObj==delObj ){	// both are of class BibTexObject
				[findMatrix removeRowAt:i andFree:NO];
				[listOfFoundPatterns removeObject:delObj];
				if( lastFindObject==delObj ) lastFindObject=nil;
				continue;
			}
		}
	}
	[findMatrix sizeToCells];
	[listOfSelectedPatterns free];
	listOfSelectedPatterns = [findMatrix getSelectedCells:nil];
	sprintf( buffer, "%d selected", [listOfSelectedPatterns count] );
	[selectedIconText setStringValue:buffer];

	if( [listOfSelectedPatterns count]>0 ){
		[preViewButton setEnabled:YES];
		[selectButton setEnabled:YES];
		[findIcon setList:listOfSelectedPatterns];
	}
	else{
		[preViewButton setEnabled:NO];
		[selectButton setEnabled:NO];
		[findIcon setList:nil];
	}
	[[findMatrix window] display];
}


- find:sender
{
	NXRect frame = {{0.0, 0.0},{100.0, 100.0}};

	if( !findMatrix ){
		findMatrix = [[Matrix alloc] initFrame:&frame
					mode:NX_LISTMODE
					cellClass:[MyBrowserCell class]
					numRows:0 numCols:1];
		[findMatrix setCellBackgroundGray:NX_LTGRAY];
		[findMatrix setBackgroundGray:NX_LTGRAY];
		[findMatrix setAutoscroll:YES];
		[findScroller setBackgroundGray:NX_LTGRAY];
		[[findScroller setDocView:findMatrix] free];
		[[findMatrix window]setFrameUsingName:"FindPanel"];
		[[findMatrix window] setFrameAutosaveName:"FindPanel"];
		[selectButton setEnabled:NO];
		preViewPushDown =
			[[[NXApp delegate]texObject] buildPreviewMenuFor:preViewButton];
		[preViewButton setEnabled:NO];
	}

	[[findScroller window] makeKeyAndOrderFront:self];
	return self;
}


- (void)showFindList
{
	int		i;
	NXRect	frame = {{0.0, 0.0},{100.0, 100.0}};
	NXSize	theSize;
	char	keyBuffer[128];
	char	buffer[512];

	if( findMatrix ){
		[findMatrix renewRows:0 cols:0];
		[findMatrix sizeToCells];
	}
	else{
		findMatrix = [[Matrix alloc] initFrame:&frame
					mode:NX_LISTMODE
					cellClass:[MyBrowserCell class]
					numRows:0 numCols:1];
		[findMatrix setCellBackgroundGray:NX_LTGRAY];
		[findMatrix setBackgroundGray:NX_LTGRAY];
		[findMatrix setAutoscroll:YES];
		[findScroller setBackgroundGray:NX_LTGRAY];
		[[findScroller setDocView:findMatrix] free];
	}

	[findScroller getFrame:&frame];
	[findScroller getContentSize:&frame.size];
	
	[findMatrix setAction:@selector(singleClickInFindMatrix:)];
	[findMatrix getCellSize:&theSize];
	[findMatrix setAutosizeCells:YES];	// tut nicht
	theSize.width = frame.W;
	[findMatrix setCellSize:&theSize];
	
	for( i=0; i<[listOfFoundPatterns count]; i++ ){
		MyBrowserCell	 *newCell;
		BibTeXObject *obj = [listOfFoundPatterns objectAt:i];
		BibliographicFile	*fileObj = [obj fileObject];

		if( i<0 ){	// TEST
			[findMatrix addRow];
			newCell = [findMatrix cellAt:i :0];
			[newCell setFont:[Font newFont:"Courier" // tut nicht
							size:14.0
							style:0
							matrix:NX_IDENTITYMATRIX]];
			sprintf( buffer, "    file : %s", [fileObj fileName] );
			[newCell setStringValue:buffer];
			[newCell setObject:obj inFile:fileObj];
		}
		
		[findMatrix addRow];
		newCell = [findMatrix cellAt:i :0];
		[newCell setLeaf:YES];	// is NXBrowerCell !!
		[obj copyRange:[obj key] toBuffer:keyBuffer];
		sprintf( buffer, "%s : %s", [fileObj fileName], keyBuffer );
		[newCell setStringValue:buffer];
		[newCell setObject:obj inFile:fileObj];
	}
	
	[findMatrix selectCellAt:-1 :-1];
	[[[findMatrix sizeToCells] window] display];
	
	sprintf( buffer, "%d found", [listOfFoundPatterns count] );
	[findIconText setStringValue:buffer];
	[selectedIconText setStringValue:""];
	
	[findIcon setList:nil];
	[findIcon display];

	[preViewButton setEnabled:NO];
	[selectButton setEnabled:NO];
}


- (void)setFindText
{
	char	*data;
	int		length;
	const	NXAtom *array;

	array = [findPBoard types];
	if( *array && (changeCount!=[findPBoard changeCount]) ){
		if( [findPBoard readType:*array data:&data length:&length] ){
			[searchTextField setStringValue:data];
		}
		changeCount = [findPBoard changeCount];
	}
	[searchTextField selectText:self];
	textDidChange = NO;
}


- (void)convert:(const char *)txt to:(char *)buffer length:(int)len
{
	int	i = 0, j = 0;
	unsigned char c, tc;

	while( (c = txt[i++]) && (i<len) ){
		if( c>=0x80 ){
			const char *texText = [BibTexParser NX2TeX][ c-0x80 ];
			while( tc = *texText++ ) buffer[j++] = tc;
		}
		else{
			buffer[j++] = c;
		}
	}
	buffer[j] = '\0';
}


//	objects in selList are of class MyBrowserCell
- (void)findStringIn:(List *)selList
{
	int	index;
	BibliographicFile	*fileObj;
	List	*rtnList;
	char	searchbuffer[1024];

	lastFindObject = nil;
	lastFindFile = nil;

	if( !listOfFoundPatterns )
			listOfFoundPatterns = [[List alloc] init];
	if( ![addButton state] )[listOfFoundPatterns empty];
	
	for( index=0; index<[selList count]; index++ ){
		fileObj = [[selList objectAt:index] fileObject];
		// copy (and convert search-string
		[self convert:
			[searchTextField stringValue] to:searchbuffer length:1024];
		//rtnList = [fileObj findString:searchbuffer
		rtnList = [self findString:searchbuffer inFile:fileObj
			ignoreCase:[ignoreCase state] tolerance:[allowErrors state]];
		[listOfFoundPatterns appendList:rtnList];
	
		// check for multiple entries
		// (not yet implemented)
	}

	[self showFindList];
}


//	called from menu Edit -> Find -> Find Next
- findString:sender
{
	BrowserController	*browser = [[NXApp delegate] browserController];
	Matrix	*matrix  = [browser matrixInColumn:0];
	List	*selList = [matrix getSelectedCells:nil];

	[self find:self];
	if( textDidChange ){
		// write to NXFindPboard
		[findPBoard declareTypes:&NXAsciiPboardType num:1 owner:nil];
		[findPBoard writeType:NXAsciiPboardType
			data:[searchTextField stringValue] 
			length:strlen([searchTextField stringValue])];
		textDidChange = NO;
	}
	[searchTextField selectText:self];
	[self findStringIn:selList];
	[selList free];

	return self;
}


- singleClickInFindMatrix:sender
{
	MyBrowserCell	*cell = [sender selectedCell];
	char	buffer[64];
	
	
	[[NXApp delegate] P:self];
	[listOfSelectedPatterns free];
	listOfSelectedPatterns = [sender getSelectedCells:nil];
	if( [listOfSelectedPatterns count]==1 ){
		[inspector setEntry:(lastFindObject = [cell object])
				inFile:(lastFindFile = [cell fileObject]) ];
	}
	else {
		[inspector clear:self];
		lastFindObject = nil;
		lastFindFile = nil;
	}
	[findIcon setList:listOfSelectedPatterns];
	sprintf( buffer, "%d selected", [listOfSelectedPatterns count] );
	[selectedIconText setStringValue:buffer];

	if( [listOfSelectedPatterns count] ){
		[preViewButton setEnabled:YES];
		[selectButton setEnabled:YES];
	}
	else{
		[preViewButton setEnabled:NO];
		[selectButton setEnabled:NO];
	}
	[findIcon display];
	[[NXApp delegate] V:self];
	return self;
}


- (void)deleteFile:(BibliographicFile *)theFile
{
	int		i;
	int		row, col;
	BOOL	needsRedraw = NO;

	if( !findMatrix ) return;
	[findMatrix  getNumRows:&row numCols:&col];
	for( i=row-1; i>=0; i-- ){
		id	cell = [findMatrix cellAt:i :0];
		if( [cell fileObject] == theFile ){
			if( theFile==lastFindFile ){
				lastFindObject = nil;
				lastFindFile = nil;
			}
			[listOfFoundPatterns removeObject:[cell object]];
			needsRedraw = YES;
		}
	}
	if( needsRedraw==YES ) [self showFindList];
}


- prepareLaTeX:(int)tag
{
	[[NXApp delegate] LaTeXFromList:[findIcon getList] tag:tag];
	return self;
}


- LaTeX:sender
{
	[[NXApp delegate] LaTeXFromList:[findIcon getList] tag:RUNLATEX];
	return self;
}


- selectInFile:sender	// sender is Button
{
	BrowserController	*browser = [[NXApp delegate] browserController];
	List	*selList = [findMatrix getSelectedCells:nil];
	int		i;

	//	the selection list contains objects of class MyBrowserCell
	for( i=0; i<[selList count]; i++ ){
		[selList replaceObjectAt:i with:[[selList objectAt:i] object]];
	}
	//	the selection list contains now objects of class BibTeXObject
	[browser selectEntryObjects:selList];
	[selList free];
	return self;
}


// copy selected entries to copyPasteBoard

- copyCitationForEdit
{
	[findIcon copyCitation:self];
	return self;

}


- windowDidBecomeKey:sender
{
	BrowserController	*browser = [[NXApp delegate] browserController];
	Cell				*selCell = [[browser matrixInColumn:0] selectedCell];

	if( [[NXApp delegate] semaphore:self] <= 0 ) return self;

	[[NXApp delegate] P:self];

	if( lastFindObject==nil ) [inspector clear:self];
	else
	[inspector setEntry:lastFindObject inFile:lastFindFile ];

	[[NXApp delegate] V:self];
	[findButton setIconPosition:NX_ICONRIGHT];
	[findButton setEnabled:(selCell!=nil)];
	[self setFindText];
	[sender flushWindow];
	return self;
}


- windowDidResignKey:sender
{
	[sender endEditingFor:nil];
	//[self setFindText];
	[findButton setIconPosition:NX_TITLEONLY];
	[findButton display];
	return self;
}


//	returns the new state of the menuCell
	// YES = enabled
	//	NO = disabled

- (BOOL)updateMenuFor:menuCell
{
	BOOL	newState=NO;	// .. and the compiler keeps silent

	switch( [menuCell tag] ){
		case CUT		: newState = YES; break;
		case COPY		: newState = YES; break;
		case PASTE		: newState = YES; break;
		case SELECTALL	: newState = YES; break;
		case ENTERSELECTION : newState = NO; break;
		case FINDNEXT   : newState = [findButton isEnabled]; break;

		case PRVWINDOW	: newState = YES; break;
		case COPYCITE	:
		case RUNLATEX	:
		case RUNRTF		:
		case RUNHTML	: newState = [[findIcon getList] count]>0;
						  break;
		case PRINT		: newState = [menuCell isEnabled];
						  break;
		default : newState = [[NXApp delegate]updateMenuFor:menuCell];
				  break;
		}

	return newState;
}


- textDidGetKeys:sender isEmpty:(BOOL)flag
{
	textDidChange = YES;
	return self;
}



@end
