/* 
 * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
 *
 * 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; version 2 of the
 * License.
 * 
 * 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
 */

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

using MySQL.Controls;
using MySQL.Forms;
using MySQL.Grt;
using MySQL.Workbench;

namespace MySQL.GUI.Workbench.Plugins
{
  /// <summary>
  /// Generic GRT Object Editor
  /// </summary>
  public partial class ObjectEditorPlugin : DockablePlugin
  {
    #region Member Variables

    /// <summary>
    /// Specifies if the controls on the form are currently getting initialized
    /// </summary>
    private bool initializingControls = false;

    private int refreshBlocked = 0;

    private BaseEditor backend;
    private SqlEditorWrapper editorBackend;

    private SplitContainer mainSplitContainer = null;
    Button revertLiveObjectButton;
    Button applyLiveObjectButton;
    public ObjectEditorView ContainerForm;

    #endregion

    #region Constructors

    public ObjectEditorPlugin()
      : base()
    {
    }

    public ObjectEditorPlugin(GrtManager manager)
      : base(manager)
    {
    }

    public virtual bool ReinitWithArguments(GrtValue value)
    {
      return false;
    }

    #endregion

    #region Properties

    protected bool InitializingControls
    {
      get { return initializingControls; }
      set { initializingControls = value; }
    }

    public bool InsideInitializationOrRefresh()
    {
      return InitializingControls || insideRefreshFormData;
    }

    public new Control ActiveControl
    {
      get
      {
        if (null != ContainerForm)
          return ContainerForm.ActiveControl;
        else
          return ActiveControl;
      }
      set
      {
        if (null != ContainerForm)
          ContainerForm.ActiveControl = value;
        else
          ActiveControl = value;
      }
    }

    protected BaseEditor Backend
    {
      get { return backend; }
      set
      {
        if (backend != null)
          backend.Dispose();
        if (editorBackend != null)
        {
          Control editor = editorBackend.get_native_editor();
          if (editor.Parent != null)
            editor.Parent.Controls.Remove(editor);
          editor.Dispose();
          editorBackend.Dispose();
          editorBackend = null;
        }

        backend = value;
        if (backend != null)
        {
          backend.set_refresh_ui_handler(CallRefreshFormData);
          backend.set_refresh_partial_ui_handler(OnPartialRefresh);
        }
      }
    }

    #endregion

    #region Notifications

    protected void OnPartialRefresh(int what)
    {
      switch ((BaseEditor.PartialRefreshType)what)
      {
        case BaseEditor.PartialRefreshType.RefreshTextChanged:
          EditorTextChanged();
          break;

        default:
          Logger.LogDebug("Object Editor Plugin", 1, "Unhandled partial refresh message\n");
          break;
      }
    }

    protected virtual void EditorTextChanged()
    {
      TabText = backend.get_title();
    }

    #endregion

    #region IWorkbenchDocument Interface

    public override UIForm BackendForm
    {
      get { return backend; }
    }

    public override void RefreshGUI(RefreshType refresh, String str, IntPtr ptr)
    {
    }

    public override void PerformCommand(String command)
    {
    }

    public override DockablePlugin FindPluginOfType(Type type)
    {
      if (GetType() == type)
        return this;
      return null;
    }

    public override bool ClosePluginOfType(Type type)
    {
      if (GetType() == type)
      {
        Close();
        return true;
      }
      return false;
    }

    public override bool CanCloseDocument()
    {
      bool result = base.CanCloseDocument();

      if (!result)
        return false;

      result = backend.can_close();
      if (!result)
        return false;

      return true;
    }

    public override void CloseDocument()
    {
      if (backend != null)
        backend.disable_auto_refresh();

      base.CloseDocument();
    }

    #endregion

    #region Form Implementation

    public Control SetupEditorOnHost(Control host, bool coloring)
    {
      if (backend != null && editorBackend == null)
        editorBackend = SqlEditorWrapper.get_sql_editor(backend);

      if (editorBackend != null)
      {
        if (coloring)
        {
          editorBackend.set_sql_check_enabled(true);
          editorBackend.set_language("mysql");
        }
        else
        {
          editorBackend.set_sql_check_enabled(false);
          editorBackend.set_language("");
        }
        Control editor = editorBackend.get_native_editor();
        host.Controls.Add(editor);
        editor.Dock = DockStyle.Fill;

        editor.LostFocus += new EventHandler(editor_LostFocus);

        return editor;
      }

      return null;
    }

    void editor_LostFocus(object sender, EventArgs e)
    {
      EditorLostFocus();
    }

    public bool IsEditingLiveObject
    {
      get { return (null == backend) ? false : backend.is_editing_live_object(); }
    }

    public void ApplyChangesToLiveObject()
    {
      backend.apply_changes_to_live_object();
    }

    private void AdjustEditModeControls(Control mainTabControl)
    {
      if (null == mainTabControl)
        return;

      bool isEditingLiveObject = IsEditingLiveObject;
      bool isEditingLiveObjectUI = ((null != mainSplitContainer) && (mainTabControl.Parent == mainSplitContainer.Panel1));
      if (isEditingLiveObject == isEditingLiveObjectUI)
        return;

      Control mainContainer = (isEditingLiveObjectUI) ? mainSplitContainer.Parent : mainTabControl.Parent;
      mainContainer.SuspendLayout();

      try
      {
        if (isEditingLiveObject)
        {
          if (null == mainSplitContainer)
          {
            mainSplitContainer = new SplitContainer();
            mainSplitContainer.Dock = DockStyle.Fill;
            mainSplitContainer.Orientation = Orientation.Horizontal;
            mainSplitContainer.SplitterWidth = 2;
            mainSplitContainer.FixedPanel = FixedPanel.Panel2;

            Panel liveObjectControlsPanel = new Panel();
            liveObjectControlsPanel.Parent = mainSplitContainer.Panel2;
            liveObjectControlsPanel.Dock = DockStyle.Fill;

            {
              revertLiveObjectButton = new Button();
              revertLiveObjectButton.UseVisualStyleBackColor = true;
              revertLiveObjectButton.Parent = liveObjectControlsPanel;
              revertLiveObjectButton.FlatStyle = FlatStyle.System;
              revertLiveObjectButton.Text = "Revert";
              revertLiveObjectButton.Location = new Point(
                mainSplitContainer.Panel2.ClientSize.Width - revertLiveObjectButton.Width - revertLiveObjectButton.Margin.Right,
                revertLiveObjectButton.Margin.Top);
              revertLiveObjectButton.Click += new EventHandler(revertChangesToLiveObjectButton_Click);
            }
            {
              applyLiveObjectButton = new Button();
              applyLiveObjectButton.UseVisualStyleBackColor = true;
              applyLiveObjectButton.Parent = liveObjectControlsPanel;
              applyLiveObjectButton.FlatStyle = FlatStyle.System;
              applyLiveObjectButton.Text = "Apply";
              applyLiveObjectButton.Location = new Point(
                revertLiveObjectButton.Location.X - revertLiveObjectButton.Margin.Left - applyLiveObjectButton.Width - applyLiveObjectButton.Margin.Right,
                revertLiveObjectButton.Location.Y);
              applyLiveObjectButton.Click += new EventHandler(applyChangesToLiveObjectButton_Click);
            }

            mainSplitContainer.Panel2MinSize = revertLiveObjectButton.Height + revertLiveObjectButton.Margin.Vertical;
            mainSplitContainer.SplitterDistance = mainContainer.ClientSize.Height - mainSplitContainer.Panel2MinSize - mainSplitContainer.SplitterWidth;

            //closeLiveEditorButton.Anchor =
            revertLiveObjectButton.Anchor =   AnchorStyles.Right | AnchorStyles.Bottom;
            applyLiveObjectButton.Anchor = AnchorStyles.Right | AnchorStyles.Bottom;
            /*applyingChangesProgressBar.Anchor =*/
          }

          mainSplitContainer.Parent = mainTabControl.Parent;
          mainTabControl.Parent = mainSplitContainer.Panel1;

       }
        else
        {
          mainSplitContainer.Parent = null;
          mainTabControl.Parent = mainContainer;
        }
      }
      finally
      {
        mainContainer.ResumeLayout();
      }
    }

    private void applyChangesToLiveObjectButton_Click(object sender, EventArgs e)
    {
      ApplyChangesToLiveObject();
    }

    private void revertChangesToLiveObjectButton_Click(object sender, EventArgs e)
    {
      backend.refresh_live_object();
    }

    public override string TabText
    {
      set
      {
        base.TabText = value;
        if (null != ContainerForm)
          ContainerForm.TabText = value;
      }
    }

    public bool ShouldCloseOnObjectDelete(String oid)
    {
      return backend.should_close_on_delete_of(oid);
    }

    protected void BlockUIRefreshes()
    {
      refreshBlocked++;
    }

    protected void UnblockUIRefreshes()
    {
      refreshBlocked--;
      if (refreshBlocked == 0)
        CallRefreshFormData();
      else if (refreshBlocked < 0)
        throw new Exception("too many UnblockUIRefreshes");
    }

    protected bool insideRefreshFormData= false;
    protected virtual void RefreshFormData() {}

    protected bool insideRefreshPartialFormData= false;
    protected virtual void RefreshPartialFormData(int where) { }

    protected virtual void CallRefreshFormData() 
    {
      if (InvokeRequired)
      {
        Invoke((Action)(() => CallRefreshFormData()));
        return;
      }

      if (insideRefreshFormData || refreshBlocked > 0)
        return;

      insideRefreshFormData = true;
      try
      {
        // This is not right to hack inside derived class, pick part of it containing tabs 
        // and adjust something there leaving rest of controls unadjusted.
        // There is no guarantee that derived editor will have anything named "mainTabControl" at all
        Control[] controls = Controls.Find("mainTabControl", true);
        if (controls.Length > 0)
        {
          FlatTabControl mainTabControl = controls[0] as FlatTabControl;
          AdjustEditModeControls(mainTabControl);
        }

        RefreshFormData();
      }
      finally 
      {
        insideRefreshFormData = false;
      }
    }

    protected virtual void CallRefreshPartialFormData(int where) 
    {
      if (InvokeRequired)
      {
        Invoke((Action)(() => CallRefreshPartialFormData(where)));
        return;
      }

      if (insideRefreshPartialFormData || refreshBlocked > 0)
        return;

      insideRefreshPartialFormData = true;
      try
      {
        RefreshPartialFormData(where);
      }
      finally 
      {
        insideRefreshPartialFormData = false;
      }
    }

    public void ActivateEditor()
    {
      ActiveControl = editorBackend.get_native_editor();
    }

    public virtual void EditorLostFocus()
    {
      // Can be overridden by descendants to store the editors text in the backend.
    }

    #endregion
  }
}