/* The code of this example is not covered by the FindPanel license.
 * You may freely copy, distribute, and reuse the code in this example.
 * The author disclaims any warranty of any kind, expressed or implied,
 * as to its fitness for any particular use.
 *
 * Created by Christopher J. Kane
 * Version: 1.0 (15 August 1993)
 */

#import "SearchText.h"
#import "regexpr.h"

@implementation Text (SearchText)

- (oneway void)makeSelectionVisible
{
  [self scrollSelToVisible];
}

- (oneway void)replaceSelection:(const char *)replacement
{
  [self replaceSel:replacement];
}

- (oneway void)selectTextFrom:(int)start to:(int)end
{
  [self setSel:start :end];
}

- (void)writeSelectionToPasteboard:(in Pasteboard *)pboard asType:(in NXAtom)type
{
  char text[spN.cp-sp0.cp+1];
  [self getSubstring:text start:sp0.cp length:spN.cp-sp0.cp];
  text[spN.cp-sp0.cp] = '\0';	// writeType:data:length: includes more chars
				// than the length field if this isn't done
  if (*text!='\0')	// true if there was a non-zero length selection
    {
      [pboard declareTypes:&type num:1 owner:NULL];
      [pboard writeType:type data:text length:spN.cp-sp0.cp];
    }
}

// makes use of the fact that the FindPanel only uses two modes; one for
// each boolean value of the 'rev' parameter
- (int)searchFor:(const char *)pattern mode:(SearchMode)mode reverse:(BOOL)rev regexpr:(BOOL)regexpr cases:(BOOL)cases position:(out int *)pos size:(out int *)size
{
  if (!regexpr)
    { // use Text's built-in (but not necessarily efficient) functionality
      if ([self findText:pattern ignoreCase:!cases backwards:rev wrap:YES])
        {
	  *pos = sp0.cp;
	  *size = spN.cp-sp0.cp; //findText... selects the found text
	  return 1;
	}
      return 0;
    }
  else
    { // use regular expression package
      struct re_pattern_buffer p;
      char textBuffer[textLength+2], fm[256], tr[256];
      char *err=NULL;
      int res=0, i;
      memset(&p, 0, sizeof(struct re_pattern_buffer));
      [self getSubstring:textBuffer start:0 length:textLength+1];
      p.translate = tr;
      p.fastmap = fm;
      for(i=0; i<256; i++) tr[i] = i;
      if (!cases)	// translation buffer maps lower case to upper case
        for(i='a'; i<='z'; i++) tr[i] = i-('a'-'A');
      err = re_compile_pattern(pattern, strlen(pattern), &p);
      if (NULL!=err)
        {
          fprintf(stderr, err);
          return -1;
        }
      res = re_search(&p, textBuffer, textLength, rev?sp0.cp-1:spN.cp, rev?-sp0.cp+1:textLength-spN.cp, 0);
      if (res<-1)
        return -1;	// error in search
      if (res>-1)
        {		// match found
          *pos = res;	// now use re_match to find size (not very clever)
          res = re_match(&p, textBuffer, textLength, res, 0);
          if (res<0)
            return -1;
          *size = res;
          return 1;
        }
	// wrap to other bit of text and try again
      res = re_search(&p, textBuffer, textLength, rev?textLength:0, rev?spN.cp-textLength:sp0.cp, 0);
      if (res<-1)
        return -1;	// error in search
      if (res>-1)
        {		// match found
          *pos = res;	// now use re_match to find size (not very clever)
          res = re_match(&p, textBuffer, textLength, res, 0);
          if (res<0)
            return -1;
          *size = res;
          return 1;
        }
      return 0;		// not found in non-selected text
    }
  return -1;
}

- (int)replaceAll:(const char *)pattern with:(const char *)replacement mode:(SearchMode)mode regexpr:(BOOL)regexpr cases:(BOOL)cases
{
  int count=0, end;
  if (!regexpr)
    {
      switch (mode)
        {
          case TextEdgeToTextEdge: [self setSel:0 :0];
            while ([self findText:pattern ignoreCase:!cases backwards:NO wrap:NO])
              {	// findText:... method does selecting for us, leverage
		// off that and use replaceSel:
                [self replaceSel:replacement];
                count++;
              }
            return count;
          case SelStartToSelEnd: end = spN.cp-strlen(pattern); [self setSel:sp0.cp :sp0.cp];
            while ([self findText:pattern ignoreCase:!cases backwards:NO wrap:NO])
              { // do same as above, but we have to keep track of the
		// end point (Text does that for us above in textLength)
                if (sp0.cp>end)
                  break;
                [self replaceSel:replacement];
                end += strlen(replacement)-strlen(pattern);
                count++;
              }
            return count;
          default: return -1;  // FindPanel uses no other modes
        }
    }
  else
    NXRunAlertPanel("Replace All", "Replace All with regular expressions is not implemented", NULL, NULL, NULL);
	// it would be implemented very similar to above and searchFor:...
  return -1;
}

@end
