//
//  ctLimitFunc.m
//  CocoaTams
//
//  Created by matthew on Mon Apr 15 2002.
//  Copyright (c) 2001 Matthew Weinstein. All rights reserved.
//

#import "ctLimitFunc.h"
#import "ctLimitCrit.h"
#import "tams.h"
#import "utils.h"
#import "ctQChar.h"
#import "ctReadChar.h"
#import "ctUnlimitedRuns.h"
#import "coderec.h"
#import "myResults.h"
#import "prefBoss.h"

#define PRINTCODER(X, Y) {if([gPrefBoss showCoder] && [(NSString *)Y length]){[X appendString:@" ["]; [X appendString: Y]; [X appendString: @"]"];}}
NSMutableString *ctLimString;
NSMutableArray *ctLimitVault;
NSMutableArray *ctZoneVault;
int inLimFlag = 0;
int singleLimFlag;

//zone management

void ctAddZone(ctQChar *who)
{
    NSMutableDictionary *dd;
    dd = [[NSMutableDictionary alloc] init];
    [dd setObject: [[who buff] copy] forKey: @"codeName"];
    [dd setObject: [[who coder] copy] forKey: @"coderName"];
    
    [ctZoneVault addObject:dd];
}

void ctAddLimChar(ctQChar *qq)
{
	FORALL(ctRunVault)
	{
	    if(([temp openRun] == YES) /*&& !isrepeat(qq)*/)
	    {
		switch([qq tokentype])
		{
		    case CHAR:
			[(coderec *)temp addString: [qq buff]];
			break;
		    case TOKEN:
			if(israw() && ([gCurrentTAMS isRepeat: [temp theName]] == NO))
			{
			    [(coderec *)temp addString: @"{"];
			    [(coderec *)temp addString: [qq buff]];
			    
			    //add coder
			    PRINTCODER((coderec *)temp, [qq coder]);
			    if([[qq coder] length])
			    {[(coderec *)temp appendString:@"["]; 
				[(coderec *)temp appendString: [qq coder]]; 
				[(coderec *)temp appendString: @"]"];}
			    [(coderec *)temp addString: @"}"];
			}
			break;
		    case ENDTOKEN:
			if(israw() && ([gCurrentTAMS isRepeat: [temp theName]] == NO))
			{
			    [(coderec *)temp addString: @"{/"];
			    [(coderec *)temp addString: [qq buff]];
			    //add coder
			    PRINTCODER((coderec *)temp, [qq coder]);
			    [(coderec *)temp addString: @"}"];
			}
		    
		    default:;
		};
			
			    
	    }
	}
	ENDFORALL;
}

void ctOpenLimRun(ctQChar *qq)
{
    coderec *cr;
    
    cr = [[coderec alloc] init];
     if(israw() && !isrepeat(qq))
    {
	
	FORALL(ctZoneVault)
	{
	    [[cr record] appendString: @"{"];
	    [[cr record] appendString: [temp objectForKey: @"codeName"]];

	    if([[temp objectForKey: @"coderName"] isEqualToString: @""] == YES)
		;
	    else //add coder
	    {
		PRINTCODER([cr record], [temp objectForKey: @"coderName"]);
	    }
/* old code
	    {
		[[cr record] appendString: @"["];
		[[cr record] appendString: [temp objectForKey: @"coderName"]];
		[[cr record] appendString: @"]"];
	    }
	    
*/
	    [[cr record] appendString: @"}"];
	}ENDFORALL;
    }
    
    if(singleLimFlag)
    {
	NSMutableString *sss = [[NSMutableString alloc] init];
	[sss setString: @"+"];
	[sss appendString: [qq buff]];
	[cr setName: sss];
    }
    else
    {
	NSMutableString *sss = [[NSMutableString alloc] init];
	[sss setString: @"-"];
	[sss appendString: [gCurrentTAMS limString]];
	[cr setName: sss];
	
    }
    [cr setBegin: [qq where]];
    [cr setDoc: hotSearchDocument];

    [cr setOpen: YES];
    [cr retain];
    [ctRunVault addObject: cr];
    //need to add current state at this point!
    //need to add self tothe current state
}
void ctCloseLimRun(ctQChar *qq)
{
    if(israw())
    {
	FORALL(ctRunVault)
	{
	    if([temp openRun] == YES && ([gCurrentTAMS isRepeat: [temp theName]] == NO))
	    {
		[[temp record] appendString: @"{/"];
		[[temp record] appendString: [qq buff]];
		//add coder
		PRINTCODER([temp record], [qq coder]);
		[[temp record] appendString: @"}"];
	    }
	}
	ENDFORALL;
    }

    FORALL(ctRunVault)
    {
	if([temp openRun] == YES && ([gCurrentTAMS isRepeat: [temp theName]] == NO) )
	{
	    [temp setOpen: NO];
	    [temp setEnd: [qq where]];
	    [temp setComment: [qq extra]];
	    return;
	}
    
    }
    ENDFORALL;
    //need to check out of the current state
}

void checkZones(void)
{
    if([ctZoneVault count])
	NSWARNING(@"There are passages missing a close tag; run \"check code pairs\" on your files");
}
void ctDelAllZones()
{
    [ctZoneVault removeAllObjects];
}
void ctDelZone(ctQChar *who)
{
    id theOne;
    FORALL(ctZoneVault)
    {
	theOne = nil;
	if([[temp objectForKey: @"codeName"] isEqualToString: [who buff]] &&
	    [[temp objectForKey: @"coderName"] isEqualToString: [who coder]])
	{
	    theOne = temp;
	    break;
	}
    }
    ENDFORALL;
    
    if (theOne != nil)
	[ctZoneVault removeObject: theOne];
    else NSWARNING(@"There are formatting errors in your file; run check code pairs");
    //[theOne release];
}

int handleLimChar(ctQChar *qq)
{
    if([qq tokentype] == META) [gCurrentTAMS handlemeta: qq];
    if([qq tokentype] == TOKEN)
    {
	ctAddZone(qq);
	if([gCurrentTAMS isRepeat: [qq buff]])
	{
	    ctOpenRun(qq);
	}
    }
    else if([qq tokentype ] == ENDTOKEN)
    {
	ctDelZone(qq);
	if([gCurrentTAMS isRepeat: [qq buff]])
	{
	    ctCloseRun(qq);
	}
    }
    else if([qq tokentype] == CHAR)
    {
	if(inLimFlag)
	    return INLIM;
	else
	{
	    ctAddOpenChar(qq);  //cludge for repeats
	    return OUTLIM;
	}
    }
    if(inLimFlag == 0 && isinlimit())
    {
	inLimFlag = 1;
	return STARTLIM;
    }
    else if(inLimFlag == 1 && isinlimit())
    {
	return INLIM;
    }
    else if (inLimFlag == 0 && !isinlimit())
    {
	return OUTLIM;
    }
    else if (inLimFlag == 1 && !isinlimit())
    {
	inLimFlag = 0;
	return ENDLIM;
    }
    return OUTLIM;
}

void zeroRuns()
{
    ctLimitVault = nil;
    ctZoneVault = nil;
    ctLimString = nil;
}
void initLimit()
{
    if(ctLimitVault) [ctLimitVault release];
    
    ctLimitVault = [[NSMutableArray alloc] init];
   //[ctLimitVault retain];
   if(ctLimString) [ctLimString release];
    ctLimString = [[NSMutableString alloc] init];
    if(ctZoneVault) [ctZoneVault release];
    ctZoneVault = [[NSMutableArray alloc] init];
    inLimFlag = 0;
}

int isStringTrue(ctLimitCrit *elem, NSString *code, NSString *coder)
{
    NSMutableArray *aa;
    NSRange rr;
		//get help from the current tams object
    NSMutableArray *bb;
    int matchflag;
    int lcnt;
    int ii, jj;
    int ncnt,tcnt;
   //first find out if we match the coder
    if([elem areCoders]==YES) //check coder against everyone
    {
	int foundFlag;
	NSRange r;
	foundFlag = 0;
	FORALL([elem getCoders])
	{
	    NSString *s1 = [NSString stringWithString: [temp objectForKey: @"coderName"]];
	    if([s1 isEqualToString: coder]) ii++;
	    if([[temp objectForKey: @"modifier"] isEqualToString: @"~"] == YES)
	    {
		//just check out the front part
		r = [coder rangeOfString: [temp objectForKey: @"coderName"]];
		if(r.location == 0)
		{
		    foundFlag = 1; break;
		}
	    }
	    else
	    {
		//check out the rest, but make sure to do a null string check
		if([[temp objectForKey: @"coderName"] isEqualToString: @"*"] == YES)
		{
		    if([coder isEqualToString: @""] == YES)
		    {
			foundFlag = 1; break;
		    }
		}
		else
		{
		    if([coder isEqualToString: [temp objectForKey: @"coderName"]])
		    {
			foundFlag = 1; break;
		    }
		}
		    
	    }
	}
	ENDFORALL;
	if(!foundFlag) return 0;
    }
    if([[elem name] isEqualToString: @""] == YES) return 1;
 	switch([elem modif])
	{
	    case '>':
		//get help from the current tams object

		    matchflag = 1;
		    aa = convertStringToArray(code, '>');
		    bb = convertStringToArray([elem name], '>');
		    [aa retain]; [bb retain];
		    ncnt = [bb count];
		    tcnt = [aa count];
		    if(ncnt > tcnt)  //the string must be equal at least to the test name
		    {
			matchflag = 0;
		    }
		    else
		    {
			for(ii = 0; ii <= tcnt - ncnt; ii++)
			{
			    matchflag = 1;
			    for(lcnt = 0, jj = ii; lcnt < ncnt; lcnt++, jj++)
			    {
				if([[bb objectAtIndex: lcnt] isEqualToString:
					[aa objectAtIndex: jj]] == NO)
				{
				    matchflag = 0;
				    break;
				}
			    }
			    if(matchflag == 1) return matchflag;
			}
		    }
		    if(matchflag) return matchflag;
		    //loop on bb, aa can be longer
		    //if ([[aa objectAtIndex: 0] isEqualToString: [elem name]])
			//return 1;
		    break;
	    case '*':
		rr = [code rangeOfString: [elem name]];
		if(rr.location != NSNotFound)
		    return 1;
		break;
	    case '\'':
		 if([code isEqualToString: [elem name]] == YES)
			return 1;
		break;
	    default:
		if([gCurrentTAMS global: @"exactflag"])
		{
		    if([code isEqualToString: [elem name]] == YES)
			return 1;
		}
		else
		{
		    NSMutableArray *bb;
		    int matchflag;
		    int lcnt;
		    int ncnt,tcnt;
		    matchflag = 1;
		
		    aa = convertStringToArray(code, '>');
		    bb = convertStringToArray([elem name], '>');
		    [aa retain]; [bb retain];
		    ncnt = [bb count];
		    tcnt = [aa count];
		    if(ncnt > tcnt)  //the zone must be equal at least to the test name
		    {
			matchflag = 0;
		    }
		    else
		    {
			for(lcnt = 0; lcnt < ncnt; lcnt++)
			{
			    if([[bb objectAtIndex: lcnt] isEqualToString:
				    [aa objectAtIndex: lcnt]] == NO)
			    {
				matchflag = 0;
				break;
			    }
			}
		    }
		    if(matchflag) return matchflag;
		    //loop on bb, aa can be longer
		    //if ([[aa objectAtIndex: 0] isEqualToString: [elem name]])
			//return 1;
		}
		break;
	}
	;
    return 0;
}

//compare this andgroup with the current zone
int isCurrTrue(ctLimitCrit *elem)
{
    
    FORALL(ctZoneVault)
    {
	if(isStringTrue(elem, [temp objectForKey: @"codeName"], [temp objectForKey: @"coderName"])) return 1;
    } ENDFORALL;
    return 0;
}

int isTagTrue(ctQChar *who)
{
    int i,j, c1, c2;
    ctLimitCrit *elem;
    NSArray *andList;
    
    c1 = [ctLimitVault count];
    for(i = 0; i< c1; i++)
    {
	andList = [ctLimitVault objectAtIndex: i] ;
	c2 = [andList count];
	for(j = 0; j < c2; j++)
	{
	    elem = [andList objectAtIndex: j];
	    if(isStringTrue(elem, [who buff], [who coder])) return 1;
	}
    }
    return 0;

}

int checkand(NSArray *a)
{
	int r, oldr; 
	oldr = 1;
	FORALL(a)
	{
		r = isCurrTrue(temp);
		oldr *= r; //and is like times if any are 0 it goes to 0
	}
	ENDFORALL;
	return oldr ;
}



int isinlimit()
{
	int   rslt;
	int trslt;
	trslt = 0;
	if([ctZoneVault count] == 0) return 0;
	FORALL(ctLimitVault)
	{
	    rslt = checkand(temp);
	    trslt += rslt;
	    if(trslt) { return 1;}
	
	}
	ENDFORALL;
	return 0;
}
/* handlers */

void parseand(NSString *nsbuff)
{
	int i, j;
	MWCHAR c;
	NSMutableString *b;
	ctLimitCrit *elem;
	NSMutableArray *agp;
	
#ifdef ldebug
	printf("in parseand: %s\n", buff);
#endif
	agp = [[NSMutableArray alloc] init]; //agp = (andgroup *) new andgroup;
	b = [[NSMutableString alloc] init];
	[b retain];
	
	i = j = 0;
	for(;;)
	{
		if(i >= [nsbuff length]) c = '\0';
		else
		    c = [nsbuff characterAtIndex: i];
		if(c == ' ') {i++; continue;}
		if(c == '+' || c == '\0')
		{
			if([b length])
			{
				elem = [[ctLimitCrit alloc] initWithString: b];
				[elem retain];
				[agp addObject: elem];
				//[elem release];
			}
			j = 0;
			[b setString: @""];
			if (c == '\0') break;
		}
		else
		    [b ADDCHAR(c)];
		i++;
	}
	[ctLimitVault addObject: agp];
	//[agp release];
	[b release];
}

void handlelimit(NSString *buff)
{
	int j, i;
	MWCHAR c;
	int n;
	NSMutableString *b;
	
	singleLimFlag = 0;
	[ctLimString setString: buff];
	b = [[NSMutableString alloc] init];
	[b retain];
	n = [buff length];
	if(n)
	{
		[gCurrentTAMS setGlobal: @"limitflag" to: 1];
		i = 0;
		j = 0;
		for(;;)
		{
			if(i >= [buff length]) c = '\0';
			else
			    c = [buff characterAtIndex: i];
			if(c  == ' ') 
			{
			    i++; 
			    if(i >= [buff length]) break; 
			    c = [buff characterAtIndex: i];
			}
			
			if(i >= n) break;
			while(c  != ',' && i < n)
			{
				[b ADDCHAR(c)]; i++; j++;
				if(i < n)
				    c = [buff characterAtIndex: i];
				else
				    break;
			}
			
			if ([b length]) {parseand(b); [b setString: @""];}
			if (c == '\0') break;
			i++;
		}
	}
	if([ctLimitVault count] == 1)
	{
	    NSArray *theOne;
	    //may be a single name search
	    theOne = [ctLimitVault objectAtIndex: 0];
	    //passes test 1
	    if([theOne count] == 1)
	    {
		if([[theOne objectAtIndex: 0] modif] == ' ')
		{
		    singleLimFlag = 1;
		}
	    
	    }
	
	
	}
}
