/*
    Controller.m

    Implementation of the ArgumentsInspector class for the
    ProjectManager application.

    Copyright (C) 2005  Saso Kiselkov

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#import "Controller.h"

#import <Foundation/NSBundle.h>
#import <Foundation/NSSortDescriptor.h>
#import <Foundation/NSString.h>
#import <Foundation/NSFileManager.h>
#import <Foundation/NSUserDefaults.h>
#import <Foundation/NSNotification.h>

#import <AppKit/NSOpenPanel.h>
#import <AppKit/NSView.h>
#import <AppKit/NSNibLoading.h>
#import <AppKit/NSTextField.h>
#import <AppKit/NSDocumentController.h>
#import <AppKit/NSMenu.h>
#import <AppKit/NSMenuItem.h>
#import <AppKit/NSApplication.h>

#import "Preferences.h"
#import "ProjectCreator.h"
#import "ProjectDocument.h"
#import "ProjectModule.h"
#import "ProjectType.h"
#import "ProjectTypeLoader.h"
#import "ProjectWindowController.h"
#import "TextFinder.h"
#import "NewFileTypeChooser.h"

/**
 * Posted when the user chooses a different document to be the
 * current document in the app. The user info dictionary can
 * either:
 *
 * - contain a single key named "Document" which contains the
 *   document the user currenty chose.
 *
 * - or be `nil' in case no document is currently active.
 */
NSString * const CurrentDocumentDidChangeNotification =
  @"CurrentDocumentDidChangeNotification";

@interface Controller (Private)

- (void) syncPipesMenu;

- (void) registerProjectMenuEntries: (NSArray *) entries;
- (void) deregisterProjectMenuEntries: (NSArray *) entries;

@end


@implementation Controller

- (void) dealloc
{
  NSDebugLLog(@"Controller", @"Controller: dealloc");

  [[NSNotificationCenter defaultCenter] removeObserver: self];

  DESTROY(projectMenuEntries);

  [super dealloc];
}

- (void) awakeFromNib
{
  pipesMenu = [pipesMenu submenu];

  projectMenu = [projectMenu submenu];
  [projectMenu removeItemAtIndex: 0];
  [projectMenu sizeToFit];
}

- (void) showPrefsPanel: (id)sender
{
  [[Preferences shared] activate];
}

- (void) newProject: sender
{
  id document;
  NSString * projectPath;
  NSString * projectName;
  NSString * templatePath;
  NSDictionary * projectInfo;
  NSError * error;

  projectInfo = [[ProjectCreator shared] getNewProjectSetupWithLocation: YES];
  if (projectInfo == nil)
    {
      return;
    }

  NSDebugLLog(@"Controller", _(@"New project info:\n%@"), projectInfo);

  projectPath = [projectInfo objectForKey: @"ProjectPath"];
  projectName = [projectInfo objectForKey: @"ProjectName"];
  templatePath = [projectInfo objectForKey: @"ProjectTemplate"];
  if ([ProjectCreator createNewProjectAtPath: projectPath
                                 projectName: projectName
                                fromTemplate: templatePath
                                       error: &error] == NO)
    {
      NSRunAlertPanel(_(@"Failed to create project"),
        _(@"I couldn't create a new project at the specified path.\n%@"),
        nil, nil, nil, [[error userInfo] objectForKey:
        NSLocalizedDescriptionKey]);

      return;
    }

  document = [[NSDocumentController sharedDocumentController]
    openDocumentWithContentsOfFile: [projectPath
    stringByAppendingPathComponent: [[projectInfo objectForKey: @"ProjectName"]
    stringByAppendingPathExtension: @"pmproj"]]
                           display: YES];
  if (document != nil)
    {
      [document saveDocument: nil];
    }
}

- (void) newSourceFile: sender
{
  NewFileTypeChooser * chooser = [NewFileTypeChooser shared];
  NSString * type;

  type = [chooser runModalWithRequiredKey: @"NSDocumentClass"
                                    value: @"SourceEditorDocument"];
  if (type != nil)
    {
      [[NSDocumentController sharedDocumentController]
        openUntitledDocumentOfType: type display: YES];
    }
}

- (void) applicationDidFinishLaunching: (NSNotification *) notif
{
  NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];

  [self syncPipesMenu];
  [nc addObserver: self
         selector: @selector(syncPipesMenu)
             name: NSUserDefaultsDidChangeNotification
           object: [NSUserDefaults standardUserDefaults]];

  /* Watches for main window changes and posts
     CurrentProjectModuleDidChangeNotification's as appropriate, so that
     inspectors can gracefully follow main window changes */
  NSDebugLLog(@"Controller", @"Controller: registering for main window changes");
  [nc addObserver: self
         selector: @selector(mainWindowChanged:)
             name: NSWindowDidBecomeMainNotification
           object: nil];
}

- (void) orderFrontFindPanel: sender
{
  [[TextFinder sharedInstance] orderFrontFindPanel: self];
}

- (void) mainWindowChanged: (NSNotification *) notif
{
  id document;
  id wc;
  NSNotificationCenter * nc = [NSNotificationCenter defaultCenter];

  wc = [[notif object] windowController];
  if ([wc respondsToSelector: @selector(currentModule)])
    {
      [nc postNotificationName: CurrentProjectModuleDidChangeNotification
                        object: self
                      userInfo: [NSDictionary
        dictionaryWithObject: [wc currentModule] forKey: @"Module"]];
    }
  else
    {
      [nc postNotificationName: CurrentProjectModuleDidChangeNotification
                        object: self
                      userInfo: nil];
    }

  // deregister old project menu entries
  if (projectMenuEntries != nil)
    {
      [self deregisterProjectMenuEntries: projectMenuEntries];
      DESTROY(projectMenuEntries);
    }

  document = [wc document];
  if (document != nil)
    {
      [nc postNotificationName: CurrentDocumentDidChangeNotification
                        object: self
                      userInfo: [NSDictionary
        dictionaryWithObject: document forKey: @"Document"]];

      // and see if this document provides project menu entries
      document = [wc document];
      if ([document respondsToSelector: @selector(projectMenuEntries)])
        {
          ASSIGN(projectMenuEntries, [document projectMenuEntries]);
          [self registerProjectMenuEntries: projectMenuEntries];
        }
    }
  else
    {
      [nc postNotificationName: CurrentDocumentDidChangeNotification
                        object: self
                      userInfo: nil];
    }
}

@end

@implementation Controller (Private)

- (void) syncPipesMenu
{
  unsigned int i, n;
  NSDictionary * userPipes;
  NSArray * allItems;
  NSEnumerator * e;
  NSString * itemTitle;

  for (i = 0, n = [pipesMenu numberOfItems]; i<n; i++)
    {
      id <NSMenuItem> item = [pipesMenu itemAtIndex: i];

      if (sel_eq([item action], @selector(pipeOutput:)))
        {
          [pipesMenu removeItemAtIndex: i];
          i--;
          n--;
        }
    }

  userPipes = [[NSUserDefaults standardUserDefaults]
    objectForKey: @"UserPipes"];
  allItems = [[userPipes allKeys] sortedArrayUsingSelector:
    @selector(caseInsensitiveCompare:)];
  e = [allItems objectEnumerator];
  while ((itemTitle = [e nextObject]) != nil)
    {
      [pipesMenu insertItemWithTitle: itemTitle
                              action: @selector(pipeOutput:)
                       keyEquivalent: [[userPipes objectForKey: itemTitle]
        objectForKey: @"KeyEquivalent"]
                             atIndex: [allItems indexOfObject: itemTitle] + 1];
    }
}

- (void) registerProjectMenuEntries: (NSArray *) entries
{
  NSEnumerator * e;
  id <NSMenuItem> item;

  NSDebugLLog(@"Controller", @"Controller: registering project menu "
    @"entries %@.", entries);

  e = [entries objectEnumerator];
  while ((item = [e nextObject]) != nil)
    {
      [projectMenu addItem: item];
    }
}

- (void) deregisterProjectMenuEntries: (NSArray *) entries
{
  NSEnumerator * e;
  id <NSMenuItem> item;

  NSDebugLLog(@"Controller", @"Controller: deregistering project menu "
    @"entries %@.", entries);

  e = [entries objectEnumerator];
  while ((item = [e nextObject]) != nil)
    {
      [projectMenu removeItem: item];
    }
}

@end
