using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using WeifenLuo.WinFormsUI.Docking;
using MySQL.Grt;
using MySQL.Workbench;
using MySQL.GUI.Shell;
using MySQL.GUI.Mdc;
using System.Reflection;
using System.Runtime.InteropServices;
using MySQL.GUI.Workbench.Plugins;
using Aga.Controls.Tree;
using MySQL.Utilities;
using MySQL.GUI.Workbench.Properties;

namespace MySQL.GUI.Workbench
{
  public partial class MainForm : Form, IMySQLMainForm
  {
    #region Member Variables

    // The Workbench context
    protected WbContext wbContext;
    // the GRT manager
    protected GrtManager grtManager;

    // a timer for helping backend timers
    private System.Windows.Forms.Timer timer = null;
    
    // for debugging/info
    private ToolStripLabel infoStripLabel = null;

    // Dock Forms
    private ModelToolbarForm modelToolbarForm = new ModelToolbarForm();
    private ModelCatalogForm modelCatalogForm;
    private ModelLayerForm modelLayerForm;
    private ModelNavigatorForm modelNavigatorForm;
    private ModelPropertiesForm modelPropertiesForm;
    private ModelObjectDescriptionForm modelObjectDescriptionForm;
    private UserDatatypesForm userDatatypesForm;
    
    // Special Forms
    private GrtShellForm grtShellForm = null;
    private DockContent grtInspectorForm = null;
    private DockContent grtGlobalsTreeForm = null;
    private DockContent grtFileExplorer = null;
    private GrtStructsForm grtStructsForm = null;
    private GrtModulesForm grtModulesForm = null;

    private WorkbenchOverviewForm workbenchMainOverviewForm = null;
    private WorkbenchOverviewForm workbenchPhysicalOverviewForm = null;
    private WorkbenchMenuManager workbenchMenuManager = null;
    private WorkbenchToolbarManager workbenchToolbarManager = null;

    private WorkbenchOutputForm outputForm = null;

    private UndoHistoryForm historyForm= null;

    private Form findForm = null;

    // Set when restoring application state. Used to set the active document tab
    // if an existing model is loaded at startup.
    private String activeDocumentTitle = "";

    private bool shuttingDown = false;

    #endregion

    #region Constructors

    /// <summary>
    /// Standard constructor
    /// </summary>
    private MainForm()
    {
      InitializeComponent();
    }

    /// <summary>
    /// Constructor that takes a WbContext and passes it to the sub-forms that get created
    /// </summary>
    /// <param name="WbContext">The WbContext Backend Wrapper</param>
    public MainForm(WbContext WbContext)
      : this()
    {
      wbContext = WbContext;
      grtManager = wbContext.get_grt_manager();

      // suspend dock layout to prevent flicker
      mainDockPanel.SuspendLayout(true);
      modelSidebarDockPanel.SuspendLayout(true);
      try
      {

        // create Sidebar
        sidebarFlatTabControl.Hide();
        mainSplitContainer.Panel2.Controls.Add(modelSidebarDockPanel);
        modelSidebarDockPanel.Dock = DockStyle.Fill;


        modelNavigatorForm = new ModelNavigatorForm(wbContext);
        userDatatypesForm = new UserDatatypesForm(wbContext);
        modelLayerForm = new ModelLayerForm(wbContext);
        modelCatalogForm = new ModelCatalogForm(wbContext);
        historyForm = new UndoHistoryForm(wbContext);

        workbenchMenuManager = new WorkbenchMenuManager(wbContext);
        workbenchToolbarManager = new WorkbenchToolbarManager(wbContext, toolBarStrip, workToolStrip);

        modelPropertiesForm = new ModelPropertiesForm(wbContext);
        modelObjectDescriptionForm = new ModelObjectDescriptionForm(wbContext);

        outputForm = new WorkbenchOutputForm(wbContext);

      }
      finally
      {
        // resume suspended dock layout
        mainDockPanel.ResumeLayout(true, true);
        modelSidebarDockPanel.ResumeLayout(true, true);
      }


      // Create a timer to be triggered when the backend needs
      timer = new System.Windows.Forms.Timer();
      timer.Tick += new EventHandler(timer_Tick);

      // Prepare Statusbar
      infoStripLabel = new ToolStripLabel();
      infoStripLabel.AutoSize = false;
      infoStripLabel.Width = 50;
      mainStatusStrip.Items.Add(infoStripLabel);

      PictureBox statusStripImg = new PictureBox();
      statusStripImg.SizeMode = PictureBoxSizeMode.CenterImage;
      statusStripImg.Image = Resources.statusbar_separator;
      statusStripImg.BackColor = Color.Transparent;
      mainStatusStrip.Items.Add(new ToolStripControlHost(statusStripImg));


      // output img
      statusStripImg = new PictureBox();
      statusStripImg.Name = "grtShellStripButton";
      statusStripImg.SizeMode = PictureBoxSizeMode.CenterImage;
      statusStripImg.Image = Resources.statusbar_output;
      statusStripImg.BackColor = Color.Transparent;
      statusStripImg.Click += new System.EventHandler(grtOutputImg_Click);
      mainStatusStrip.Items.Add(new ToolStripControlHost(statusStripImg));
      mainFormToolTip.SetToolTip(statusStripImg, "Display Output Window");

      statusStripImg = new PictureBox();
      statusStripImg.SizeMode = PictureBoxSizeMode.CenterImage;
      statusStripImg.Image = Resources.statusbar_separator;
      statusStripImg.BackColor = Color.Transparent;
      mainStatusStrip.Items.Add(new ToolStripControlHost(statusStripImg));
    }

    #endregion

    #region Implement Interfaces

    delegate void DelegateFunc();

    public string StatusBarText
    {
      get { return statusLbl.Text; }
      set 
      {
        if (InvokeRequired)
        {
          DelegateFunc f = delegate
          {
            statusLbl.Text = value.Replace("\r\n", "\n").Replace("\n", "");
          };

          Invoke(f);
        }
        else
          statusLbl.Text = value.Replace("\r\n", "\n").Replace("\n", "");
      }
    }

    #endregion

    #region Properties

    public GrtShellForm GrtShellForm
    {
      get { return grtShellForm; }
      set 
      {
        if (grtShellForm != value)
        {
          if (grtShellForm != null)
            grtShellForm.DockStateChanged -= new EventHandler(grtShellForm_DockStateChanged); 
          grtShellForm = value;
          grtShellForm.DockStateChanged += new EventHandler(grtShellForm_DockStateChanged);
          grtShellForm.NewShellDocument += new NewShellDocument(grtShellForm_NewShellDocument);
        }
      }
    }

    void grtShellForm_DockStateChanged(object sender, EventArgs e)
    {
      if (grtShellForm.IsHidden)
        HideGrtIde();
      else
        ShowGrtIde();
    }

    bool grtShellForm_NewShellDocument(DockContent documentForm)
    {
      documentForm.Show(mainDockPanel);

      return true;
    }

    public WorkbenchOverviewForm WorkbenchMainOverviewForm
    {
      get { return workbenchMainOverviewForm; }
    }

    public WorkbenchOverviewForm WorkbenchPhysicalOverviewForm
    {
      get { return workbenchPhysicalOverviewForm; }
    }

    #endregion

    #region Callbacks

    /// <summary>
    /// Routine to print an error message
    /// </summary>
    /// <param name="message">The message</param>
    /// <param name="detail">Message details</param>
    public void ShowErrorText(String message, String detail)
    {
      MessageBox.Show(
        string.Format("{0}" + Environment.NewLine + "{1}", message, detail), 
        "MySQL Workbench Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }

    public void ShowStatusText(String message)
    {
      StatusBarText = message;
    }

    public bool ShowProgress(String title, String detail, float progress)
    {
      if (progress < 0)
      {
        if (title != "" && detail != "")
          StatusBarText = title + ": " + detail;
        else if (title != "")
          StatusBarText = title;
        else
          StatusBarText = "Finished.";

        statusProgress.Visible = false;
        return false;
      }
      else if (!statusProgress.Visible)
        statusProgress.Visible = true;

      if (title != "" && detail != "")
        StatusBarText = title + ": " + detail;
      else
        StatusBarText = title;

      statusProgress.Value = (int)(progress * 100);

      return false;
    }

    private bool RequestInputPressedOk;

    public void OnRequestInputButton(Object sender, EventArgs e)
    {
      Button b = sender as Button;
      if (b == null)
        return;
      Form f = b.Parent as Form;
      if (f == null)
        return;
      f.Close();
      RequestInputPressedOk = (b.Text == "OK");
    }

    public bool RequestInput(String title, int flags, out String result)
    {
      this.RequestInputPressedOk = false;
      result = "";

      Form dialog = new Form();
      dialog.Text = title;
      dialog.Size = new Size(350, 125);
      dialog.StartPosition = FormStartPosition.CenterParent;
      dialog.FormBorderStyle = FormBorderStyle.FixedDialog;

      Label prompt = new Label();
      prompt.Location = new Point(10, 5);
      prompt.Text = title;

      TextBox input = new TextBox();
      input.Location = new Point(10, 30);
      input.Size = new Size(dialog.Size.Width - (input.Location.X*2 + 1), 50);

      Button ok = new Button();
      ok.Text = "OK"; // if you rename this also update OnRequestInputButton()
      ok.Location = new Point(dialog.Size.Width/2 - ok.Size.Width - 10, 
        input.Location.Y + input.Size.Height + 10);
      ok.Click += new EventHandler(OnRequestInputButton);
      
      Button cancel = new Button();
      cancel.Text = "Cancel";
      cancel.Location = new Point(dialog.Size.Width/2 + 10, 
        input.Location.Y + input.Size.Height + 10);
      cancel.Click += new EventHandler(OnRequestInputButton);

      dialog.Controls.Add(prompt);
      dialog.Controls.Add(input);
      dialog.Controls.Add(ok);
      dialog.Controls.Add(cancel);
      dialog.ShowDialog();

      if (RequestInputPressedOk)
        result = input.Text;

      return RequestInputPressedOk;
    }

    public void RefreshGUI(RefreshType refresh, String str, IntPtr ptr)
    {
      SuspendLayout();

      switch (refresh)
      {
        case RefreshType.RefreshCloseDocument:
        {
          wbContext.flush_idle_tasks();
          wbContext.close_document_finish();
          if (workbenchMainOverviewForm != null)
          {
            workbenchMainOverviewForm.resetDocument(true);
          }
          if (workbenchPhysicalOverviewForm != null)
          {
            workbenchPhysicalOverviewForm.resetDocument(true);
          }
          break;
        }
        case RefreshType.RefreshNewView:
        {
          BaseWindowsCanvasView canvas = BaseWindowsCanvasView.GetFromFixedId(ptr);
          if (canvas != null)
          {
            ModelDiagramForm modelViewForm = canvas.GetOwnerForm() as ModelDiagramForm;

            if (modelViewForm != null)
              modelViewForm.Show(mainDockPanel);
          }
          break;
        }
        case RefreshType.RefreshViewName:
        {
          BaseWindowsCanvasView canvas = BaseWindowsCanvasView.GetFromFixedId(ptr);
          if (canvas != null)
          {
            ModelDiagramForm modelViewForm = canvas.GetOwnerForm() as ModelDiagramForm;

            if (modelViewForm != null)
              modelViewForm.TabText = str;
          }
          break;
        }

        case RefreshType.RefreshOverviewNodeInfo:
          //if (wbContext.get_main_overview().matches_handle(ptr))
          //  workbenchMainOverviewForm.RefreshNodeInfo(new NodeId(str));
          if (ptr == null || wbContext.get_physical_overview().matches_handle(ptr))
            workbenchPhysicalOverviewForm.RefreshNodeInfo(new NodeId(str));
          break;

        case RefreshType.RefreshOverviewNodeChildren:
        //  if (wbContext.get_main_overview().matches_handle(ptr))
        //    workbenchMainOverviewForm.RefreshNodeChildren(new NodeId(str));
          if (ptr == null || wbContext.get_physical_overview().matches_handle(ptr))
            workbenchPhysicalOverviewForm.RefreshNodeChildren(new NodeId(str));
          //modelCatalogForm.UpdateCatalogTree();
          break;

        case RefreshType.RefreshSchema:
        case RefreshType.RefreshSchemaList:
          modelCatalogForm.UpdateCatalogTree();
          break;
      
        case RefreshType.RefreshLayers:
          modelLayerForm.UpdateLayerTree();
          break;

        case RefreshType.RefreshSelection:
          if (ptr != null && ptr.ToInt64() != 0)
          {
            UIForm form = UIForm.GetFromFixedId(ptr);

            if (form != null)
            {
              modelPropertiesForm.UpdateForForm(form);
              modelObjectDescriptionForm.UpdateForView(form);
            }
            else
            {
              modelPropertiesForm.UpdateForForm(null);
              modelObjectDescriptionForm.UpdateForView(null);
            }
          }
          else
          {
            UIForm form = (null != workbenchPhysicalOverviewForm) ? workbenchPhysicalOverviewForm.BackendClass : null;
            modelPropertiesForm.UpdateForForm(form);
            modelObjectDescriptionForm.UpdateForView(null);
          }
          break;

        case RefreshType.RefreshToolbar: 
          if (str == "main" || str == "")
            workbenchToolbarManager.UpdateMainToolbar();
          if (str == "tools" || str == "")
            workbenchToolbarManager.UpdateToolsToolbar();
          break;

        case RefreshType.RefreshMenubar:
          workbenchMenuManager.ConstructMainMenu(mainMenuStrip);

          // Hack: Restore last active document tab, if one set. We cannot do that earlier
          //       as we don't know when the document is fully loaded.
          if (activeDocumentTitle != "")
          {
            foreach (IDockContent document in mainDockPanel.Documents)
              if (document.DockHandler.TabText == activeDocumentTitle)
              {
                document.DockHandler.Activate();
                break;
              }
            activeDocumentTitle = "";
          }
          break;

        case RefreshType.RefreshMessages:
          if (outputForm != null)
            outputForm.RefreshMessages();
          if (str == "show")
            ShowOutputForm();
          break;

        case RefreshType.RefreshCloseEditor:
          CloseEditorsForObject(str);
          break;

        case RefreshType.RefreshHistory:
          Text = wbContext.get_title();
          historyForm.UpdateHistoryTree();
          break;

        case RefreshType.RefreshUserTypes:
          userDatatypesForm.UpdateTree();
          break;

        case RefreshType.RefreshDocument:
          Text= wbContext.get_title();

          userDatatypesForm.UpdateTree();

          break;

        case RefreshType.RefreshZoom:
          modelNavigatorForm.UpdateZoom();
          break;

        case RefreshType.RefreshTimer:
          UpdateTimer();
          break;

        default:
          break;
      }

      ResumeLayout();
    }

    public void LockGUI(bool flag)
    {
      //XXX
    }

    public BaseWindowsCanvasView CreateNewView(string viewId, string name)
    {
      ModelDiagramForm modelViewForm = new ModelDiagramForm(wbContext, viewId);

      modelViewForm.Text = name;
      modelViewForm.TabText = name;

      return modelViewForm.Canvas;
    }

    public void DestroyView(BaseWindowsCanvasView canvasView)
    {
      ModelDiagramForm modelViewForm = canvasView.GetOwnerForm() as ModelDiagramForm;
      if (modelViewForm != null)
      {
        modelViewForm.Close();
      }
    }

    public void SwitchedView(BaseWindowsCanvasView canvasView)
    {
      ModelDiagramForm modelViewForm = canvasView.GetOwnerForm() as ModelDiagramForm;
      if (modelViewForm != null)
      {
        if (!DocumentIsDocked(modelViewForm))
          modelViewForm.Show(mainDockPanel);

        modelViewForm.Activate();
      }
    }

    public void ToolChanged(BaseWindowsCanvasView canvasView)
    {
      workbenchToolbarManager.UpdateMainToolbar();
      workbenchToolbarManager.UpdateToolsToolbar();

      ModelDiagramForm modelViewForm = canvasView.GetOwnerForm() as ModelDiagramForm;
      if (modelViewForm != null)
      {
        modelViewForm.UpdateCursor();
      }
    }

    public void CloseEditorsForObject(string oid)
    {
      IDockContent[] documents = mainDockPanel.DocumentsToArray();

      // loop over all documents
      for (int i = documents.Length - 1; i > 0; i--)
      {
        if (documents[i] is ObjectEditorPlugin)
        {
          ObjectEditorPlugin editor = documents[i] as ObjectEditorPlugin;
          // If so, try to change the current GRT Object and exit
          if (oid == "" || editor.ShouldCloseOnObjectDelete(oid))
          {
            // release the current editor in the BE
            //editorForm_FormClosed(editor, new FormClosedEventArgs(CloseReason.None));

            editor.Close();
          }
        }
      }
    }

    public IntPtr OpenPlugin(GrtManager GrtManager, GrtModule GrtModule, string AssemblyName,
      string ClassName, GrtValue GrtList, GUIPluginFlags flags)
    {
      IntPtr ptr = IntPtr.Zero;

      try
      {
        // Load assembly
        Assembly assembly = Assembly.LoadFrom(System.IO.Path.Combine(
          Application.StartupPath, AssemblyName));

        // Find class
        foreach (Type type in assembly.GetTypes())
        {
          if (type.IsClass == true && type.FullName.EndsWith("." + ClassName))
          {
            // use global grtManager
            Object[] args = { grtManager, GrtList };

            if (typeof(DockablePlugin).IsAssignableFrom(type))
            {
              // If ForceNewWindowFlag is not set and this is an object editor
              if ((GUIPluginFlags.ForceNewWindowFlag & flags) == 0 && 
                typeof(ObjectEditorPlugin).IsAssignableFrom(type))
              {
                // Check if a plugin of this type is already open
                foreach (IDockContent content in mainDockPanel.Documents)
                  if (content is ObjectEditorPlugin && content.GetType() == type)
                  {
                    // If so, try to change the current GRT Object and exit
                    if ((content as ObjectEditorPlugin).ChangeGrtList(grtManager, GrtList))
                    {
                      // release the current editor in the BE
                      editorForm_FormClosed(content, new FormClosedEventArgs(CloseReason.None));

                      // return old Ptr as Ptr for the new editor
                      return (content as DockablePlugin).GetFixedPtr();
                    }
                    else
                      System.Diagnostics.Debug.Print(String.Format("Object editor for {0} does not support reuse", ClassName));
                  }
              }

              DockablePlugin pluginForm = Activator.CreateInstance(type, args) as DockablePlugin;
              if (pluginForm != null)
              {
                // Make sure form is removed from GRT editor list when closed
                pluginForm.FormClosed += new FormClosedEventHandler(editorForm_FormClosed);

                InstallFocusChangeHandlers(pluginForm);

                // Get fixed ptr
                ptr = pluginForm.GetFixedPtr();

                if (ptr == IntPtr.Zero)
                  throw new Exception("Class not found");
              }

              break;
            }
            else if (typeof(Plugin).IsAssignableFrom(type))
            {
              Plugin plugin = Activator.CreateInstance(type, args) as Plugin;

              try
              {
                //plugin.ExecutePluginFunction();
                plugin.Execute();
              }
              catch (Exception e)
              {
                ShowErrorText(String.Format("Error while executing plugin ({0}::{1})",
                  AssemblyName, ClassName), e.Message);
              }
            }
          }
        }
      }
      catch (Exception e)
      {
        String detail;

        detail= e.Message+"\n";
        if (e.InnerException != null)
        {
          detail += "(" + e.InnerException.Message + ")";
          /*
          detail += "\n\n";
          detail += "SOURCE:" + e.InnerException.Source+"\n";
          detail += "STACK:"+e.InnerException.StackTrace+"\n";
          if (e.InnerException.InnerException != null)
          {
            detail += "ANOTHER INNER (" + e.InnerException.Message + ")\n";
            detail += "\n\n";
            detail += "SOURCE:" + e.InnerException.Source+"\n";
            detail += "STACK:" + e.InnerException.StackTrace+"\n";
          }*/
        }
        /*if (e.StackTrace != null)
          detail += e.StackTrace;
         */

        ShowErrorText(String.Format("Cannot load selected plugin ({0}::{1})",
          AssemblyName, ClassName), detail);
      }

      return ptr;
    }

    public void ShowPlugin(IntPtr ptr)
    {
      if (ptr != IntPtr.Zero)
      {
        DockContent pluginForm = DockablePlugin.GetFromFixedPtr(ptr);
        if (pluginForm != null)
        {
          if (pluginForm is ObjectEditorPlugin)
            DockAsBottomDocument(pluginForm);
          else if (pluginForm is DockablePlugin && !(pluginForm is WizardPlugin))
            pluginForm.Show(mainDockPanel);
          else
            pluginForm.ShowDialog();
        }
      }
    }

    public void HidePlugin(IntPtr ptr)
    {
      if (ptr != IntPtr.Zero)
      {
        DockContent editorForm = ObjectEditorPlugin.GetFromFixedPtr(ptr);

        if (editorForm != null)
          editorForm.Hide();
      }
    }


    public void PopupCanvasMenu(List<MySQL.Grt.MenuItem> items, int x, int y)
    {
      ModelDiagramForm model= mainDockPanel.ActiveDocument as ModelDiagramForm;

      if (model != null)
        workbenchMenuManager.PopUpContextMenu(model, items, x, y);
    }

    private Control GetLeafActiveControl()
    {
      Control ctl= ActiveControl;
      while (ctl != null)
      {
        ContainerControl child;
        child = ctl as ContainerControl;
        if (child == null)
          break;
        ctl = child.ActiveControl;
      }
      return ctl;
    }

    private void EditCopy()
    {
      if (wbContext.edit_can_copy())
        wbContext.edit_copy();
      else
      {
        TextBoxBase text = GetLeafActiveControl() as TextBoxBase;
        if (text != null)
          text.Copy();
      }
    }

    private bool EditCanCopy()
    {
      if (!wbContext.edit_can_copy())
      {
        TextBoxBase text = GetLeafActiveControl() as TextBoxBase;
        if (text != null)
          return text.SelectionLength > 0;
        else
          return false;
      }
      return true;
    }


    private void EditCut()
    {
      if (wbContext.edit_can_cut())
        wbContext.edit_cut();
      else
      {
        TextBoxBase text = GetLeafActiveControl() as TextBoxBase;
        if (text != null)
          text.Cut();
      }
    }

    private bool EditCanCut()
    {
      if (!wbContext.edit_can_cut())
      {
        TextBoxBase text = GetLeafActiveControl() as TextBoxBase;
        if (text != null)
          return text.SelectionLength > 0;
        else
          return false;
      }
      return true;
    }

    private void EditPaste()
    {
      if (wbContext.edit_can_paste())
        wbContext.edit_paste();
      else
      {
        TextBoxBase text = GetLeafActiveControl() as TextBoxBase;
        if (text != null)
          text.Paste();
      }
    }

    private bool EditCanPaste()
    {
      if (!wbContext.edit_can_paste())
      {
        TextBoxBase text = GetLeafActiveControl() as TextBoxBase;
        if (text != null)
          return text.Enabled; // not sure when we can paste stuff
        else
          return false;
      }
      return true;
    }

    private void EditSelectAll()
    {
      if (wbContext.edit_can_select_all())
        wbContext.edit_select_all();
      else
      {
        TextBoxBase text = GetLeafActiveControl() as TextBoxBase;
        if (text != null)
          text.SelectAll();
      }
    }

    private bool EditCanSelectAll()
    {
      if (!wbContext.edit_can_select_all())
      {
        TextBoxBase text = GetLeafActiveControl() as TextBoxBase;
        if (text != null)
          return text.Enabled; // not sure when we can paste stuff
        else
          return false;
      }
      return true;
    }

    private void EditDelete()
    {
      if (wbContext.edit_can_delete())
        wbContext.edit_delete();
      else
      {
        //TextBoxBase text = GetLeafActiveControl() as TextBoxBase;
        //if (text != null)
        //  text.Delete();
      }
    }

    private bool EditCanDelete()
    {
      if (!wbContext.edit_can_delete())
      {
        /*TextBoxBase text = GetLeafActiveControl() as TextBoxBase;
        if (text != null)
          return text.Enabled;
        else*/
          return false;
      }
      return true;
    }

    private void SearchActiveTab()
    {
      String text= workbenchToolbarManager.lastSearchString;

      if (!wbContext.try_searching_diagram(text))
      {
        // if active main tab is Overview, search it
        if (mainDockPanel.ActiveDocument == workbenchPhysicalOverviewForm)
        {
          workbenchPhysicalOverviewForm.SearchAndFocusNode(text);
        }
      }
    }

    private void RegisterCommands()
    {
      List<String> commands= new List<string>();

      commands.Add("overview.mysql_model");
      commands.Add("show_grt_shell");
      commands.Add("show_about");
      commands.Add("diagram_size");
      commands.Add("show_output_form");
      commands.Add("view_model_navigator");
      commands.Add("view_catalog");
      commands.Add("view_layers");
      commands.Add("view_user_datatypes");
      commands.Add("view_object_properties");
      commands.Add("view_object_description");
      commands.Add("view_undo_history");
      commands.Add("reset_layout");
      commands.Add("wb.page_setup");
      commands.Add("closetab");
      commands.Add("help_index");
      commands.Add("help_version_check");
      commands.Add("find");
      commands.Add("wb.sidebarHide");

      wbContext.add_frontend_commands(commands);

      // special command in Windows implemented in wrapper to perform 
      // edit menu actions
      wbContext.set_edit_menu_delegates(EditCopy, EditCanCopy,
        EditCut, EditCanCut, EditPaste, EditCanPaste, 
        EditSelectAll, EditCanSelectAll, EditDelete, EditCanDelete,
        SearchActiveTab);
    }

    public void PerformCommand(String command)
    {
      if (command == "overview.mysql_model")
        ShowPhysicalOverviewForm();
      //deprecated else if (command == "overview.rename")
      //deprecated workbenchOverviewForm.BeginRenameSelection();
      else if (command == "show_grt_shell")
        ShowGrtShellForm();
      else if (command == "show_about")
      {
        new WorkbenchSplashScreen(true).ShowDialog();
      }
      else if (command == "diagram_size")
        ShowDiagramOptionsForm();
      else if (command == "show_output_form")
        ShowOutputForm();
      else if (command == "view_model_navigator")
        ShowModelNavigator();
      else if (command == "view_catalog")
        ShowCatalog();
      else if (command == "view_layers")
        ShowLayers();
      else if (command == "view_user_datatypes")
        ShowUserDatatypes();
      else if (command == "view_object_properties")
        ShowProperties();
      else if (command == "view_object_description")
        ShowDescriptions();
      else if (command == "view_undo_history")
        ShowUndoHistory();
      else if (command == "reset_layout")
        ResetWindowLayout();
      else if (command == "show_find_dialog")
      {
        ShowSimpleFindForm();
      }
      else if (command == "wb.page_setup")
        ShowPageSettingsForm();
      else if (command == "closetab")
      {
        if (mainDockPanel.ActiveDocument != null && mainDockPanel.ActiveDocument.DockHandler != null)
        {
          if (mainDockPanel.ActiveDocument.DockHandler.HideOnClose)
            mainDockPanel.ActiveDocument.DockHandler.Hide();
          else
            mainDockPanel.ActiveDocument.DockHandler.Close();
        }
      }
      // Help
      else if (command.Equals("help_index"))
        Help.ShowHelp(null, System.IO.Path.Combine(Application.StartupPath, "MySQLWorkbench.chm"));
      else if (command.Equals("help_version_check"))
        Program.CheckForNewVersion();
      else if (command.Equals("wb.sidebarHide"))
      {
        mainSplitContainer.Panel2Collapsed = !mainSplitContainer.Panel2Collapsed;
        ToolStripButton item = workbenchToolbarManager.GetToolStripItem("builtin:wb.sidebarHide") as ToolStripButton;
        if (item != null)
          item.Checked = mainSplitContainer.Panel2Collapsed;
      }
      else if (command.Equals("find"))
      {
        if (workbenchToolbarManager.searchBox != null)
          workbenchToolbarManager.searchBox.Focus();
      }
      else
      {
        System.Console.WriteLine(command + " NOT HANDLED!");
      }
    }

    public void PrintOutputText(string OutputText)
    {
      if (outputForm != null)
      {
        outputForm.PrintOutputText(OutputText);

        //ShowOutputForm();
      }
    }


    public void ResetWindowLayout()
    {
      // suspend dock layout to prevent flicker
      mainDockPanel.SuspendLayout(true);
      modelSidebarDockPanel.SuspendLayout(true);
      try
      {
        // hide windows
        modelNavigatorForm.Hide();
        userDatatypesForm.Hide();
        modelLayerForm.Hide();
        modelCatalogForm.Hide();
        historyForm.Hide();
        modelObjectDescriptionForm.Hide();
        modelPropertiesForm.Hide();

        //ShowMainOverviewForm();
        ShowPhysicalOverviewForm();

        // rebuild Sidebar
        modelNavigatorForm.Show(modelSidebarDockPanel);

        userDatatypesForm.Show(modelSidebarDockPanel, DockState.DockBottom);
        modelLayerForm.Show(userDatatypesForm.Pane, userDatatypesForm);
        modelCatalogForm.Show(modelLayerForm.Pane, modelLayerForm);  

        historyForm.Show(modelLayerForm.Pane, DockAlignment.Bottom, 0.5);
        modelPropertiesForm.Show(historyForm.Pane, historyForm);
        modelObjectDescriptionForm.Show(modelPropertiesForm.Pane, modelPropertiesForm);

        InstallFocusChangeHandlers(this);
      }
      finally
      {
        // resume suspended dock layout
        mainDockPanel.ResumeLayout(true, true);
        modelSidebarDockPanel.ResumeLayout(true, true);
      }
    }


    public void ShowOutputForm()
    {
      if (outputForm != null)
      {
        // Refresh messages
        outputForm.RefreshMessages();

        // Try to find output form
        IDockContent dockedOutputForm = FindDocument(outputForm.Text);

        if (dockedOutputForm == null)
        {
          mainDockPanel.SuspendLayout();
          try
          {
            // Add output form at bottom if possible
            DockAsBottomDocument(outputForm);
          }
          finally
          {
            mainDockPanel.ResumeLayout();
          }
        }
        else
        {
          // If the output form is not the active doc, make it active
          if (mainDockPanel.ActiveContent != dockedOutputForm)
            dockedOutputForm.DockHandler.Activate();
        }
      }
    }

    private void ShowModelNavigator()
    {
      mainSplitContainer.Panel2Collapsed = false;
      modelNavigatorForm.Activate();
    }

    private void ShowCatalog()
    {
      mainSplitContainer.Panel2Collapsed = false;
      modelCatalogForm.Activate();
    }

    private void ShowLayers()
    {
      mainSplitContainer.Panel2Collapsed = false;
      modelLayerForm.Activate();
    }

    private void ShowUserDatatypes()
    {
      mainSplitContainer.Panel2Collapsed = false;
      userDatatypesForm.Activate();
    }

    private void ShowProperties()
    {
      mainSplitContainer.Panel2Collapsed = false;
      modelPropertiesForm.Activate();
    }

    private void ShowDescriptions()
    {
      mainSplitContainer.Panel2Collapsed = false;
      modelObjectDescriptionForm.Activate();
    }

    private void ShowUndoHistory()
    {
      mainSplitContainer.Panel2Collapsed = false;
      historyForm.Activate();
    }

    private void ShowSimpleFindForm()
    {
      if (findForm == null)
      {
        findForm = new ObjectFindForm(wbContext);
      }
      findForm.ShowDialog();
    }

    private void ShowPageSettingsForm()
    {
      PageSettingsForm form = new PageSettingsForm(wbContext);

      form.ShowDialog();

      form.Dispose();
    }

    
    private void ShowDiagramOptionsForm()
    {
      DiagramOptionsForm form = new DiagramOptionsForm(wbContext);

      form.ShowDialog();

      form.Close();
      form.Dispose();
    }


    public String ShowFileDialog(String type, String title, String extensions)
    {
      FileDialog dialog;

      if (type == "save")
      {
        dialog = new SaveFileDialog();
      }
      else if (type == "open")
      {
        dialog = new OpenFileDialog();
      }
      else
        return "";

      String[] exts = extensions.Split(new char[] { ',' });

      dialog.RestoreDirectory = true;
      dialog.Title = title;
      dialog.DefaultExt = exts[0];
      String filter = "";
      foreach (String ext in exts)
      {
        if (ext.Contains("|"))
          filter = filter + ext + "|";
        else if (ext == "mwb")
          filter = filter + String.Format("{0} (*.mwb)|*.mwb|",
            "MySQL Workbench Models");
        else if (ext == "sql")
          filter = filter + String.Format("{0} (*.sql)|*.sql|",
            "SQL Script Files");
        else
          filter = filter + String.Format("{0} files (*.{0})|*.{0}|", ext);
      }
      filter = filter + String.Format("{0} (*.*)|*.*", "All files");

      dialog.Filter = filter;

      if (dialog.ShowDialog() == DialogResult.OK)
        return dialog.FileName;

      return "";
    }

    public void QuitApplication()
    {
      Close();
    }

    #endregion

    #region Standard Forms Handling

    private void grtShellImg_Click(object sender, EventArgs e)
    {
      ShowGrtShellForm();
    }

    private void grtOutputImg_Click(object sender, EventArgs e)
    {
      ShowOutputForm();
    }

    private void DockGrtIde()
    {
      // Dock Inspector in separate palette
      grtFileExplorer = grtShellForm.GrtFileExplorerForm;
      grtFileExplorer.Show(grtDockPanel, DockState.Document);

      grtStructsForm = grtShellForm.GrtStructsForm;
      grtStructsForm.Show(grtDockPanel, DockState.DockBottom);

      grtModulesForm = grtShellForm.GrtModulesForm;
      grtModulesForm.Show(grtStructsForm.Pane, grtStructsForm);

      if (grtGlobalsTreeForm == null)
      {
        // Dock Inspector in separate palette
        grtGlobalsTreeForm = new DockContent();
        grtGlobalsTreeForm.Padding = new Padding(4, 3, 4, 3);
        grtGlobalsTreeForm.BackColor = Color.White;
        grtGlobalsTreeForm.Icon = grtShellForm.GrtValuesForm.Icon;
        grtGlobalsTreeForm.Text = "GRT Tree";
        grtGlobalsTreeForm.HideOnClose = true;
        grtGlobalsTreeForm.Controls.Add(grtShellForm.GrtValuesForm.GrtTreeSplitContainer);
        grtShellForm.GrtValuesForm.GrtTreeSplitContainer.Dock = DockStyle.Fill;
        grtShellForm.GrtValuesForm.GrtTreeSplitContainer.Visible = true;
      }
      grtGlobalsTreeForm.Show(grtModulesForm.Pane, grtModulesForm);

      if (grtInspectorForm == null)
      {
        // Dock Inspector in separate palette
        grtInspectorForm = new DockContent();
        grtInspectorForm.Padding = new Padding(4, 3, 4, 3);
        grtInspectorForm.BackColor = Color.White;
        grtInspectorForm.Icon = grtShellForm.GrtValuesForm.Icon;
        grtInspectorForm.Text = "GRT Inspector";
        grtInspectorForm.HideOnClose = true;
        grtInspectorForm.Controls.Add(grtShellForm.GrtValuesForm.GrtInspectorSplitContainer);
        grtShellForm.GrtValuesForm.GrtInspectorSplitContainer.Dock = DockStyle.Fill;
      }
      grtInspectorForm.Show(grtGlobalsTreeForm.Pane, DockAlignment.Bottom, 0.5);
      grtShellForm.GrtValuesForm.GrtInspectorVisible = true;
    }

    private void ShowGrtIde()
    {
      modelSidebarTabPage.Controls.Add(modelSidebarDockPanel);
      sidebarFlatTabControl.Show();
      sidebarFlatTabControl.SelectedTab = grtTabPage;
    }

    private void HideGrtIde()
    {
      sidebarFlatTabControl.Hide();
      mainSplitContainer.Panel2.Controls.Add(modelSidebarDockPanel);
      modelSidebarDockPanel.Dock = DockStyle.Fill;
    }

    public void ShowGrtShellForm()
    {
      if (grtShellForm != null)
      {
        IDockContent dockedGrtShellForm = FindDocument(grtShellForm.Text);

        if (dockedGrtShellForm == null)
        {
          mainDockPanel.SuspendLayout();
          modelSidebarDockPanel.SuspendLayout();
          grtDockPanel.SuspendLayout();
          try
          {
            // Undock from current places
            grtShellForm.GrtValuesForm.Hide();
            grtShellForm.GrtFileExplorerForm.Hide();
            grtShellForm.GrtStructsForm.Hide();
            grtShellForm.GrtModulesForm.Hide();

            // Add GRT Shell at bottom if possible
            DockAsBottomDocument(grtShellForm);

            // Dock the GRT Shell Windows
            DockGrtIde();

            // Show the GRT Ide Windows
            ShowGrtIde();

            // Set the focus and refresh.
            grtShellForm.ActivateShell();
            grtShellForm.RefreshGrtTrees();
          }
          finally
          {
            mainDockPanel.ResumeLayout();
            modelSidebarDockPanel.ResumeLayout();
            grtDockPanel.ResumeLayout();
          }
        }
        else
        {
          dockedGrtShellForm.DockHandler.Activate();
          grtShellForm.ActivateShell();
        }
      }
    }

    public void ShowMainOverviewForm()
    {
      if (workbenchMainOverviewForm == null)
        workbenchMainOverviewForm = new WorkbenchOverviewForm(wbContext, wbContext.get_main_overview());

      IDockContent dockedOverviewForm = FindDocument(workbenchMainOverviewForm.Text);
      if (dockedOverviewForm == null)
      {
        // Select the first model type
        workbenchMainOverviewForm.RebuildModelContents();

        workbenchMainOverviewForm.Show(mainDockPanel);
      }
      else
      {
        dockedOverviewForm.DockHandler.Activate();
      }
    }

    public void ShowPhysicalOverviewForm()
    {
      if (workbenchPhysicalOverviewForm == null)
        workbenchPhysicalOverviewForm = new WorkbenchOverviewForm(wbContext, wbContext.get_physical_overview());

      IDockContent dockedOverviewForm = FindDocument(workbenchPhysicalOverviewForm.Text);
      if (dockedOverviewForm == null)
      {
        // Select the first model type
        workbenchPhysicalOverviewForm.RebuildModelContents();

        workbenchPhysicalOverviewForm.Show(mainDockPanel);
      }
      else
      {
        dockedOverviewForm.DockHandler.Activate();
      }
    }

    public void SetupUI()
    {
      RegisterCommands();

      workbenchMenuManager.ConstructMainMenu(mainMenuStrip);
      workbenchToolbarManager.UpdateMainToolbar();
      workbenchToolbarManager.UpdateToolsToolbar();
    }

    #endregion

    #region Document Handling

    private IDockContent FindDocument(string text)
    {
      if (mainDockPanel.DocumentStyle == DocumentStyle.SystemMdi)
      {
        foreach (Form form in MdiChildren)
          if (form.Text == text)
            return form as IDockContent;

        return null;
      }
      else
      {
        foreach (IDockContent content in mainDockPanel.Documents)
          if (content.DockHandler.TabText == text)
            return content;

        return null;
      }
    }

    private bool DocumentIsDocked(IDockContent Form)
    {
      foreach (IDockContent content in mainDockPanel.Documents)
        if (content.DockHandler == Form)
          return true;

      return false;
    }

    private void mainDockPanel_ContentAdded(object sender, WeifenLuo.WinFormsUI.Docking.DockContentEventArgs e)
    {
      IDockContent content = e.Content;
      if (content is IWorkbenchDocument)
      {
        IWorkbenchDocument wbDoc = content as IWorkbenchDocument;
        if (wbDoc is ModelDiagramForm)
        {
          ModelDiagramForm form = wbDoc as ModelDiagramForm;

          ((ModelViewForm)form.BackendClass).set_closed(false);
        }
      }
    }

    private void mainDockPanel_ContentRemoved(object sender, WeifenLuo.WinFormsUI.Docking.DockContentEventArgs e)
    {
      IDockContent content = e.Content;
      if (content is IWorkbenchDocument)
      {
        IWorkbenchDocument wbDoc = content as IWorkbenchDocument;
        if (wbDoc is ModelDiagramForm)
        {
          ModelDiagramForm form = wbDoc as ModelDiagramForm;

          ((ModelViewForm)form.BackendClass).set_closed(true);
        }
      }
    }

    private void mainDockPanel_ActiveDocumentChanged(object sender, EventArgs e)
    {
      if (sender is DockPanel && ((DockPanel)sender).ActiveDocument != null)
      {
        IDockContent activeDocument = ((DockPanel)sender).ActiveDocument;

        if (activeDocument is IWorkbenchDocument)
        {
          IWorkbenchDocument wbDoc =
            activeDocument as IWorkbenchDocument;

          // If the current View is changed, update ModelCatalogForm
          if (activeDocument is ModelDiagramForm)
          {
            ModelDiagramForm form = activeDocument as ModelDiagramForm;

            wbContext.set_active_form(form.BackendClass);

            form.FocusCanvasControl();
          }
          else
          {
            wbContext.set_active_form(wbDoc.BackendClass);  
          }
        }
      }
      else
      {
        wbContext.set_active_form(null);
      }

      //string viewPath = wbContext.get_view_path_for_active_view();
    }

    /// <summary>
    /// Remove Editor Form from WbContext editor cache
    /// </summary>
    /// <param name="sender">The form</param>
    /// <param name="e">The close event arguments</param>
    void editorForm_FormClosed(object sender, FormClosedEventArgs e)
    {
      DockablePlugin editorForm = sender as DockablePlugin;

      if (editorForm != null)
        wbContext.close_gui_plugin(editorForm.GetFixedPtr());
    }

    private void DockAsBottomDocument(DockContent editorForm)
    {
      if (editorForm != null)
      {
        // If there are no documents, show as main document
        if (mainDockPanel.DocumentsCount == 0)
        {
          editorForm.Show(mainDockPanel);
          return;
        }

        // If the editor is already there, activate it  
        foreach (IDockContent content in mainDockPanel.Documents)
          if (content.DockHandler == editorForm.DockHandler)
          {
            editorForm.DockHandler.Activate();
            return;
          }

        // Check if there already is a docking Pane for documents at the Bottom
        for (int i = 0; i < mainDockPanel.Panes.Count; i++)
        {
          DockPane pane = mainDockPanel.Panes[i];
          if (pane.DockState == DockState.Document &&
            pane.NestedDockingStatus.Alignment == DockAlignment.Bottom)
          {
            editorForm.Show(pane, pane.ActiveContent);
            return;
          }
        }

        // If there is a main document already, show editor at the bottom
        for (int i = 0; i < mainDockPanel.Panes.Count; i++)
        {
          DockPane pane = mainDockPanel.Panes[i];
          if (pane.DockState == DockState.Document &&
            pane.NestedDockingStatus.Alignment != DockAlignment.Bottom)
          {
            editorForm.Show(pane, DockAlignment.Bottom, 0.35);
            return;
          }
        }
      }
    }

    #endregion

    #region UI Event Handling

    private void spacerPanel_Paint(object sender, PaintEventArgs e)
    {
      Graphics g = e.Graphics;

      g.DrawLine(Pens.White, e.ClipRectangle.Left, 1, e.ClipRectangle.Right - 1, 1);
    }

    public void UpdateNavigatorImage()
    {
      modelNavigatorForm.UpdateNavigatorImage();
    }


    private void MainForm_KeyDown(object sender, KeyEventArgs e)
    {
      //wbContext.handle_key_event(true, e);
    }

    private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
    {
      shuttingDown = true;
      RemoveFocusChangeHandlers(this);

      IDockContent[] documents = mainDockPanel.DocumentsToArray();

      if (wbContext.has_unsaved_changes())
      {
        DialogResult result = MessageBox.Show("Do you want to save changes to the document before closing?\n\n" +
                        "If you don't save your changes, they will be lost.",
                        "Close Workbench", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation);
        if (result == DialogResult.Cancel)
          e.Cancel = true;
        else 
          if (result == DialogResult.No)
            e.Cancel = false;
          else
          {
            if (!wbContext.save_changes())
              e.Cancel = true;
          }
      }
      else
        e.Cancel = false;

      // Reset termination flag, in case it was set.
      if (e.Cancel)
      {
        grtManager.resetTermination();
        return;
      }

      saveFormState();

      mainDockPanel.SuspendLayout();
      try
      {
        // Close all documents (editors implement the document interface too, so we get them
        // with that loop as well).
        for (int i = documents.Length - 1; i >= 0; i--)
        {
          if (documents[i] is IWorkbenchDocument)
            documents[i].DockHandler.Close(); // This will also trigger a form's OnFormClosing event.
        }
      }
      finally
      {
        mainDockPanel.ResumeLayout();
      }
    }

    private void loadFormState()
    {
      int x = wbContext.read_state("left", "mainform", 100);
      int y = wbContext.read_state("top", "mainform", 100);
      int w = wbContext.read_state("width", "mainform", 1200);
      int h = wbContext.read_state("height", "mainform", 900);

      // First set the bounds then the state. If the window is maximized this will properly set
      // RestoreBounds so un-maximizing will later do the right job.
      Bounds = new Rectangle(x, y, w, h);
      WindowState = (FormWindowState)wbContext.read_state("windowstate", "mainform", (int)FormWindowState.Normal);

      // Don't restore into minimized state. Confusing for the user and makes trouble for layouting.
      if (WindowState == FormWindowState.Minimized)
        WindowState = FormWindowState.Normal;

      mainSplitContainer.SplitterDistance = wbContext.read_state("splitpos", "mainform", mainSplitContainer.SplitterDistance);

      if (wbContext.read_state("shellvisible", "mainform", 0) == 1)
        ShowGrtShellForm();
      if (wbContext.read_state("outputvisible", "mainform", 0) == 1)
        ShowOutputForm();

      // Restore active document.
      activeDocumentTitle = wbContext.read_state("activedocument", "mainform", "");
    }

    private void saveFormState()
    {
      wbContext.save_state("windowstate", "mainform", (int)WindowState);

      if (WindowState == FormWindowState.Normal)
      {
        wbContext.save_state("left", "mainform", Left);
        wbContext.save_state("top", "mainform", Top);
        wbContext.save_state("width", "mainform", Width);
        wbContext.save_state("height", "mainform", Height);
      }
      else
      {
        wbContext.save_state("left", "mainform", RestoreBounds.Left);
        wbContext.save_state("top", "mainform", RestoreBounds.Top);
        wbContext.save_state("width", "mainform", RestoreBounds.Width);
        wbContext.save_state("height", "mainform", RestoreBounds.Height);
      }
      
      wbContext.save_state("splitpos", "mainform", mainSplitContainer.SplitterDistance);

      if (grtShellForm != null && grtShellForm.Visible)
        wbContext.save_state("shellvisible", "mainform", 1);
      else
        wbContext.save_state("shellvisible", "mainform", 0);
      if (outputForm != null && outputForm.Visible)
        wbContext.save_state("outputvisible", "mainform", 1);
      else
        wbContext.save_state("outputvisible", "mainform", 0);

      // Active document.
      if (mainDockPanel.ActiveDocument != null)
        wbContext.save_state("activedocument", "mainform", mainDockPanel.ActiveDocument.DockHandler.TabText);
    }

    private void MainForm_Load(object sender, EventArgs e)
    {
      loadFormState();

      SplashScreen.Close();

      BringToFront();
      Activate();
    }


    public void UpdateTimer()
    {
      int interval = (int)(1000 * wbContext.delay_for_next_timer());
      if (interval > 0)
      {
        timer.Interval = interval;
        timer.Start();
      }
      else if (interval < 0)
        timer.Stop();
      else
      {
        timer.Interval = 1;
        timer.Start();
      }
    }


    private void timer_Tick(object sender, EventArgs e)
    {
      wbContext.flush_timers();
      UpdateTimer();
    }


    private void FocusChanged(object sender, EventArgs e)
    {
      // force a refresh of the menu so that Edit -> Copy/Paste/Cut/Select All can get updated accordingly
      if (!shuttingDown)
        workbenchMenuManager.ConstructMainMenu(mainMenuStrip);
    }

    private EventHandler FocusChangeHandler= null;


    private void RemoveFocusChangeHandlers(Control control)
    {
      if (FocusChangeHandler == null)
        return;

      // recursivelly install event handler for GotFocus and LostFocus
      control.GotFocus -= FocusChangeHandler;

      foreach (Control sub in control.Controls)
        RemoveFocusChangeHandlers(sub);
    }

    private void InstallFocusChangeHandlers(Control control)
    {
      if (FocusChangeHandler == null)
        FocusChangeHandler= new EventHandler(FocusChanged);

      // recursivelly install event handler for GotFocus and LostFocus
      control.GotFocus -= FocusChangeHandler;
      control.GotFocus += FocusChangeHandler;

      foreach  (Control sub in control.Controls)
        InstallFocusChangeHandlers(sub);
    }

    #endregion

  }
}
