/* DocController.m
 * Purpose:  This is the main controlling class for this application.
 *	DocController loads the Info and Help nib files on demand.
 *	DocController also handles the multiple documents that can
 *	be open at any time using the Document class -- including
 *	keeping a count, tracking the current active document, 
 *	managing the open and save panels, etc.
 *
 * You may freely copy, distribute, and reuse the code in this example.
 * NeXT disclaims any warranty of any kind, expressed or  implied, as to its
 * fitness for any particular use.
 *
 * Written by: R. Dunbar Poor
 * Created: 28/April/1991
 *
 */

#import "DocController.h"
#import "Document.h"
#import <appkit/Application.h>
#import <appkit/Cell.h>
#import <appkit/OpenPanel.h>
#import <appkit/Panel.h>	/* for NX_ALERTxxx */
#import <strings.h>
#import <sys/param.h>

@interface DocController(DocControllerPrivate)
- _countDocuments:aDocument :sender;
- _save:aDocument :sender;
- _collectEditedDocuments:aDocument :editedDocuments;
- _hide:aDocument :sender;
@end

@implementation DocController

- init
{
  [super init];
  return self;
}

- free
{
  return [super free];
}

- showInfo:sender
{
  if (!infoPanel) {
    [NXApp loadNibSection:"Info.nib" owner:self withNames:NO];
  }
  [infoPanel makeKeyAndOrderFront:sender];
  return self;
}

- showHelp:sender
{
  if (!helpPanel) {
    [NXApp loadNibSection:"Help.nib" owner:self withNames:NO];
  }
  [helpPanel makeKeyAndOrderFront:sender];
  return self;
}

- activeDocument
{
  return activeDocument;
}

- setActiveDocument:aDocument
{
  char buf[100];
  
  activeDocument = aDocument;

  /* display the message of the current document */
  [activeDocumentField setStringValue:[activeDocument message]];

  /* count and display the number of documents present */
  documentCount = 0;
  [self performForDocuments:@selector(_countDocuments::) with:self];
  sprintf(buf,"There %s %d active document%s.",
  	  documentCount==1?"is":"are",
	  documentCount,
	  documentCount==1?"":"s");
  [documentCountField setStringValue:buf];

  /* adjust the menu cells accordingly */
  if (activeDocument) {
    [saveMenu setEnabled:YES];
    [saveAsMenu setEnabled:YES];
    [saveToMenu setEnabled:YES];
    [saveAllMenu setEnabled:YES];
    if ([activeDocument isDocEdited]) {
      [revertMenu setEnabled:YES];
    } else {
      [revertMenu setEnabled:NO];
    }
    [closeMenu setEnabled:YES];
  } else {
    [saveMenu setEnabled:NO];
    [saveAsMenu setEnabled:NO];
    [saveToMenu setEnabled:NO];
    [saveAllMenu setEnabled:NO];
    [revertMenu setEnabled:NO];
    [closeMenu setEnabled:NO];
  }      

  return self;
}

- _countDocuments:aDocument :sender
{
  documentCount += 1;
  return self;
}

- unsetActiveDocument:aDocument
{
  if (activeDocument == aDocument) {
    activeDocument = nil;
  }
  /* force an update of the document count */
  [self setActiveDocument:activeDocument];
  return self;
}

- open:sender
/*
 * Open one or more existing documents
 */
{
  const char *const *files;
  static const char *const fileType[2] = {DOCUMENT_TYPE, NULL};
  id openPanel;
  char fullName[MAXPATHLEN];

  openPanel = [[OpenPanel new] allowMultipleFiles:YES];

  /* run the open panel, filtering for out type of document */
  if ([openPanel runModalForTypes:fileType]) {
    /* open all the files returned by the open panel */
    files = [openPanel filenames];
    for (files = [openPanel filenames]; files && *files; files++) {\
      sprintf(fullName,"%s/%s",[openPanel directory],*files);
      if (![[Document alloc] initFromFile:fullName]) {
        NXRunAlertPanel("Open","Can't open %s", "Okay", NULL, NULL, *files);
      }
    }
  }
  return self;
}

- new:sender
/*
 * Open a new, empty document
 */
{
  [[Document alloc] init];
  return self;
}

- save:sender
{
  return [activeDocument save:sender];
}

- saveAs:sender
{
  return [activeDocument saveAs:sender];
}

- saveTo:sender
{
  return [activeDocument saveTo:sender];
}

- saveAll:sender
{
  return [self performForDocuments:@selector(_save::) with:sender];
}

- _save:aDocument :sender
{
  return [aDocument save:sender];
}

- revert:sender
{
  return [activeDocument revert:sender];
}

- close:sender
{
  return [activeDocument close:sender];
}


- performForDocuments:(SEL)aSelector with:anObject
/*
 * Send [self aSelector :aDocument :anObject] for each Document in the
 * application.  We depend on the fact that each Document has an associated
 * window and that each Document is the delegate of its window.
 */
{
  id window, windows = [NXApp windowList];
  int i=0;
  
  while (window = [windows objectAt:i++]) {
    id document = [window delegate];
    if (document && ([document class] == [Document class])) {
      [self perform:aSelector with:document with:anObject];
    }
  }
  return self;
}



- (int)app:sender openFile:(const char *)path type:(const char *)type
/*
 * This method is performed whenever a user opens a document from the Workspace
 * Manager. (This method has replaced the obsolete method appOpenFile:type:)
 */
{
  if (type && !strcmp(type, DOCUMENT_TYPE)) {
    if (![[Document alloc] initFromFile:path]) {
      NXRunAlertPanel("Open","Can't open %s","Okay",NULL,NULL,path);
      return NO;
    } else {
      return YES;
    }
  }
  return NO;
}

- (BOOL)appAcceptsAnotherFile:sender
/*
 * Inform the workspace that we can open multiple files.
 */
{
  return YES;
}

- appDidInit:sender
/*
 * When the app is launched, create an empty document if we haven't opened
 * a document already.
 */
{
  if (!activeDocument) {
    [self new:self];
  }
  return self;
}

- appWillTerminate:sender
{
  int i, editedCount, choice;
  List *editedDocuments = [[List alloc] init];

  do {
    /*
     * Make a list of edited documents and count how many there are.
     */
    [editedDocuments empty];
    [self performForDocuments:@selector(_collectEditedDocuments::)
	  with:editedDocuments];
    editedCount = [editedDocuments count];

    if (editedCount > 0) {
      /*
       * If there is one or more unsaved document, ask the user if they want
       * to review documents, quit, or abort the quit.
       */
      choice = NXRunAlertPanel(
		  "Quit",
		  "There are unsaved documents.\nReview them?",
		  "Review",		/* NX_ALERTDEFAULT */
		  "Quit Anyway",	/* NX_ALERTALTERNATE */
		  "Cancel");		/* NX_ALERTOTHER */
  
      switch (choice) {
      case NX_ALERTDEFAULT:
	/* Give the user the chance to review the edited document(s). */
	for (i=0;i<editedCount;i++) {
	  [[editedDocuments objectAt:i] close:self];
	}
        break;
      case NX_ALERTALTERNATE:
        /* user selected Quit Anyway, just go to endgame */
	editedCount = 0;
        break;
      case NX_ALERTOTHER:
        /* user selected Cancel, return nil to cancel the quit */
	[editedDocuments free];
	return nil;
	break;
      }
    }
  } while (editedCount > 0);

  /*
   * ENDGAME:
   * hide all the windows first to avoid the irritation of watching
   * each window become the main window in turn while we're closing
   * documents.  After all, we simply want to quit...
   */
  [self performForDocuments:@selector(_hide::) with:self];
  [editedDocuments free];
  /* ...and terminate without any futher ado (or review of docs) */
  return self;
}

- _collectEditedDocuments:aDocument :editedDocuments
/*
 * Collect those documents that have been modified into the editedDocuments
 * list.  Called from performForDocuments:with:.
 */ 
{
  if ([aDocument isDocEdited]) {
    [editedDocuments addObject:aDocument];
  }
  return self;
}

- _hide:aDocument :sender
/*
 * hide one document.
 */
{
  return [aDocument hideDocument:sender];
}


@end
