/*
 *  Copyright (c) 1993 Christopher J. Kane.  All rights reserved.
 *
 *  This software is subject to the terms of the MiscKit license
 *  agreement.  Refer to the license document included with the
 *  MiscKit distribution for these terms.
 *
 *  Version: 1.1 (22 October 1993)
 */

#import "Timer.h"

// Beware: this code is a mess

// The searchFor:... and replaceAll:... method implementations below
// have a few things of note:
//   1. A warmup timing is done, just for the sake of silliness
//   2. The state of the text that needs to be saved in order to
//      restore it after timing the Text class is saved, so that
//      the timing of the Misc_TBMK routines and friends happens
//      under nearly the same conditions
//   3. Searches/replaces are done for the literal string of a
//      regular expression in the Text object searching when a
//      regular expression is provided.

#define WALLTIME(T1, T2) ((1000000*(T2.tv_sec-T1.tv_sec)+T2.tv_usec-T1.tv_usec)/1000)
#define PROCTIME(U1, U2) ((1000000*(U2.ru_utime.tv_sec-U1.ru_utime.tv_sec)+\
	U2.ru_utime.tv_usec-U1.ru_utime.tv_usec+1000000*(U2.ru_stime.tv_sec-\
	U1.ru_stime.tv_sec)+U2.ru_stime.tv_usec-U1.ru_stime.tv_usec)/1000)

@implementation Timer

- init
{
  int i;				// calibrate a bit
  struct rusage usage1, usage2;
  struct timeval t1, t2;
  gettimeofday(&t1, 0);			// just done for "warmup" purposes
  getrusage(RUSAGE_SELF, &usage1);
  [self self];				// get crude estimate of method call time
  getrusage(RUSAGE_SELF, &usage2);
  gettimeofday(&t2, 0);
  wallcallms = 0;
  proccallms = 0;
  NXPing();				// why not?
  for (i=0; i<25; i++)
    {
      gettimeofday(&t1, 0);
      getrusage(RUSAGE_SELF, &usage1);
      [self self];
      getrusage(RUSAGE_SELF, &usage2);
      gettimeofday(&t2, 0);
      wallcallms += WALLTIME(t1, t2);
      proccallms += PROCTIME(usage1, usage2);
    }
  wallcallms /= 25000;
  proccallms /= 25000;
  return self;
}

- (oneway void)makeSelectionVisible
{
  [textObj makeSelectionVisible];
}

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

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

- (void)writeSelectionToPasteboard:(in Pasteboard *)pboard asType:(in NXAtom)type
{
  [textObj writeSelectionToPasteboard:pboard asType:type];
}

- (int)replaceAll:(const char *)pattern with:(const char *)replacement mode:(SearchMode)mode regexpr:(BOOL)regexpr cases:(BOOL)cases
{
  char buffer[1024];
  int r, count=0, end;
  NXSelPt sp0, spN;
  struct rusage usage1, usage2;
  struct timeval t1, t2;
  NXStream *strm;

  strm = NXOpenMemory(NULL, 0, NX_READWRITE);
  [textObj writeText:strm];		// save original text
  NXSeek(strm, 0, NX_FROMSTART);
  NXPing();
  gettimeofday(&t1, 0);			// just done for "warmup" purposes
  getrusage(RUSAGE_SELF, &usage1);
  [textObj self];
  getrusage(RUSAGE_SELF, &usage2);
  gettimeofday(&t2, 0);

  [textObj getSel:&sp0 :&spN];
  [messageField setStringValue:"Timing Text class..."];
  [textObj getSel:&sp0 :&spN];
  [[textObj window] disableDisplay];	// don't draw (and slow things down)
  NXPing();
  gettimeofday(&t1, 0);			// time the Text class
  getrusage(RUSAGE_SELF, &usage1);
  if (mode==TextEdgeToTextEdge)
    {
      [textObj setSel:0 :0];		// start at the very beginning...
        while ([textObj findText:pattern ignoreCase:!cases backwards:NO wrap:NO])
          {	// findText:... does the selecting for us, and
		// takes care of the end point
            [textObj replaceSel:replacement];
            count++;
          }
    }
  else if (mode== SelStartToSelEnd)
    {
      end = spN.cp-strlen(pattern);
      [textObj setSel:sp0.cp :sp0.cp];
      while ([textObj findText:pattern ignoreCase:!cases backwards:NO wrap:NO])
        {	// here, the we need to keep track of the end position
          if (sp0.cp>end) break;
          [textObj replaceSel:replacement];
          end += strlen(replacement)-strlen(pattern);
          count++;
        }
    }
  getrusage(RUSAGE_SELF, &usage2);
  gettimeofday(&t2, 0);
  [[textObj window] reenableDisplay];
  [[textObj window] display];
  [textWallField setIntValue:WALLTIME(t1, t2)-wallcallms];
  [textProcField setIntValue:PROCTIME(usage1, usage2)-proccallms];
  [messageField setStringValue:"One moment..."];
  NXPing();
  [textObj readText:strm];		// restore original text
  [textObj setSel:sp0.cp :spN.cp];	// restore original selection

  [messageField setStringValue:"Timing Misc_TBMK routines..."];
  NXPing();
  gettimeofday(&t1, 0);				// time the Misc_TBMK routines
  getrusage(RUSAGE_SELF, &usage1);
  r = [textObj replaceAll:pattern with:replacement mode:mode regexpr:regexpr cases:cases];
  getrusage(RUSAGE_SELF, &usage2);
  gettimeofday(&t2, 0);
  [tbmkWallField setIntValue:WALLTIME(t1, t2)-wallcallms];
  [tbmkProcField setIntValue:PROCTIME(usage1, usage2)-proccallms];

  sprintf(buffer, "replaceAll:\"%s\" with:\"%s\" mode:%s regexpr:%s cases:%s", pattern, replacement,
     (mode==TextEdgeToTextEdge?"TextEdgeToTextEdge":
          mode==SelStartToSelEnd?"SelStartToSelEnd":"Other"),
     (regexpr?"YES":"NO"), (cases?"YES":"NO"));
  [messageField setStringValue:buffer];
  NXCloseMemory(strm, NX_FREEBUFFER);
  return r;
}

- (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
{
  char buffer[1024];
  int r;
  NXSelPt sp0, spN;
  struct rusage usage1, usage2;
  struct timeval t1, t2;

  [textObj getSel:&sp0 :&spN];
  NXPing();
  gettimeofday(&t1, 0);			// just done for "warmup" purposes
  getrusage(RUSAGE_SELF, &usage1);
  [textObj self];
  getrusage(RUSAGE_SELF, &usage2);
  gettimeofday(&t2, 0);

  [messageField setStringValue:"Timing Text class..."];
  NXPing();
  gettimeofday(&t1, 0);			// time the Text class
  getrusage(RUSAGE_SELF, &usage1);
  [textObj findText:pattern ignoreCase:!cases backwards:rev wrap:YES];
  getrusage(RUSAGE_SELF, &usage2);
  gettimeofday(&t2, 0);
  [textWallField setIntValue:WALLTIME(t1, t2)-wallcallms];
  [textProcField setIntValue:PROCTIME(usage1, usage2)-proccallms];
  [textObj setSel:sp0.cp :spN.cp];	// restore original selection

  [messageField setStringValue:"Timing Misc_TBMK routines..."];
  NXPing();
  gettimeofday(&t1, 0);			// time the Misc_TBMK routines
  getrusage(RUSAGE_SELF, &usage1);
  r = [textObj searchFor:pattern mode:mode reverse:rev regexpr:regexpr cases:cases position:pos size:size];
  getrusage(RUSAGE_SELF, &usage2);
  gettimeofday(&t2, 0);
  [tbmkWallField setIntValue:WALLTIME(t1, t2)-wallcallms];
  [tbmkProcField setIntValue:PROCTIME(usage1, usage2)-proccallms];

  sprintf(buffer, "searchFor:\"%s\" mode:%s reverse:%s regexpr:%s cases:%s pos:size:", pattern,
   (mode==SelEndToSelStart?"SelEndToSelStart":
        mode==SelStartToSelEnd?"SelStartToSelEnd":"Other"),
   (rev?"YES":"NO"), (regexpr?"YES":"NO"), (cases?"YES":"NO"));
  [messageField setStringValue:buffer];
  return r;
}

@end
