//
//  WBMainController.mm
//  MySQLWorkbench
//
//  Created by Alfredo Kojima on 1/Oct/08.
//  Copyright 2008 Sun Microsystems Inc. All rights reserved.
//

#import "WBMainController.h"
#import "WBMainWindow.h"
#import "MCppUtilities.h"
#import "WBToolbarManager.h"
#import "WBMenuManager.h"

#import "WBPluginBase.h"
#import "WBPluginPanel.h"
#import "WBPluginEditorBase.h"
#import "WBPluginWindowBase.h"

#import "WBModelDiagramPanel.h"

#include "workbench/wb_context_ui.h"
#import "sigobjc++.h"

#include "grtui/gui_plugin_base.h"

#import "GRTShellViewController.h"
#import "WBOutputPanelController.h"
#import "WBExceptions.h"

#import "WBDiagramSizeController.h"

#include <mforms/mforms.h>

#include "workbench/wb_module.h"


static GThread *mainthread= 0;

@implementation WBMainController

static void showInternalError(const std::string &title, const std::string &message)
{
  NSAutoreleasePool *pool= [[NSAutoreleasePool alloc] init];
  
  NSRunAlertPanel([NSString stringWithUTF8String:title.c_str()],
                  [NSString stringWithUTF8String:message.c_str()],
                  NSLocalizedString(@"OK", @"dialog window"), nil, nil);
  
  [pool release];
}


static void showError(const std::string &title, const std::string &message)
{
  NSAutoreleasePool *pool= [[NSAutoreleasePool alloc] init];
  
  NSRunAlertPanel([NSString stringWithUTF8String:title.c_str()],
                  [NSString stringWithUTF8String:message.c_str()],
                  NSLocalizedString(@"OK", @"dialog window"), nil, nil);  
  
  [pool release];
}


static void showWarning(const std::string &title, const std::string &message)
{
  NSAutoreleasePool *pool= [[NSAutoreleasePool alloc] init];
  
  NSRunAlertPanel([NSString stringWithUTF8String:title.c_str()],
                  [NSString stringWithUTF8String:message.c_str()],
                  NSLocalizedString(@"OK", @"dialog window"), nil, nil);  
  
  [pool release];
}


static void showMessage(const std::string &title, const std::string &message)
{
  NSAutoreleasePool *pool= [[NSAutoreleasePool alloc] init];
  
  NSRunAlertPanel([NSString stringWithUTF8String:title.c_str()],
                  [NSString stringWithUTF8String:message.c_str()],
                  NSLocalizedString(@"OK", @"dialog window"), nil, nil);  
  
  [pool release];
}


static int confirmAction(const std::string &title, const std::string &message, 
                         const std::string &yes_text, const std::string &no_text, const std::string &other_text)
{
  NSAutoreleasePool *pool= [[NSAutoreleasePool alloc] init];
  
  int ret= NSRunAlertPanel([NSString stringWithUTF8String:title.c_str()],
                  [NSString stringWithUTF8String:message.c_str()],
                  yes_text.empty() ? NSLocalizedString(@"OK", @"dialog window"):[NSString stringWithUTF8String:yes_text.c_str()],
                  no_text.empty()  ? NSLocalizedString(@"Cancel", @"dialog window"):[NSString stringWithUTF8String:no_text.c_str()],
                  other_text.empty()?nil:[NSString stringWithUTF8String:other_text.c_str()]);  
  
  [pool release];
  
  if (ret == NSAlertDefaultReturn)
    return 1;
  else if (ret == NSAlertAlternateReturn)
    return 0;
  else
    return -1;
}


static std::string showFileDialog(const std::string &type, const std::string &title, const std::string &extensions)
{   
  NSMutableArray *fileTypes= [NSMutableArray array];
  std::vector<std::string> exts(bec::split_string(extensions,","));
   
  for (std::vector<std::string>::const_iterator iter= exts.begin();
       iter != exts.end(); ++iter)
  {   
    if (iter->find('|') != std::string::npos)
    {
      std::string ext= iter->substr(iter->find('|')+1);
      ext= bec::replace_string(ext, "*.", "");
      [fileTypes addObject:[NSString stringWithUTF8String:ext.c_str()]];
    }
    else
      [fileTypes addObject:[NSString stringWithUTF8String:iter->c_str()]];
  }  
  
  if (type == "open")
  {
    NSOpenPanel *panel= [NSOpenPanel openPanel];

    [panel setTitle:[NSString stringWithUTF8String:title.c_str()]];
    
    if ([panel runModalForTypes:fileTypes] == NSFileHandlingPanelOKButton)
      return [[panel filename] UTF8String];
  }
  else if (type == "save")
  {
    NSSavePanel *panel= [NSSavePanel savePanel];
    
    [panel setTitle:[NSString stringWithUTF8String:title.c_str()]];
    
    [panel setAllowedFileTypes:fileTypes];

    if ([panel runModal] == NSFileHandlingPanelOKButton)
      return [[panel filename] UTF8String];
  }
  
  return "";
}


- (void)inputSheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
{
  *(NSInteger*)contextInfo= returnCode;
}


- (IBAction)inputDialogClose:(id)sender
{
  [NSApp stopModal];
  [NSApp endSheet: inputDialog returnCode:[sender tag]];
}

static bool requestInput(const std::string &message, int flags, std::string &ret_text, WBMainController *self)
{
  if (!self->inputDialog)
    [NSBundle loadNibNamed:@"InputDialog" owner:self];

  [self->inputDialogMessage setStringValue: [NSString stringWithCPPString: message]];
  [self->inputDialogMessage sizeToFit];
  [self->inputDialogText setStringValue: [NSString stringWithCPPString: ret_text]];

  NSInteger inputDialogResult= 0;
  
  [NSApp beginSheet: self->inputDialog
     modalForWindow: [self->mainWindow window]
      modalDelegate: self
     didEndSelector: @selector(inputSheetDidEnd:returnCode:contextInfo:)
        contextInfo: &inputDialogResult];
  [NSApp runModalForWindow: self->inputDialog];
  [self->inputDialog orderOut:nil];
  ret_text= [[self->inputDialogText stringValue] UTF8String];
  
  return inputDialogResult > 0;
}



static void windowShowStatusText(const std::string &text, WBMainWindow *window)
{
  NSString *string= [[NSString alloc] initWithUTF8String:text.c_str()];

  // setStatusText must release the param
  [window performSelectorOnMainThread:@selector(setStatusText:)
                           withObject:string
                        waitUntilDone:NO];
}


static bool windowShowProgress(const std::string &,const std::string &,float, WBMainWindow *window)
{
  return true;
}


static NativeHandle windowOpenPlugin(bec::GRTManager *grtm, 
                                     grt::Module *ownerModule, const std::string &shlib, const std::string &class_name,
                                     const grt::BaseListRef &args, bec::GUIPluginFlags flags, WBMainWindow *window)
{
  std::string path= ownerModule->path();

  // Check if this is a bundle plugin or a plain dylib plugin
  if (g_str_has_suffix(shlib.c_str(), ".mwbplugin"))
  {
    NSBundle *pluginBundle;
    NSString *bundlePath;
    
    // For bundled plugins, we load it, find the requested class and instantiate it.
    
    // determine the path for the plugin bundle by stripping Contents/Framework/dylibname 
    bundlePath= [[[[NSString stringWithCPPString:path] stringByDeletingLastPathComponent] stringByDeletingLastPathComponent] stringByDeletingLastPathComponent];
    
    NSLog(@"opening plugin bundle %@ ([%s initWithModule:GRTManager:arguments:...])", bundlePath, class_name.c_str());
    
    pluginBundle= [NSBundle bundleWithPath: bundlePath];
    if (!pluginBundle)
    {
      NSLog(@"plugin bundle %@ for plugin %s could not be loaded", bundlePath, path.c_str());
      NSRunAlertPanel(NSLocalizedString(@"Error Opening Plugin", @"plugin open error"),
                      [NSString stringWithFormat:NSLocalizedString(@"The plugin %s could not be loaded.", @"plugin open error"),
                       shlib.c_str()],
                      NSLocalizedString(@"OK", @"dialog window"), nil, nil);
      return 0;
    }
    /* debug msg
    if (![pluginBundle isLoaded])
      NSLog(@"plugin bundle is not yet loaded");
    else
      NSLog(@"plugin bundle is supposed to be already loaded");
        */
    Class pclass= [pluginBundle classNamed:[NSString stringWithUTF8String:class_name.c_str()]];
    if (!pclass)
    {
      NSLog(@"plugin class %s was not found in bundle %@", class_name.c_str(), bundlePath);
      
      NSRunAlertPanel(NSLocalizedString(@"Error Opening Plugin", @"plugin open error"),
                      [NSString stringWithFormat:@"The plugin %s does not contain the published object %s",
                       shlib.c_str(), class_name.c_str()],
                      NSLocalizedString(@"OK", @"dialog window"), nil, nil);
      return 0;
    }
    
    if (!(flags & bec::ForceNewWindowFlag))
    {
      // Check if there is already a panel with an editor for this plugin type.
      id existingPanel = [window findPanelForPluginType: [pclass class]];
      if (existingPanel != nil)
      {
        // drop the old plugin->handle mapping
        grtm->get_plugin_manager()->forget_gui_plugin_handle(existingPanel);
        
        [window activatePanel: existingPanel];
        
        if ([existingPanel respondsToSelector: @selector(pluginEditor)])
        {
          id editor= [existingPanel pluginEditor];
          
          if ([editor respondsToSelector: @selector(reinitWithArguments:)])
          {
            id oldIdentifier= [editor identifier];
            
            [[existingPanel retain] autorelease];
            
            [window closeBottomPanelWithIdentifier: oldIdentifier];
            
            [editor reinitWithArguments: args];
            
            [window addBottomPanel: existingPanel];
          }
        }
                
        return existingPanel;
      }
    }
    
    // Instantiate and initialize the plugin.
    id plugin= [[pclass alloc] initWithModule:ownerModule GRTManager:grtm arguments:args];
    
    //NSLog(@"CREATED PLUGIN %@ %@ %@", plugin, [plugin identifier], [plugin title]);
    
    if ([plugin isKindOfClass: [WBPluginEditorBase class]])
    {
      WBPluginPanel *panel= [[[WBPluginPanel alloc] initWithPlugin: [plugin autorelease]] autorelease];

      [window addBottomPanel: panel];
    
      return panel;
    }
    else if ([plugin isKindOfClass: [WBPluginWindowBase class]])
    {
      [plugin show];
      return plugin;
    }
    else
    {
      NSLog(@"Plugin %@ is of unknown type", plugin);
      [plugin release];
      return nil;
    }
  }
  else
  {
    NSLog(@"open_plugin() called for an unknown plugin type");
    return 0;
  }
}


static void windowShowPlugin(NativeHandle handle, WBMainWindow *window)
{
  id plugin= (id)handle;
  
  if ([plugin respondsToSelector:@selector(topView)])
  {
    [window reopenBottomTabViewItem:[plugin identifier]];
  }
}


static void windowHidePlugin(NativeHandle handle, WBMainWindow *window)
{
}


static void windowPerformCommand(const std::string &command, WBMainWindow *window, WBMainController *main)
{  
  if (command == "reset_layout")
    [window resetWindowLayout];
  else if (command == "show_grt_shell")
    [window showGRTShell:nil];
  else if (command == "overview.mysql_model")
    [window showMySQLOverview:nil];
  else if (command == "show_about")
    [[window owner] showAbout:nil];
  
  else if (command == "diagram_size")
    [main showDiagramProperties:nil];
  
  else if (command == "show_output_form")
    [window showOutput:nil];
  else if (command == "find")
    [window focusSearchField:nil];
  else if (command == "wb.page_setup")
    [main showPageSetup:nil];
//    show_page_setup();
  /*
   // Help
   else if (command == "help_index")
   Help.ShowHelp(null, System.IO.Path.Combine(Application.StartupPath, "MySQLWorkbench.chm"));
   else if (command == "help_version_check")
   Program.CheckForNewVersion();
   */

  else if (command == "wb.grtsidebarHide")
    [window toggleGRTSidebar:nil];
  //else if (command == "wb.sidebarHide")
  //{
 // }
  else
  {
    NSLog(@"unhandled command %s", command.c_str());
  }
  
}


static mdc::CanvasView* windowCreateView(const std::string &oid, const std::string &name, WBMainWindow *window)
{  
  return [window createView:oid.c_str()
                       name:name.c_str()];
}


static void windowDestroyView(mdc::CanvasView *view, WBMainWindow *window)
{
  [window destroyView:view];
}


static void windowSwitchedView(mdc::CanvasView *cview, WBMainWindow *window)
{
  [window switchToDiagramWithIdentifier:cview->get_tag().c_str()];
}


static void windowToolChanged(mdc::CanvasView *canvas, WBMainWindow *window)
{
  if ([[window selectedMainPanel] isKindOfClass: [WBModelDiagramPanel class]])
  {
    [(WBModelDiagramPanel*)[window selectedMainPanel] canvasToolChanged:canvas];
    
    NSView* toolbar = [(WBModelDiagramPanel*)[window selectedMainPanel] toolsToolbar];
    [[window toolbarManager] refreshToolsToolbar: toolbar];
  }
}


static void windowRefreshGui(wb::RefreshType type, const std::string &arg1, NativeHandle arg2, WBMainWindow *window)
{
  [window refreshGUI:type argument1:arg1 argument2:arg2];
}


static void windowLockGui(bool lock, WBMainWindow *window)
{
  [window blockGUI:lock];
}


static void windowPopupMenu(bec::MenuItemList *items, int x, int y, WBMainWindow *window)
{
  [window popupDiagramMenu:items atPoint: NSMakePoint(x, y)];
}


static void windowPrintOutput(const std::string &text, WBMainWindow *window)
{
  [[window outputPanel] addOutputText: [NSString stringWithUTF8String:text.c_str()]];
}


static void windowShellOutput(const std::string &text, WBMainWindow *window)
{
  [[window shell] write: [NSString stringWithUTF8String: text.c_str()]];
}


static void windowNextShellCommand(const std::string &prompt, WBMainWindow *window)
{
  [[window shell] nextShellCommand: [NSString stringWithUTF8String:prompt.c_str()]];
}


static void quitApplication()
{
  [NSApp terminate:nil];
}


- (void)applicationWillTerminate:(NSNotification *)aNotification
{
  _wbui->get_wb()->finalize();
  // is crashing delete _wbui;
}


static void call_copy(wb::WBContextUI *wbui)
{
  if (wbui->get_active_form() && wbui->get_active_form()->can_copy())
    wbui->get_active_form()->copy();
  else
    [[[NSApp keyWindow] firstResponder] tryToPerform:@selector(copy:) with:nil];
}

static void call_cut(wb::WBContextUI *wbui)
{
  if (wbui->get_active_form() && wbui->get_active_form()->can_cut())
    wbui->get_active_form()->cut();
  else    
    [[[NSApp keyWindow] firstResponder] tryToPerform:@selector(cut:) with:nil];
}

static void call_paste(wb::WBContextUI *wbui)
{
  if (wbui->get_active_form() && wbui->get_active_form()->can_paste())
    wbui->get_active_form()->paste();
  else    
    [[[NSApp keyWindow] firstResponder] tryToPerform: @selector(paste:) with:nil];
}

static void call_select_all(wb::WBContextUI *wbui)
{
  if (wbui->get_active_form())
    wbui->get_active_form()->select_all();
  else    
    [[[NSApp keyWindow] firstResponder] tryToPerform: @selector(selectAll:) with:nil];
}

static void call_delete(wb::WBContextUI *wbui)
{
  if (wbui->get_active_form())
    wbui->get_active_form()->delete_selection();
  else    
  {
    id responder= [[NSApp keyWindow] firstResponder];

    if (![responder tryToPerform: @selector(delete:) with: nil])
      [responder tryToPerform: @selector(deleteBackward:) with:nil];
  }
}

static bool validate_copy(wb::WBContextUI *wbui)
{
  if (wbui->get_active_form() && wbui->get_active_form()->can_copy())
    return true;
  return [[[NSApp keyWindow] firstResponder] respondsToSelector: @selector(copy:)];
}


static bool validate_paste(wb::WBContextUI *wbui)
{
  if (wbui->get_active_form() && wbui->get_active_form()->can_paste())
    return true;
  return [[[NSApp keyWindow] firstResponder] respondsToSelector: @selector(paste:)];
}

static bool validate_select_all(wb::WBContextUI *wbui)
{
  if (wbui->get_active_form())
    return true;
  return [[[NSApp keyWindow] firstResponder] respondsToSelector: @selector(selectAll:)];
}


static bool validate_delete(wb::WBContextUI *wbui)
{
  if (wbui->get_active_form() && wbui->get_active_form()->can_delete())
    return true;
  
  id responder= [[NSApp keyWindow] firstResponder];
  
  if ([responder respondsToSelector: @selector(canDeleteItem:)])
    return [responder performSelector: @selector(canDeleteItem:) withObject: nil];

  return [responder respondsToSelector: @selector(deleteBackward:)];
}


static void call_closetab(WBMainWindow *mainWindow)
{
  [mainWindow closePanel: [mainWindow activePanel]];
}


static bool validate_closetab(WBMainWindow *mainWindow)
{  
  // find where this belongs to
  return [mainWindow activePanel] != nil;
}


static void call_search(WBMainWindow *mainWindow)
{
  [mainWindow performSearchObject:nil];
}


- (void)registerCommandsWithBackend
{
  std::list<std::string> commands;
  
  commands.push_back("overview.mysql_model");
  commands.push_back("show_grt_shell");
  commands.push_back("show_about");
  commands.push_back("diagram_size");
  commands.push_back("show_output_form");
  //  commands.push_back("view_model_navigator");
  //  commands.push_back("view_catalog");
  //  commands.push_back("view_layers");
  //  commands.push_back("view_user_datatypes");
  //  commands.push_back("view_object_properties");
  //  commands.push_back("view_object_description");
  //  commands.push_back("view_undo_history");
//  commands.push_back("reset_layout");
  commands.push_back("find"); 
  commands.push_back("wb.page_setup");
  //  commands.push_back("help_index");
  //  commands.push_back("help_version_check");

  commands.push_back("wb.grtsidebarHide");
//  commands.push_back("wb.sidebarHide");
  
  _wbui->get_command_ui()->add_frontend_commands(commands);

  _wbui->get_command_ui()->add_builtin_command("closetab", sigc::bind(sigc::ptr_fun(call_closetab), mainWindow), 
                                               sigc::bind(sigc::ptr_fun(validate_closetab), mainWindow));
  
  _wbui->get_command_ui()->add_builtin_command("searchbox", sigc::bind(sigc::ptr_fun(call_search), mainWindow));
                                               //sigc::bind(sigc::ptr_fun(validate_closetab), mainWindow));
  
  _wbui->get_command_ui()->add_builtin_command("copy", sigc::bind(sigc::ptr_fun(call_copy), _wbui), 
                                               sigc::bind(sigc::ptr_fun(validate_copy), _wbui));
  _wbui->get_command_ui()->add_builtin_command("cut", sigc::bind(sigc::ptr_fun(call_cut), _wbui), 
                                               sigc::bind(sigc::ptr_fun(validate_copy), _wbui));
  _wbui->get_command_ui()->add_builtin_command("paste", sigc::bind(sigc::ptr_fun(call_paste), _wbui), 
                                               sigc::bind(sigc::ptr_fun(validate_paste), _wbui));
  _wbui->get_command_ui()->add_builtin_command("delete", sigc::bind(sigc::ptr_fun(call_delete), _wbui),
                                               sigc::bind(sigc::ptr_fun(validate_delete), _wbui));
  _wbui->get_command_ui()->add_builtin_command("selectAll", sigc::bind(sigc::ptr_fun(call_select_all), _wbui), 
                                               sigc::bind(sigc::ptr_fun(validate_select_all), _wbui));
}


static void set_clipboard_text(const std::string &text)
{
  [[NSPasteboard generalPasteboard] declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
  [[NSPasteboard generalPasteboard] setString:[NSString stringWithCPPString:text]
                                      forType:NSStringPboardType];
}


static void flush_main_thread()
{
  // flush stuff that could be called with performSelectorOnMainThread:
  [[NSRunLoop currentRunLoop] acceptInputForMode:NSModalPanelRunLoopMode beforeDate:[NSDate date]];
}


- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
  if (_initFinished)
  {
    if (_wb->open_document([filename fileSystemRepresentation]))
      return YES;
  }
  else
  {
    _options->open_at_startup= [filename fileSystemRepresentation];
    return YES;
  }
  return NO;
}


- (void)applicationDidFinishLaunching:(NSNotification*)notification
{
  _wb->get_ui()->init_finish(_options);
  _initFinished= YES;
  [[NSNotificationCenter defaultCenter] addObserver: self
                                           selector: @selector(windowDidBecomeKey:)
                                               name: NSWindowDidBecomeKeyNotification
                                             object: nil];
}


- (void)windowDidBecomeKey:(NSNotification*)notification
{
  if (![[notification object] isMainWindow])
    [[mainWindow menuManager] rebuildMainMenuForAuxiliaryWindow];
  else
    [[mainWindow menuManager] rebuildMainMenu];
}


static NSString *applicationSupportFolder()
{
  NSArray *res= NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);

  if ([res count] > 0)
    return [res objectAtIndex: 0];
  return @"/tmp/";
}


- (void)setupBackend
{
  mainthread= g_thread_self();

  try
  {
    bec::GRTManager *grtm;
    
    // Setup backend stuff
    _wbui = new wb::WBContextUI(true);
    _wb = _wbui->get_wb();
    
    grtm = _wb->get_grt_manager();
    grtm->get_dispatcher()->set_main_thread_flush_and_wait(flush_main_thread);
    
    [mainWindow setWBContext: _wbui];
    [mainWindow setOwner:self];
    
    // Define a set of methods which backend can call to interact with user and frontend
    wb::WBFrontendCallbacks wbcallbacks;
    
    // Assign those callback methods
    wbcallbacks.show_internal_error= sigc::ptr_fun(showInternalError);
    wbcallbacks.show_error= sigc::ptr_fun(showError);
    wbcallbacks.show_warning= sigc::ptr_fun(showWarning);
    wbcallbacks.show_message= sigc::ptr_fun(showMessage);
    wbcallbacks.confirm_action= sigc::ptr_fun(confirmAction);
    wbcallbacks.show_file_dialog= sigc::ptr_fun(showFileDialog);
    wbcallbacks.show_status_text= sigc::bind(sigc::ptr_fun(windowShowStatusText), mainWindow);
    wbcallbacks.show_progress= sigc::bind(sigc::ptr_fun(windowShowProgress), mainWindow);
    wbcallbacks.request_input= sigc::bind(sigc::ptr_fun(requestInput), self);
    wbcallbacks.open_editor= sigc::bind(sigc::ptr_fun(windowOpenPlugin), mainWindow);
    wbcallbacks.show_editor= sigc::bind(sigc::ptr_fun(windowShowPlugin), mainWindow);
    wbcallbacks.hide_editor= sigc::bind(sigc::ptr_fun(windowHidePlugin), mainWindow);
    wbcallbacks.perform_command= sigc::bind(sigc::ptr_fun(windowPerformCommand), mainWindow, self);
    wbcallbacks.create_view= sigc::bind(sigc::ptr_fun(windowCreateView), mainWindow);
    wbcallbacks.destroy_view= sigc::bind(sigc::ptr_fun(windowDestroyView), mainWindow);
    wbcallbacks.switched_view= sigc::bind(sigc::ptr_fun(windowSwitchedView), mainWindow);
    wbcallbacks.tool_changed= sigc::bind(sigc::ptr_fun(windowToolChanged), mainWindow);
    wbcallbacks.refresh_gui= sigc::bind(sigc::ptr_fun(windowRefreshGui), mainWindow);
    wbcallbacks.lock_gui= sigc::bind(sigc::ptr_fun(windowLockGui), mainWindow);
    wbcallbacks.popup_menu= sigc::bind(sigc::ptr_fun(windowPopupMenu), mainWindow);
    wbcallbacks.print_output= sigc::bind(sigc::ptr_fun(windowPrintOutput), mainWindow);
    wbcallbacks.shell_output= sigc::bind(sigc::ptr_fun(windowShellOutput), mainWindow);
    wbcallbacks.next_shell_command= sigc::bind(sigc::ptr_fun(windowNextShellCommand), mainWindow);
    wbcallbacks.quit_application= sigc::ptr_fun(quitApplication);
      
    _options= new wb::WBOptions();
//    wboptions.open_at_startup= "/tmp/sakila_full.mwb";
    _options->basedir = [[[NSBundle mainBundle] resourcePath] fileSystemRepresentation];
    _options->struct_search_path = _options->basedir + "/grt";
    _options->plugin_search_path = std::string([[[NSBundle mainBundle] builtInPlugInsPath] fileSystemRepresentation]);
    _options->module_search_path = std::string([[[NSBundle mainBundle] builtInPlugInsPath] fileSystemRepresentation]);
    _options->library_search_path = std::string([[[NSBundle mainBundle] resourcePath] fileSystemRepresentation]) + "/libraries";
    _options->cdbc_driver_search_path = std::string([[[NSBundle mainBundle] privateFrameworksPath] fileSystemRepresentation]);
    _options->user_data_dir= [[applicationSupportFolder() stringByAppendingString: @"/MySQL/Workbench"] fileSystemRepresentation];
    
    //wboptions.open_at_startup = "";
    
    _wbui->init(&wbcallbacks, _options);
    
    _wb->flush_idle_tasks();
  }
  catch (std::exception &exc)
  {
    MShowCPPException(exc);
  }
}

extern "C" {
  extern void mforms_cocoa_init();
};
static void init_mforms()
{
  extern void cf_grttreeview_init();
  static BOOL inited= NO;
  
  if (!inited)
  {
    inited= YES;
  
    mforms_cocoa_init();
    cf_grttreeview_init();
  }
}

#if 0 // for debugging

- (void)bundleLoaded:(NSNotification*)notification
{
  NSLog(@"LOADED %@", [notification object]);
  NSLog(@"Classes: %@", [[notification userInfo] objectForKey: NSLoadedClasses]);
}


- (id)init
{
  self= [super init];
  if (self)
  {
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(bundleLoaded:)
                                                 name:NSBundleDidLoadNotification
                                               object:nil];
  }
  return self;
}
#endif

- (void)awakeFromNib
{  
  if (!mainWindow)
  {    
    init_mforms();
    
    [NSApp setDelegate: self];
    
    // Setup delegate to log symbolic stack traces on exceptions.
    [[NSExceptionHandler defaultExceptionHandler] setExceptionHandlingMask: NSLogUncaughtExceptionMask | NSLogUncaughtSystemExceptionMask | NSLogUncaughtRuntimeErrorMask | NSLogTopLevelExceptionMask | NSLogOtherExceptionMask];
    [[NSExceptionHandler defaultExceptionHandler] setDelegate: [WBExceptionHandlerDelegate new]];
    
    mainWindow = [[WBMainWindow alloc] init];
    [mainWindow load];
    
    [self setupBackend];
    
    NSTimer *timer= [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(idleTasks:)
                                                   userInfo:nil repeats:YES];
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSModalPanelRunLoopMode];
    
    NSToolbar *toolbar= [[mainWindow window] toolbar];
    [toolbar setShowsBaselineSeparator: NO];
    
    [self registerCommandsWithBackend];
    
    [mainWindow showWindow:nil];

    // do the final setup for after the window is shown
    [mainWindow setupReady];    

    //XXX hack to work-around problem with opening object editors
    {
      NSString *pluginsPath= [[NSBundle mainBundle] builtInPlugInsPath];
      NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:pluginsPath];
      NSString *file;
      
      while (file = [dirEnum nextObject]) {
        if ([[file pathExtension] isEqualToString: @"mwbplugin"]) {
          NSString *path= [pluginsPath stringByAppendingPathComponent:file];
          
          NSBundle *bundle= [NSBundle bundleWithPath:path];
          [bundle load];
        }
      }
    }
  }
}


- (void)flushIdleTasks:(id)arg
{
  _wb->flush_idle_tasks();
}


- (void)idleTasks:(NSTimer*)timer
{
  static NSArray *modes= nil;
  if (!modes) 
    modes= [[NSArray arrayWithObjects:NSDefaultRunLoopMode, NSModalPanelRunLoopMode, nil] retain];
  
  // if we call flush_idle_tasks() directly here, we could get blocked by a plugin with a
  // modal loop. In that case, the timer would not get fired again until the modal loop
  // terminates, which is a problem. So we defer the execution to after this method returns.
  [[NSRunLoop currentRunLoop] performSelector:@selector(flushIdleTasks:) target:self
                                     argument:self order:0 modes:modes];
}


- (void)requestRefresh
{
  NSAutoreleasePool *pool= [[NSAutoreleasePool alloc] init];
  [self performSelector: @selector(idleTasks:)
             withObject: nil
             afterDelay: 0];
  [pool release];
}




- (IBAction)showAbout:(id)sender
{
  if (!aboutWindow)
  {
    [NSBundle loadNibNamed:@"About" owner:self];
  
#ifdef EDITION_OSS
    [[[aboutWindow contentView] viewWithTag: 1] setImage:[NSImage imageNamed:@"wb_splashscreen_oss.png"]];
#elif defined(EDITION_SE)
    [[[aboutWindow contentView] viewWithTag: 1] setImage:[NSImage imageNamed:@"wb_splashscreen_se.png"]];
#endif
    
    [[[aboutWindow contentView] viewWithTag: 2] setStringValue: [NSString stringWithFormat:@"%i.%i.%i", 
                                                                 APP_MAJOR_NUMBER, APP_MINOR_NUMBER, APP_RELEASE_NUMBER]];
    
    [[[aboutWindow contentView] viewWithTag: 3] setStringValue: [NSString stringWithFormat:@"Revision %i", 
                                                                 APP_REVISION_NUMBER]];
    
    [aboutWindow center];
  }
  [aboutWindow makeKeyAndOrderFront:nil];
}


- (IBAction)showDiagramProperties:(id)sender
{
  WBDiagramSizeController *controller= [[WBDiagramSizeController alloc] initWithWBContext:_wbui];
  
  [controller showModal];
}


- (IBAction)buttonClicked:(id)sender
{
  if ([sender tag] == 10)
  {
    [NSApp stopModalWithCode: NSOKButton];
    [pageSetup orderOut: nil];
  }
  else if ([sender tag] == 11)
  {
    [NSApp stopModalWithCode: NSCancelButton];  
    [pageSetup orderOut: nil];
  }
  else if (sender == landscapeButton)
  {
    [landscapeButton setState: NSOnState];
    [portraitButton setState: NSOffState];
  }
  else if (sender == portraitButton)
  {
    [landscapeButton setState: NSOffState];
    [portraitButton setState: NSOnState];
  }  
}


- (void)selectCollectionItem:(id)sender
{
  if (sender == paperSize)
  {
    [paperSizeLabel setStringValue: [[paperSize selectedItem] representedObject]];
    [paperSizeLabel sizeToFit];
  }
}


- (void)showPageSetup:(id)sender
{
  if (!pageSetup)
  {
    [NSBundle loadNibNamed:@"PageSetup" owner:self];
    
    [landscapeButton setImage: [[[NSImage alloc] initWithContentsOfFile: @"/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/Print.framework/Versions/A/Plugins/PrintingCocoaPDEs.bundle/Contents/Resources/Landscape.tiff"] autorelease]];
    [portraitButton setImage: [[[NSImage alloc] initWithContentsOfFile: @"/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/Print.framework/Versions/A/Plugins/PrintingCocoaPDEs.bundle/Contents/Resources/Portrait.tiff"] autorelease]];
  }
  
  app_PageSettingsRef settings(_wbui->get_page_settings());
  std::list<wb::WBPaperSize> paper_sizes= _wbui->get_paper_sizes(false);
  
  [paperSize removeAllItems];
  for (std::list<wb::WBPaperSize>::const_iterator iter= paper_sizes.begin(); iter != paper_sizes.end(); 
       ++iter)
  {
    [paperSize addItemWithTitle: [NSString stringWithCPPString: iter->name]];
    [[paperSize itemAtIndex: [paperSize numberOfItems]-1] 
     setRepresentedObject: [NSString stringWithCPPString: iter->description]];
  }
  if (settings->paperType().is_valid())
  {
    [paperSize selectItemWithTitle: [NSString stringWithCPPString: *settings->paperType()->name()]];
    [self selectCollectionItem: paperSize];
  }
  if (settings->orientation() == "landscape")
  {
    [landscapeButton setState: NSOnState];
    [portraitButton setState: NSOffState];
  }
  else
  {
    [landscapeButton setState: NSOffState];
    [portraitButton setState: NSOnState];
  }
    
  if ([NSApp runModalForWindow: pageSetup] == NSOKButton)
  {    
    std::string type= [[paperSize titleOfSelectedItem] UTF8String];

    std::string orientation;
    if ([landscapeButton state] == NSOnState)
      orientation= "landscape";
    else
      orientation= "portrait";
    
    _wbui->update_page_settings(type, orientation);    
  }
}

@end
