/* Generated by Interface Builder */

#import "FindManager.h"
#import <objc/Object.h>
#import <appkit/Application.h>
#import <appkit/Panel.h>
#import <appkit/Form.h>
#import <appkit/Text.h>
#import <appkit/Control.h>
#import <appkit/Button.h>
#import <streams/streams.h>

#import <string.h>
#import <stdlib.h>
#import <ctype.h>
#import "regex.h"

static id instance = nil;

static char upcase[0400] =
{000, 001, 002, 003, 004, 005, 006, 007,
 010, 011, 012, 013, 014, 015, 016, 017,
 020, 021, 022, 023, 024, 025, 026, 027,
 030, 031, 032, 033, 034, 035, 036, 037,
 040, 041, 042, 043, 044, 045, 046, 047,
 050, 051, 052, 053, 054, 055, 056, 057,
 060, 061, 062, 063, 064, 065, 066, 067,
 070, 071, 072, 073, 074, 075, 076, 077,
 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137,
 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177,
 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
};

int search(const char *match, char *text, int start, int most, int matchCase);

@implementation FindManager

+ new
{
	if((instance != nil) && [instance isKindOf:[FindManager class]])
		return instance;
	else
	 	return (instance = [[FindManager alloc] init]);
}

- init
{
	if(instance != nil)
		return nil;
	self = [super init];
	instance = self;	
	[NXApp loadNibSection:"FindManager.nib" owner:self withNames:NO];
	
	countType = line;
	extent = all;
	regex = NO;
	matchCase = NO;
	re_set_syntax(RE_NO_BK_PARENS | RE_NO_BK_VBAR | RE_CONTEXT_INDEP_OPS);
	return self;
}

- textDidEnd:textObject endChar:(unsigned short)whyEnd
{
	free(currentText);
	currentText = NXCopyStringBuffer([findView stringValue]);

	return self;
}

- (char *)currentText
{
	return currentText;
}

- checkResponder
{
	id fr = [[NXApp mainWindow] firstResponder];
	
	if([fr isKindOf:[Text class]])
		return fr;
	else
		return nil;
}

- orderFrontFindPanel:sender
{
	[findPanel makeKeyAndOrderFront:self];
	[findView setStringValue:currentText];
	[findView selectTextAt:0];
	return self;
}

- orderFrontCountPanel:sender
{
	id text;
	[countPanel makeKeyAndOrderFront:self];
	
	if((text =[self checkResponder]) == nil)
		return self;
	else
	{	
		NXSelPt start, end;
		char buf[20];
		
		[text getSel:&start :&end];
		
		switch(countType)
		{
			case line :
			{
				sprintf(buf, "%d", [text lineFromPosition:start.c1st]);
				if(start.c1st != end.c1st)
				{
					sprintf((buf + strlen(buf)), ":%d",
						[text lineFromPosition:end.c1st]);
				}
				break;
			}
			case character :
			{
				sprintf(buf, "%d", start.cp);
				if(start.cp != end.cp)
				{
					sprintf((buf + strlen(buf)), ":%d", end.cp);
				}
				break;
			}
		}
		[countView setStringValue:buf];
		[countView selectText:self];
	}

	return self;
}

- enterSelection:sender
{
	id text;
	NXSelPt start, end;
	
	if((text = [self checkResponder]) == nil)
		return self;

	[text getSel:&start :&end];
	if(start.cp != end.cp)
	{
		currentText = (char *)realloc(currentText, end.cp - start.cp + 1);
		[text getSubstring:currentText start:start.cp length:end.cp - start.cp];
		*(currentText + end.cp - start.cp) = '\0';
		[findView setStringValue:currentText];
	}
	[findView selectText:self];

    return self;
}

- replace:sender
{	
	return [self find:replace];
}

- replaceAll:sender
{
    return [self find:rall];
}

- setOptions:sender
{
	switch([sender selectedTag])
	{
		case 0 :
			matchCase = ![sender state];
			break;
		case 1:
			regex = [sender state];
			break;
		default:
			break;
	}
    return self;
}

- findNext:sender
{
    return [self find:next];
}

- jumpToSelection:sender
{
	id text;
	if((text =[self checkResponder]) == nil)
		return self;
	[text scrollSelToVisible];
    return self;
}

- findPrevious:sender
{
    return [self find:previous];
}

- replaceAndFind:sender
{
    return [self find:(replace | next)];
}

- find:(find_type)findTag
{
	id textO;
	NXStream *stream;
	
	if((textO =[self checkResponder]) == nil)
		return self;
	else
	{
	
	const char *replaceBuf;
	NXSelPt start, end;
	int len;
    struct re_pattern_buffer buf;
    char fastmap[(1 << BYTEWIDTH)];


	currentText = (char *)[findView stringValueAt:0];
	replaceBuf = [findView stringValueAt:1];
	
	if(findTag & replace)
	{
		[textO replaceSel:replaceBuf];
	}
	if(!(findTag & ~replace))
		return self;
		
	if(regex)
	{
    	buf.allocated = 40;
    	buf.buffer = (char *)malloc(buf.allocated);
    	buf.fastmap = fastmap;
		if(!matchCase)
    		buf.translate = upcase;
		else buf.translate = NULL;
		re_compile_pattern(currentText, strlen(currentText), &buf);
	    re_compile_fastmap(&buf);
	}
	
	len = [textO textLength];
	if(len == 0)
		return self;
	[textO getSel:&start :&end];
	stream = [textO stream];


	
	if(findTag & (next|previous))
	{
		int i, flen, wrapped = NO, found = NO;
		int begin;
		int most;
		textStart = (char *)realloc(textStart, len);
		NXRead(stream, textStart, len);
		if(findTag & next)
		{
			begin = end.cp;
			most = len - end.cp;
		}
		else
		{
			begin = start.cp - 1;
			most = -start.cp + 1;
			if(begin < 0)
			{
				begin = len - 1;
				most = start.cp - len + 1;
				wrapped = YES;
			}
		}
		
		do
		{
			if(regex)
			{
				i = re_search(&buf, textStart, len, begin, most, 0, &flen);
			}
			else
			{
				i = search(currentText, textStart, begin, most, matchCase);
				flen = strlen(currentText);
			}
			if(i < 0)
			{
				if(!wrapped)
				{	
					wrapped = YES;
					if(findTag & next)
					{
						begin = 0;
						most = end.cp;
					}
					else
					{
						begin = len - 1;
						most = start.cp - len + 1;
					}
				}
				else
				{
					if(!found)
					{
						[warningText setStringValue:"Not\nFound"];
					}
					return self;
				}
			}
			else
			{
				if(!found)
					[warningText setStringValue:""];
				found = YES;
				[textO setSel:i:i + flen];
				[textO scrollSelToVisible];
			}
		} while(!found);
	
	}
	
	if(findTag & rall)
	{
		int begin = 0, most, i, flen, found = NO, offset;
		int rlen = strlen(replaceBuf);

		if(extent != all)
		{
			offset = start.cp;
			most = end.cp - start.cp - 1;
			len = most;
		}
		else
		{
			offset = 0;
			most = len;
		}
		if(most < 1)
			return self;
			
		NXSeek(stream, offset, NX_FROMSTART);
		textStart = (char *)realloc(textStart, most);
		NXRead(stream, textStart, most);
		
		flen = strlen(currentText);
		for(;;)
		{
			if(regex)
			{
				i = re_search(&buf, textStart, len, begin, most, 0, &flen);
			}
			else
			{
				i = search(currentText, textStart, begin, most, matchCase);
			}
			if((i < 0) || (i + flen) > len)
			{
				if(!found)
					[warningText setStringValue:"Not\nFound"];
				return self;
			}
			else
			{
				if(!found)
				{
					[warningText setStringValue:""];
					found = YES;
				}
				[textO setSel:i + offset :i + offset + flen];
				[textO replaceSel:replaceBuf];
				
				offset += rlen - flen;
				begin = i + flen;
				most = len - begin;
				if(most < 0)
				{
					if(!found)
					[warningText setStringValue:"Not\nFound"];
					return self;
				}
			}
		}
	}
			
	return self;
	}
}

- jumpToCount:sender
{
	id text;
	
	if((text =[self checkResponder]) == nil)
		return self;
	else
	{
		char *test;
		int end;
		const char *buf = [countView stringValue];
		int start = atoi(buf);
		if((test = strchr(buf, ':')) != NULL)
			end = atoi(test + 1);
		else
			end = start;
		
		switch(countType)
		{
		
			case line :
			{
				[text setSel:[text positionFromLine:start]
					:[text positionFromLine:end + 1]];
				break;
			}
			case character :
			{
				[text setSel:start :end];
				break;
			}
		}
	}
	[countPanel orderOut:self];
	[text scrollSelToVisible];
	
    return self;
}

- setCountType:sender
{
	countType = [sender selectedTag];
    return self;
}

- setSearchExtent:sender
{
	extent = [sender selectedTag];
    return self;
}

int search(const char *match, char *text, int start, int most, int matchCase)
{
	int len, offset = 0;
	int c, dir;
	len = strlen(match);
	if(most < 0)
	{
		c = len - 1;
		dir = -1;
	}
	else
	{
		c = 0;
		dir = 1;
	}
	offset = 0;

	if(abs(offset) > abs(most))
		return -1;
		
	if(matchCase)
	{
		for(;;)
		{
			while(*(text + start + offset) != *(match + c))
			{
				offset += dir;
				if(abs(offset) > abs(most))
					return -1;
			}
			if(!strncmp(text + start + offset - c, match, abs(len)))
			{
				return start + offset - c;
			}
			offset += dir;
		}
	}
	else
	{
		for(;;)
		{
			while(tolower(*(text + start + offset)) != tolower(*(match + c)))
			{
				offset += dir;
				if(abs(offset) > abs(most))
					return -1;
			}
			if(!strncasecmp(text + start + offset - c, match, abs(len)))
			{
				return start + offset - c;
			}
			offset += dir;
		}
	}
	return -1;
}

@end
