#import "CodeBrowser.h"
#import "PrefsPanel.h"
#import "support.h"


@implementation CodeBrowser: Window
{ char *fileName ;
  id lineList ;
  id methodBrowser ;
  id methodList ;
  id msgTextField  ;
  id splitView ;
  id codeBrowserScrollView ;
  id codeBrowserText ;
}

- awakeFromNib ;
{ // Do some setup work.

  // put views into splitview
  [splitView addSubview:
    [methodBrowser removeFromSuperview]] ;
  [splitView addSubview:
    [codeBrowserScrollView removeFromSuperview]] ;
  // set up the CodeBrowser text
  if([codeBrowserText shouldDrawColor])
  { [codeBrowserText setBackgroundColor:
      [[[NXApp prefsPanel] textBackgroundColorWell] color]] ;
  }
  else
  { float g ;
    NXConvertColorToGray([[[NXApp prefsPanel]
      textBackgroundColorWell] color], &g) ;
    [codeBrowserText setBackgroundGray:g] ;
  }
  [codeBrowserText setSel: 0 :0] ;
  [codeBrowserText replaceSel: ""] ;
  [codeBrowserText setSel: 0 :0] ;
  methodList = [[List alloc] init] ;
  [self setDelegate: self] ;
  lineList = [[Storage alloc] initCount: 0
                       elementSize: sizeof(int)
                       description: "i"] ;
  return self  ;
}

- (int)browser:sender fillMatrix:matrix inColumn:(int)column ;
{ return [methodList count] ; 
}

- browser:sender loadCell:cell atRow:(int)row inColumn:(int)column ;
{ [cell setStringValue: [[methodList objectAt: row] cString]];
  [cell setLeaf:YES] ;
  return self ;
}

- buildDotH: sender ;
{ // build an ObjC interface file from the selected text
  NXSelPt start, end ;
  char *textBuf ;
  int textLen ;
  [codeBrowserText getSel:&start :&end] ;
  setSelectionRange(&start, &end, [codeBrowserText textLength]) ;
  textLen = end.cp-start.cp ;
  textBuf = (char *) malloc(textLen+1) ; 
  [codeBrowserText getSubstring:textBuf start:start.cp length:textLen] ;
  [NXApp buildDotH:textBuf length:textLen] ; 
  free(textBuf) ;
  return self ;
}

- clearMessage: sender ;
{ // Clear the message in the transcript.  Normally
  // called via a delayed message send.
  [self message: ""] ;
  return self ; 
}

- codeBrowserText ;
{ return codeBrowserText ;
}

- evalObjC: sender ;
{ // evaluate the current selection as ObjC
  NXSelPt start, end ;
  char *textBuf ;
  int textLen ;
  [codeBrowserText getSel:&start :&end] ;
  [codeBrowserText getSel:&start :&end] ;
  setSelectionRange(&start, &end, [codeBrowserText textLength]) ;
  textLen = end.cp-start.cp ;
  textBuf = (char *) malloc(textLen+1) ; 
  [codeBrowserText getSubstring:textBuf start:start.cp length:textLen] ;
  [NXApp evalObjC:textBuf length:textLen] ; 
  free(textBuf) ;
  return self ;
}

- evalPs: sender ;
{ // evaluate the current selection as postscript
  NXSelPt start, end ;
  char *textBuf ;
  int textLen ;
  // get the selected text
  [codeBrowserText getSel:&start :&end] ;
  [codeBrowserText getSel:&start :&end] ;
  setSelectionRange(&start, &end, [codeBrowserText textLength]) ;
  textLen = end.cp-start.cp ;
  textBuf = (char *) malloc(textLen+1) ; 
  [codeBrowserText getSubstring:textBuf start:start.cp length:textLen] ;
  textBuf[textLen] = '\0' ;
  // evalute via "ps:"
  [self message: "Evaluating Postscript..."] ;
  [Eval ps: textBuf] ;
  [self message: ""] ;
  free(textBuf) ;
  return self ;
}

- (const char *) fileName ;
{ if(!fileName)
    return "UNTITLED" ;
  else 
    return fileName ;
}

- fileName: (const char *) fname ;
{ // Copy fname into ivar, set our title to fname, 
  // with the filename followed by the path.
  int nameLen ;
  if(fileName)
  { if(!strcmp(fileName,fname))
      return self ; // fileName isn't changing
    else
      free(fileName) ;
  }
  // malloc memory and copy the fileName;
  nameLen = strlen(fname) + 1 ;
  fileName = (char *) malloc(nameLen) ;
  strcpy(fileName, fname) ;
  [self setTitleAsFilename: fileName] ;
  return self ;
}

- format: sender ;
{ // text formatting is done by class EvalText
  char pathBuf[512] ;
  [methodBrowser getPath: pathBuf toColumn: 1] ;
  [methodList freeObjects] ;
  [codeBrowserText format: self] ;
  [methodBrowser loadColumnZero] ;
  [methodBrowser setPath: pathBuf] ;
  return self ;
}

- goToMethod: sender ;
{ // find the text in the codeBrowserText
  NXSelPt start, end ;
  [codeBrowserText getSel: &start :&end] ;
  [codeBrowserText setSel: 0 :0] ;
  if(![codeBrowserText
         findText: [[methodList objectAt: [[sender matrixInColumn:0] selectedRow]] cString]
       ignoreCase: NO
        backwards: NO
             wrap: NO])
  { [self message: "Method not found.\nTry hitting the Format button."] ;
    NXBeep() ;
    [self perform: @selector(clearMessage:) with: self
        afterDelay: 2500 cancelPrevious: YES] ;
    [codeBrowserText setSel: start.cp :end.cp] ;
  }
  return self ;
}

- lineList ;
{ return lineList ;
}

- loadObjC: sender ;
{ // compile and load the current selection 
  NXSelPt start, end ;
  char *textBuf ;
  int textLen ;
  [codeBrowserText getSel:&start :&end] ;
  [codeBrowserText getSel:&start :&end] ;
  setSelectionRange(&start, &end, [codeBrowserText textLength]) ;
  textLen = end.cp-start.cp ;
  textBuf = (char *) malloc(textLen+1) ; 
  [codeBrowserText getSubstring:textBuf start:start.cp length:textLen] ;
  [NXApp loadObjC:textBuf length:textLen] ; 
  free(textBuf) ;
  [[NXApp moduleBrowser] loadColumnZero] ;
  return self ;
}


- message: (char *) aMsg ;
{ // show aMsg in the msg textfield
  [msgTextField setStringValue: aMsg] ;
  NXPing() ;
  return self ;
}

- methodList ;
{ return methodList ;
}

- readStream: (NXStream *) aStream
{ // read text from aStream into codeBrowserText
  [codeBrowserText readText: aStream] ;
  [codeBrowserText format: self] ; 
  [self setDocEdited: NO] ;
  return self ;
}

- revert:sender ;
{ if(fileName && [[codeBrowserText window] isDocEdited])
  { int rval = NXRunAlertPanel(
     "Eval","%s has been edited. Revert?",
      "Revert","Cancel",NULL,[self fileName]) ;
    if(rval)
    { NXStream *fileStream ;
      [self message: "Reading file..."] ;
      if((fileStream = NXMapFile(fileName,NX_READONLY)) != NULL)
      { [self readStream: fileStream] ;
        NXCloseMemory(fileStream,NX_FREEBUFFER);
        [self message: ""] ;
      }
    }
    else
      return nil ;
  }
  return self ;
}

-(BOOL) save:sender ;
{ if([[codeBrowserText window] isDocEdited])
  { if(!fileName) // no fileName, i.e. first save:
      [self saveAs: sender] ;
    else
      [self saveTextToFileName] ;
  }
  return YES ;
}


-(BOOL) saveAs:sender ;
{ id savePanel = [SavePanel new] ;
  if([savePanel runModalForDirectory: NULL file: fileName])
  { [self fileName: (char *) [savePanel filename]] ;
    return [self saveTextToFileName] ;
  }
  else
    return NO ;
}


- (BOOL) saveTextToFileName ;
// We assume the instance variable fileName contains a valid
// file pathname.  If it can be opened or created, with mode
//  644: text of codeBrowserText is written out to it,
//  and YES is returned, else returns NO.
{ // removeBackup should be a dwrite...some day!
  NXStream *aStream = NXOpenMemory(NULL,0, NX_WRITEONLY);
  [self message: "Saving file..."] ;
  [codeBrowserText writeText:aStream] ;
  NXSaveToFile(aStream, fileName);
  NXCloseMemory(aStream, NX_FREEBUFFER) ;
  [self message: ""] ;
  [[codeBrowserText window] setDocEdited: NO] ;
  return YES ;
}

- windowDidBecomeMain: sender ;
{ // this causes the finder to load the linenum browser
  // using our linenum's 
  [[NXApp finder] reset: self] ;
  return self ;
}

- windowWillClose: sender ;
{ if(self == [[NXApp transcriptText] window])
  // If I am the transcript, ignore this message
    return self ;
  if([self isDocEdited])
  { int rval = NXRunAlertPanel("Review Unsaved",
      "Save changes to %s?","Cancel","Don't Save","Save",
      [self title]) ;
    if(rval == NX_ALERTOTHER) // rightmost button...save
      [self save: self] ;
    else if(rval == NX_ALERTALTERNATE)
    {}   // middle button == don't save, i.e. do nothing
    else // rval == NX_ALERTDEFAULT, leftmost button == Cancel
    { return nil ;
    }
  }
  [lineList free] ;
  [methodList freeObjects] ;
  [methodList free] ;
  [NXApp removeWindowsItem: sender] ;
  return self ;
}

- windowWillMiniaturize: sender toMiniwindow: miniwindow ;
{ if(fileName)
  { int extension = strlen(fileName) - 2 ;
    if(!strcmp(&fileName[extension],".m"))
      return [self setMiniwindowIcon: "HackM"] ; 
    else if(!strcmp(&fileName[extension],".h"))
      return [self setMiniwindowIcon: "HackH"] ; 
  }
  return [self setMiniwindowIcon: "Hack"] ;
}

@end
