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 Aga.Controls.Tree;

using MySQL.Grt;
using MySQL.Utilities;
using MySQL.GUI.Workbench;
using MySQL.Utilities.SysUtils;

namespace MySQL.GUI.Workbench.Plugins
{
  public partial class DbMysqlTableEditor : ObjectEditorPlugin
  {
    #region Member Variables

    private MySQLTableEditorBE tableEditorBE { get { return editorBE as MySQLTableEditorBE; } } 
    private GrtListNode activeNode = null;

    private DbMysqlTableColumnsListModel columnsListModel;
    private DbMysqlTableIndicesListModel indicesListModel;
    private DbMysqlTableIndexColumnsListModel indexColumnsListModel;
    private DbMysqlTableFkListModel fkListModel;
    private DbMysqlTableFkColumnListModel fkColumnsListModel;

    private DbObjectEditorPages dbObjectEditorPages = null;

    private SimpleGrtTreeModel partitionTreeModel;
    private MySQLTablePartitionTreeBE partitionTreeBE;

    private IMessageFilter wheelFilter;

    #endregion

    #region Constructors

    public DbMysqlTableEditor(GrtManager GrtManager, GrtValue GrtList)
      : base(new MySQLTableEditorBE(GrtManager, GrtList))
    {
      InitializeComponent();

      grtManager = GrtManager;

      sqlEd = new SqlEditorHelper(GrtManager, triggersTabPage, null);

      InitializingControls = true;
      try
      {
        InitFormData();

        CallRefreshFormData();
      }
      finally
      {
        InitializingControls = false;
      }

      tableEditorBE.add_refresh_ui_handler(RefreshFormDataInvoke);

      wheelFilter = new WheelMessageFilter(this);
      Application.AddMessageFilter(wheelFilter);
    }

    /// <summary>
    /// Internal class used to forward mouse wheel messages to the overview page to be able to scroll
    /// all panels useing the mouse wheel.
    /// </summary>
    internal class WheelMessageFilter : IMessageFilter
    {
      private DbMysqlTableEditor form;

      public WheelMessageFilter(DbMysqlTableEditor form)
      {
        this.form = form;
      }

      public bool PreFilterMessage(ref Message m)
      {
        if (m.Msg == Win32.WM_MOUSEWHEEL && form.mainTabControl.SelectedTab == form.optionsTabPage)
        {
          // Only handle messages if the mouse pointer is in the form's client area.
          // Also check we don't have any other window (e.g. dialogs) in front.
          Rectangle rect = form.RectangleToScreen(form.ClientRectangle);
          Point cursorPoint = new Point();
          Win32.GetCursorPos(ref cursorPoint);
          IntPtr targetWindow = Win32.WindowFromPoint(cursorPoint);
          Control control = Form.FromHandle(targetWindow);
          if (control != null && (control.FindForm() == form) && rect.Contains(cursorPoint))
          {
            // Forward the message to the form.
            Win32.SendMessage(control.Handle, m.Msg, m.WParam, m.LParam);
            return true;
          }
        }

        return false;
      }
    }

    #endregion

    #region IWorkbenchDocument implementation

    public override UIForm BackendClass
    {
      get { return tableEditorBE; }
    }

    #endregion

    #region ObjectEditorPlugin Overrides

    public override bool ChangeGrtList(GrtManager GrtManager, GrtValue GrtList)
    {
      InitializingControls = true;
      SuspendLayout();

      try
      {
        grtManager = GrtManager;
        grtList = GrtList;

        if (editorBE != null)
          editorBE.disable_auto_refresh();
        editorBE = new MySQLTableEditorBE(GrtManager, GrtList);

        InitFormData();

        CallRefreshFormData();
      }
      finally
      {
        ResumeLayout();
        InitializingControls = false;
      }

      tableEditorBE.add_refresh_ui_handler(RefreshFormDataInvoke);

      Invalidate();

      return true;
    }

    #endregion

    #region Form implemenation

    protected void InitFormData()
    {
      // Init Columns TreeView
      columnsTreeView.SelectedNode = null;
      if (columnsTreeView.Model != null && columnsTreeView.Model is DbMysqlTableColumnsListModel)
        (columnsTreeView.Model as DbMysqlTableColumnsListModel).DetachEvents();

      columnsListModel = new DbMysqlTableColumnsListModel(columnsTreeView, tableEditorBE.get_columns(),
        columnIconNodeControl, nameNodeControl, datatypeComboBoxNodeControl,
        pkNodeControl, nnNodeControl, binNodeControl, unNodeControl, zfNodeControl, aiNodeControl,
        defaultNodeControl,
        tableEditorBE);
      columnsTreeView.Model = columnsListModel;
      columnsTreeView.Refresh();

      // Init Index TreeView
      indicesTreeView.SelectedNode = null;
      if (indicesTreeView.Model != null && indicesTreeView.Model is DbMysqlTableIndicesListModel)
        (indicesTreeView.Model as DbMysqlTableIndicesListModel).DetachEvents();

      indicesListModel = new DbMysqlTableIndicesListModel(indicesTreeView, tableEditorBE.get_indexes(),
        indexNameNodeControl, indexTypeNodeControl, indexColumnNameNodeControl, tableEditorBE);
      indicesTreeView.Model = indicesListModel;
      indicesTreeView.Refresh();

      // Init FK TreeView
      fkTreeView.SelectedNode = null;
      if (fkTreeView.Model != null && fkTreeView.Model is DbMysqlTableFkListModel)
        (fkTreeView.Model as DbMysqlTableFkListModel).DetachEvents();

      fkListModel = new DbMysqlTableFkListModel(fkTreeView, tableEditorBE.get_fks(),
        nameFkNodeControl, targetFkNodeControl, tableEditorBE, columnEnabledFkNodeControl);
      fkTreeView.Model = fkListModel;
      fkTreeView.Refresh();

      // fk actions
      onDeleteActionComboBox.Enabled = true;
      onDeleteActionComboBox.Items.Clear();
      onUpdateActionComboBox.Enabled = true;
      onUpdateActionComboBox.Items.Clear();

      onDeleteActionComboBox.BeginUpdate();
      onUpdateActionComboBox.BeginUpdate();

      try
      {
        System.Collections.Generic.List<String> fk_action_options_list = tableEditorBE.get_fk_action_options();
        foreach (String fk_action in fk_action_options_list)
        {
          onDeleteActionComboBox.Items.Add(fk_action);
          onUpdateActionComboBox.Items.Add(fk_action);
        }
      }
      finally
      {
        onDeleteActionComboBox.EndUpdate();
        onUpdateActionComboBox.EndUpdate();
      }
      // index storage types
      indexStorageTypeComboBox.Enabled = true;
      indexStorageTypeComboBox.Items.Clear();
      System.Collections.Generic.List<String> index_storage_types_list = tableEditorBE.get_index_storage_types();
      foreach (String storage_type in index_storage_types_list)
      {
        indexStorageTypeComboBox.Items.Add(storage_type);
      }

      // engines
      optEngine.Enabled = true;
      optEngine.Items.Clear();
      optEngine.Items.Add("Server Default");
      System.Collections.Generic.List<String> engines_list = tableEditorBE.get_engines_list();
      foreach (String engine in engines_list)
      {
        optEngine.Items.Add(engine);
      }

      System.Collections.Generic.List<String> collations_list = tableEditorBE.get_charset_collation_list();

      optCollation.Items.Clear();
      optCollation.Items.Add("Schema Default");

      columnCollationComboBox.Enabled = true;
      columnCollationComboBox.Items.Clear();
      columnCollationComboBox.Items.Add("Table Default");

      foreach (String coll in collations_list)
      {
        optCollation.Items.Add(coll);
        columnCollationComboBox.Items.Add(coll);
      }

      // Add privileges tab sheet
      if (dbObjectEditorPages != null)
        mainTabControl.TabPages.Remove(dbObjectEditorPages.PrivilegesTabPage);
      dbObjectEditorPages = new DbObjectEditorPages(grtManager, tableEditorBE);
      mainTabControl.TabPages.Add(dbObjectEditorPages.PrivilegesTabPage);

      // inserts
      insertsTextBox.Text= tableEditorBE.get_inserts();

      // Partitioning stuff
      if (partitionTreeView.Model != null && partitionTreeView.Model is SimpleGrtTreeModel)
        (partitionTreeView.Model as SimpleGrtTreeModel).DetachEvents();

      partitionTreeBE = tableEditorBE.get_partitions();
      partitionTreeModel = new SimpleGrtTreeModel(partitionTreeView, partitionTreeBE, partNodeStateIcon);
      partitionTreeModel.AddColumn(partNameNodeControl, (int)MySQLTablePartitionTreeBE.Columns.Name, true);
      partitionTreeModel.AddColumn(partValuesNodeControl, (int)MySQLTablePartitionTreeBE.Columns.Value, true);
      partitionTreeModel.AddColumn(partDataDirNodeControl, (int)MySQLTablePartitionTreeBE.Columns.DataDirectory, true);
      partitionTreeModel.AddColumn(partIndexDirNodeControl, (int)MySQLTablePartitionTreeBE.Columns.IndexDirectory, true);
      partitionTreeModel.AddColumn(partMaxRowsNodeControl, (int)MySQLTablePartitionTreeBE.Columns.MaxRows, true);
      partitionTreeModel.AddColumn(partMinRowsNodeControl, (int)MySQLTablePartitionTreeBE.Columns.MinRows, true);
      partitionTreeModel.AddColumn(partCommentNodeControl, (int)MySQLTablePartitionTreeBE.Columns.Comment, true);
      partitionTreeView.Model = partitionTreeModel;
      partitionTreeView.Refresh();
    }

    protected override void RefreshFormData()
    {
      // Sets all the table options.
      refreshTableOptUi();

      if (!sqlEd.sqlEditor.IsDirty || sqlEd.EditorBE != tableEditorBE)
      {
        sqlEd.SetEditorBackend(tableEditorBE, tableEditorBE.set_triggers_sql);
        sqlEd.sqlEditor.SqlText = tableEditorBE.get_all_triggers_sql();
      }

      nameTextBox.Text = tableEditorBE.get_name();
      optComments.Text = tableEditorBE.get_comment();

      switch (tableEditorBE.get_table_option_by_name("PACK_KEYS"))
      {
        case "DEFAULT":
          optPackKeys.SelectedIndex = 0;
          break;
        case "0":
          optPackKeys.SelectedIndex = 1;
          break;
        case "1":
          optPackKeys.SelectedIndex = 2;
          break;
      }

      optTablePassword.Text = tableEditorBE.get_table_option_by_name("PASSWORD");
      optAutoIncrement.Text = tableEditorBE.get_table_option_by_name("AUTO_INCREMENT");

      switch (tableEditorBE.get_table_option_by_name("DELAY_KEY_WRITE"))
      {
        case "0":
          optDelayKeyUpdates.Checked = false;
          break;
        case "1":
          optDelayKeyUpdates.Checked = true;
          break;
      }

      switch (tableEditorBE.get_table_option_by_name("ROW_FORMAT"))
      {
        case "DEFAULT":
          optRowFormat.SelectedIndex = 0;
          break;
        case "DYNAMIC":
          optRowFormat.SelectedIndex = 1;
          break;
        case "FIXED":
          optRowFormat.SelectedIndex = 2;
          break;
        case "COMPRESSED":
          optRowFormat.SelectedIndex = 3;
          break;
        case "REDUNDANT":
          optRowFormat.SelectedIndex = 4;
          break;
        case "COMPACT":
          optRowFormat.SelectedIndex = 5;
          break;
      }

      optAvgRowLength.Text = tableEditorBE.get_table_option_by_name("AVG_ROW_LENGTH");
      optMaxRows.Text = tableEditorBE.get_table_option_by_name("MAX_ROWS");
      optMinRows.Text = tableEditorBE.get_table_option_by_name("MIN_ROWS");

      switch (tableEditorBE.get_table_option_by_name("CHECKSUM"))
      {
        case "0":
          optUseChecksum.Checked = false;
          break;
        case "1":
          optUseChecksum.Checked = true;
          break;
      }

      optDataDirectory.Text = tableEditorBE.get_table_option_by_name("DATA DIRECTORY");
      optIndexDirectory.Text = tableEditorBE.get_table_option_by_name("INDEX DIRECTORY");
      optUnionTables.Text = tableEditorBE.get_table_option_by_name("UNION");

      switch (tableEditorBE.get_table_option_by_name("INSERT_METHOD"))
      {
        case "NO":
          optMergeMethod.SelectedIndex = 0;
          break;
        case "FIRST":
          optMergeMethod.SelectedIndex = 0;
          break;
        case "LAST":
          optMergeMethod.SelectedIndex = 0;
          break;
      }

      String eng = tableEditorBE.get_table_option_by_name("ENGINE");
      if (eng == String.Empty)
        optEngine.SelectedIndex = 0;
      else
        optEngine.Text = eng;

      int idx = 0;
      optCollation.SelectedIndex = 0;
      String cscoll = tableEditorBE.get_table_option_by_name("CHARACTER SET - COLLATE");

      foreach (String next_cscoll in optCollation.Items)
      {
        if (next_cscoll == cscoll)
        {
          optCollation.SelectedIndex = idx;
          break;
        }
        idx++;
      }

      TabText = nameTextBox.Text;

      columnsListModel.RefreshModel();
      indicesListModel.RefreshModel();
      fkListModel.RefreshModel();
      if (fkColumnsListModel != null)
        fkColumnsListModel.RefreshModel();

      // partitioning
      if (tableEditorBE.get_partition_type() == null || tableEditorBE.get_partition_type() == "")
        partEnable.Checked = false;
      else
      {
        partEnable.Checked = true;
        partFunction.SelectedItem = tableEditorBE.get_partition_type();

        partParams.Text = tableEditorBE.get_partition_expression();
        partManual.Checked = tableEditorBE.get_explicit_partitions();
        partCount.Text = tableEditorBE.get_partition_count().ToString();

        subpartFunction.SelectedItem = tableEditorBE.get_subpartition_type();

        subpartParams.Text = tableEditorBE.get_subpartition_expression();
        subpartManual.Checked = tableEditorBE.get_explicit_subpartitions();
        subpartCount.Text = tableEditorBE.get_subpartition_count().ToString();
      }

      // inserts
      insertsTextBox.Text = tableEditorBE.get_inserts();

      refreshPartitioningList();

      if (InitializingControls)
      {
        // XXX this is kind of hackish. to streamline the workflow, we always select the 
        // 1st tab of the table editor if its being shown for a new object
        if (tableEditorBE.is_new_table())
          mainTabControl.SelectedIndex = 0;
      }
    }


    private void refreshGridToolStripMenuItem_Click(object sender, EventArgs e)
    {
      columnsListModel.RefreshModel();
    }

    private void nameTextBox_TextChanged(object sender, EventArgs e)
    {
      if (!InitializingControls && !nameTextBox.Text.Equals(tableEditorBE.get_name()))
        tableEditorBE.set_name(nameTextBox.Text);

      TabText = nameTextBox.Text;
    }

    private void commentsTextBox_TextChanged(object sender, EventArgs e)
    {
      if (!InitializingControls && !optComments.Text.Equals(tableEditorBE.get_comment()))
        tableEditorBE.set_comment(optComments.Text);
    }

    private void nameTextBox_KeyDown(object sender, KeyEventArgs e)
    {
      if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return)
      {
        // Make sure a text change is now applied
        nameTextBox.CheckTextChangedNow();

        if ((e.Shift && e.KeyCode == Keys.Tab) || e.KeyCode != Keys.Tab)
          mainTabControl.SelectedTab = columnsTabPage;
        else
          ActiveControl = optComments;

        e.SuppressKeyPress = true;
        e.Handled = true;
      }
    }

    private void commentsTextBox_KeyDown(object sender, KeyEventArgs e)
    {
      if ((e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return) && e.Shift)
      {
        mainTabControl.SelectedTab = columnsTabPage;
        ActiveControl = columnsTreeView;
        nameNodeControl.BeginEdit();
      }
    }

    private void DbMysqlTableEditor_Load(object sender, EventArgs e)
    {
      MySQLApplicationContext.MainFormStatusBarText = "Press [Ctrl]+[Tab] to switch to the next tab.";

      ActiveControl = nameTextBox;
    }

    private void mainTabControl_SelectedIndexChanged(object sender, EventArgs e)
    {
      if (mainTabControl.SelectedTab == columnsTabPage)
      {
        // if the columns tab is selected, auto-edit a table-column
        if (columnsTreeView.Root.Children.Count >= 1)
        {
          ActiveControl = columnsTreeView;
          columnsTreeView.SelectedNode = columnsTreeView.Root.Children[columnsTreeView.Root.Children.Count - 1];
          nameNodeControl.BeginEdit();

          MySQLApplicationContext.MainFormStatusBarText = "Press [Tab] to move to the next field.";
        }
      }
      else if (mainTabControl.SelectedTab == indicesTabPage)
      {
        indicesListModel.RefreshModel();

        // if the index tab is selected, auto-edit an index
        if (indicesTreeView.Root.Children.Count >= 1)
        {
          ActiveControl = indicesTreeView;
          indicesTreeView.SelectedNode = indicesTreeView.Root.Children[indicesTreeView.Root.Children.Count - 1];
          indexNameNodeControl.BeginEdit();
        }

        MySQLApplicationContext.MainFormStatusBarText = "Use the right mouse button on the index grid to bring up the popup menu.";
      }
      else if (mainTabControl.SelectedTab == foreignKeysTabPage)
      {
        fkListModel.RefreshModel();

        // if the fk tab is selected, auto-edit a fk
        if (fkTreeView.Root.Children.Count >= 1)
        {
          ActiveControl = fkTreeView;
          fkTreeView.SelectedNode = fkTreeView.Root.Children[fkTreeView.Root.Children.Count - 1];
          nameFkNodeControl.BeginEdit();
        }
      }
      else if (mainTabControl.SelectedTab == insertsTabPage)
      {
        insertsTextBox.Text = tableEditorBE.get_inserts();
      }
    }

    private void mainTabControl_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
    {
      if (e.Control)
      {
        if (e.KeyCode == Keys.Left)
        {
          if (mainTabControl.SelectedIndex < mainTabControl.TabCount - 1)
            mainTabControl.SelectedIndex++;
        }
        else if (e.KeyCode == Keys.Right)
        {
          if (mainTabControl.SelectedIndex > 0)
            mainTabControl.SelectedIndex--;
        }
        else if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return)
        {
          Close();
        }
      }
    }


    #region Columns


    private void moveUpToolStripMenuItem_Click(object sender, EventArgs e)
    {
      if (columnsTreeView.SelectedNode != null)
      {
        TreeNodeAdv node = columnsTreeView.SelectedNode as TreeNodeAdv;
        GrtListNode lnode= node.Tag as GrtListNode;

        tableEditorBE.get_columns().reorder_up(lnode.NodeId);
        columnsListModel.RefreshModel();
      }
    }

    private void moveDownToolStripMenuItem_Click(object sender, EventArgs e)
    {
      if (columnsTreeView.SelectedNode != null)
      {
        TreeNodeAdv node = columnsTreeView.SelectedNode as TreeNodeAdv;
        GrtListNode lnode = node.Tag as GrtListNode;

        tableEditorBE.get_columns().reorder_down(lnode.NodeId);
        columnsListModel.RefreshModel();
      }
    }

    private void deleteSelectedColumnsToolStripMenuItem_Click(object sender, EventArgs e)
    {
      // Loop over all selected Nodes and delete them
      if (columnsTreeView.SelectedNodes.Count > 0)
      {
        List<NodeId> nodes = new List<NodeId>();

        foreach (TreeNodeAdv node in columnsTreeView.SelectedNodes)
        {
          GrtListNode listNode = node.Tag as GrtListNode;
          nodes.Add(listNode.NodeId);
        }
        nodes.Reverse();

        foreach (NodeId node in nodes)
          tableEditorBE.remove_column(node);

        columnsListModel.RefreshModel();
      }
    }

    private void columnsTreeView_NodeMouseDoubleClick(object sender, TreeNodeAdvMouseEventArgs e)
    {
      if (e.Node != null && e.Node.Tag != null)
      {
        GrtListNode node = e.Node.Tag as GrtListNode;

        if (e.Control is AdvNodeTextBox)
        {
          AdvNodeTextBox tbox = e.Control as AdvNodeTextBox;
          tbox.BeginEdit();
        }
        else if (e.Control is AdvNodeComboBox)
        {
          AdvNodeComboBox tbox = e.Control as AdvNodeComboBox;
          tbox.BeginEdit();
        }
      }
    }

    private void columnsTreeView_SelectionChanged(object sender, EventArgs e)
    {
      if (activeNode != null)
      {
        activeNode = null; // make sure the following changes wont be commited
        columnCommentTextBox.Text = "";
      }

      if (columnsTreeView.SelectedNode != null)
      {
        activeNode = columnsTreeView.SelectedNode.Tag as GrtListNode;

        if (activeNode != null)
        {
          // Set column details

          // comment
          string comment;
          columnsListModel.GrtList.get_field(activeNode.NodeId,
            (int)MySQLTableColumnsListBE.MySQLColumnListColumns.Comment, out comment);
          columnCommentTextBox.Text = comment;
          columnCommentTextBox.Enabled = true;

          // charset/collation
          String hasCharset = null;
          columnsListModel.GrtList.get_field(activeNode.NodeId,
            (int)MySQLTableColumnsListBE.MySQLColumnListColumns.HasCharset, out hasCharset);
          if (hasCharset == "1")
          {
            String column_cscoll;
            columnsListModel.GrtList.get_field(activeNode.NodeId,
              (int)MySQLTableColumnsListBE.MySQLColumnListColumns.CharsetCollation, out column_cscoll);

            columnCollationComboBox.Enabled = true;

            int idx = 0;
            columnCollationComboBox.SelectedIndex = 0;

            foreach (String next_cscoll in columnCollationComboBox.Items)
            {
              if (next_cscoll == column_cscoll)
              {
                columnCollationComboBox.SelectedIndex = idx;
                break;
              }
              idx++;
            }
          }
          else
          {
            columnCollationComboBox.SelectedIndex = 0;
            columnCollationComboBox.Enabled = false;
          }
        }
      }
      else
      {
        activeNode = null;
        columnCommentTextBox.Enabled = false;
        columnCollationComboBox.Enabled = false;
      }
    }

    private void columnCollationComboBox_SelectedIndexChanged(object sender, EventArgs e)
    {
      // set charset/collation
      if (activeNode != null)
      {
        if (columnCollationComboBox.SelectedIndex == 0)
        {
          columnsListModel.GrtList.set_field(activeNode.NodeId,
            (int)MySQLTableColumnsListBE.MySQLColumnListColumns.CharsetCollation, "");
        }
        else
        {
          columnsListModel.GrtList.set_field(activeNode.NodeId,
            (int)MySQLTableColumnsListBE.MySQLColumnListColumns.CharsetCollation,
              columnCollationComboBox.Text);
        }
      }
    }

    private void columnCommentTextBox_TextChanged(object sender, EventArgs e)
    {
      // column comment
      if (activeNode != null)
        columnsListModel.GrtList.set_field(activeNode.NodeId,
          (int)MySQLTableColumnsListBE.MySQLColumnListColumns.Comment, columnCommentTextBox.Text);
    }

    #endregion

    #region Index
    private void indicesTreeView_SelectionChanged(object sender, EventArgs e)
    {
      // remove old virtual value events
      if (indexColumnsTreeView.Model != null)
        indexColumnsListModel.DetachEvents();

      // if a new node was selected, create an inspector for it
      if (indicesTreeView.SelectedNode != null && indicesTreeView.SelectedNode.NextNode != null)
      {
        GrtListNode node = indicesTreeView.SelectedNode.Tag as GrtListNode;

        if (node != null)
        {
          // select the index
          tableEditorBE.get_indexes().select_index(node.NodeId);

          // create new inspector model for the selected value, pass valueNodeTextBox so virtual value events can be attached
          indexColumnsListModel = new DbMysqlTableIndexColumnsListModel(indexColumnsTreeView,
            tableEditorBE.get_indexes().get_columns(), tableEditorBE.get_columns(),
            indexColumnEnabledNodeControl,
            indexColumnNameNodeControl,
            indexColumnOrderNodeControl,
            indexColumnStorageNodeControl,
            indexColumnLengthNodeControl);

          // assign model to treeview
          indexColumnsTreeView.Model = indexColumnsListModel;

          MySQL.Grt.Db.IndexListBE ilist = tableEditorBE.get_indexes();

          String value = null;

          ilist.get_field(node.NodeId, (int)MySQL.Grt.Db.IndexListBE.IndexListColumns.Comment, out value);
          indexCommentText.Text = value;
          ilist.get_field(node.NodeId, (int)MySQLIndexListBE.Columns.StorageType, out value);
          indexStorageTypeComboBox.SelectedItem = value != "" ? value : null;
          ilist.get_field(node.NodeId, (int)MySQLIndexListBE.Columns.RowBlockSize, out value);
          indexRowBlockSizeText.Text = value;
          ilist.get_field(node.NodeId, (int)MySQLIndexListBE.Columns.Parser, out value);
          indexParserText.Text = value;
        }
        else
        {
          indexColumnsTreeView.Model = null;
          indexCommentText.Text = "";
          indexStorageTypeComboBox.SelectedItem = null;
          indexRowBlockSizeText.Text = "";
          indexParserText.Text = "";
        }
      }
      else
      {
        indexColumnsTreeView.Model = null;
        indexCommentText.Text = "";
        indexStorageTypeComboBox.SelectedItem = null;
        indexRowBlockSizeText.Text = "";
        indexParserText.Text = "";
      }
    }


    void indexCommentText_TextChanged(object sender, System.EventArgs e)
    {
      if (indicesTreeView.SelectedNode != null)
      {
        GrtListNode node = indicesTreeView.SelectedNode.Tag as GrtListNode;

        if (node != null)
        {
          String text = null;
          tableEditorBE.get_indexes().get_field(node.NodeId, 
            (int)MySQL.Grt.Db.IndexListBE.IndexListColumns.Comment, out text);
          if (indexCommentText.Text != text)
          {
            tableEditorBE.get_indexes().set_field(node.NodeId, 
              (int)MySQL.Grt.Db.IndexListBE.IndexListColumns.Comment, indexCommentText.Text);
          }
        }
      }
    }

    void indexParserText_TextChanged(object sender, System.EventArgs e)
    {
      if (indicesTreeView.SelectedNode != null)
      {
        GrtListNode node = indicesTreeView.SelectedNode.Tag as GrtListNode;

        if (node != null)
        {
          String text = null;
          tableEditorBE.get_indexes().get_field(node.NodeId, 
            (int)MySQL.GUI.Workbench.Plugins.MySQLIndexListBE.Columns.Parser, out text);
          if (indexParserText.Text != text)
          {
            tableEditorBE.get_indexes().set_field(node.NodeId, 
              (int)MySQL.GUI.Workbench.Plugins.MySQLIndexListBE.Columns.Parser, indexParserText.Text);
          }
        }
      }
    }

    void indexRowBlockSizeText_TextChanged(object sender, System.EventArgs e)
    {
      if (indicesTreeView.SelectedNode != null)
      {
        GrtListNode node = indicesTreeView.SelectedNode.Tag as GrtListNode;

        if (node != null)
        {
          String text = null;
          tableEditorBE.get_indexes().get_field(node.NodeId, 
            (int)MySQL.GUI.Workbench.Plugins.MySQLIndexListBE.Columns.RowBlockSize, out text);
          if (indexRowBlockSizeText.Text != text)
          {
            tableEditorBE.get_indexes().set_field(node.NodeId, 
              (int)MySQL.GUI.Workbench.Plugins.MySQLIndexListBE.Columns.RowBlockSize, indexRowBlockSizeText.Text);
          }
        }
      }
    }

    void indexStorageTypeComboBox_SelectedIndexChanged(object sender, System.EventArgs e)
    {
      if (indicesTreeView.SelectedNode != null)
      {
        GrtListNode node = indicesTreeView.SelectedNode.Tag as GrtListNode;

        if (node != null)
        {
          String text = null;
          tableEditorBE.get_indexes().get_field(node.NodeId,
            (int)MySQL.GUI.Workbench.Plugins.MySQLIndexListBE.Columns.StorageType, out text);

          if (indexStorageTypeComboBox.SelectedItem == null)
          {
            tableEditorBE.get_indexes().set_field(node.NodeId,
              (int)MySQL.GUI.Workbench.Plugins.MySQLIndexListBE.Columns.StorageType,
              "");
          }
          else if (indexStorageTypeComboBox.SelectedItem.ToString() != text)
          {
            tableEditorBE.get_indexes().set_field(node.NodeId,
              (int)MySQL.GUI.Workbench.Plugins.MySQLIndexListBE.Columns.StorageType,
              indexStorageTypeComboBox.SelectedItem.ToString());
          }
        }
      }
    }
    
    private void deleteSelectedIndicesToolStripMenuItem_Click(object sender, EventArgs e)
    {
      // Loop over all selected Nodes and delete them
      if (indicesTreeView.SelectedNodes.Count > 0)
      {
        List<NodeId> nodes = new List<NodeId>();

        foreach (TreeNodeAdv node in indicesTreeView.SelectedNodes)
        {
          GrtListNode listNode = node.Tag as GrtListNode;
          nodes.Add(listNode.NodeId);
        }
        nodes.Reverse();

        foreach (NodeId node in nodes)
          tableEditorBE.remove_index(node);

        indicesListModel.RefreshModel();
      }
    }

    #endregion

    #region Options

    private void refreshTableOptUi()
    {
      // Refresh th UI based on the stored table options
    }

    private void setTableOpt()
    {
      // TODO: add KEY_BLOCK_SIZE option

      switch (optPackKeys.SelectedIndex)
      {
        case 0:
          tableEditorBE.set_table_option_by_name("PACK_KEYS", "DEFAULT");
          break;
        case 1:
          tableEditorBE.set_table_option_by_name("PACK_KEYS", "0");
          break;
        case 2:
          tableEditorBE.set_table_option_by_name("PACK_KEYS", "1");
          break;
      }

      tableEditorBE.set_table_option_by_name("PASSWORD", optTablePassword.Text);
      tableEditorBE.set_table_option_by_name("AUTO_INCREMENT", optAutoIncrement.Text);

      if (optDelayKeyUpdates.Checked)
        tableEditorBE.set_table_option_by_name("DELAY_KEY_WRITE", "1");
      else
        tableEditorBE.set_table_option_by_name("DELAY_KEY_WRITE", "0");

      switch (optRowFormat.SelectedIndex)
      {
        case 0:
          tableEditorBE.set_table_option_by_name("ROW_FORMAT", "DEFAULT");
          break;
        case 1:
          tableEditorBE.set_table_option_by_name("ROW_FORMAT", "DYNAMIC");
          break;
        case 2:
          tableEditorBE.set_table_option_by_name("ROW_FORMAT", "FIXED");
          break;
        case 3:
          tableEditorBE.set_table_option_by_name("ROW_FORMAT", "COMPRESSED");
          break;
        case 4:
          tableEditorBE.set_table_option_by_name("ROW_FORMAT", "REDUNDANT");
          break;
        case 5:
          tableEditorBE.set_table_option_by_name("ROW_FORMAT", "COMPACT");
          break;
      }

      tableEditorBE.set_table_option_by_name("AVG_ROW_LENGTH", optAvgRowLength.Text);
      tableEditorBE.set_table_option_by_name("MAX_ROWS", optMaxRows.Text);
      tableEditorBE.set_table_option_by_name("MIN_ROWS", optMinRows.Text);

      if (optUseChecksum.Checked)
        tableEditorBE.set_table_option_by_name("CHECKSUM", "1");
      else
        tableEditorBE.set_table_option_by_name("CHECKSUM", "0");

      tableEditorBE.set_table_option_by_name("DATA DIRECTORY", optDataDirectory.Text);
      tableEditorBE.set_table_option_by_name("INDEX DIRECTORY", optIndexDirectory.Text);
      tableEditorBE.set_table_option_by_name("UNION", optUnionTables.Text);

      switch (optMergeMethod.SelectedIndex)
      {
        case 0:
          tableEditorBE.set_table_option_by_name("INSERT_METHOD", "NO");
          break;
        case 1:
          tableEditorBE.set_table_option_by_name("INSERT_METHOD", "FIRST");
          break;
        case 2:
          tableEditorBE.set_table_option_by_name("INSERT_METHOD", "LAST");
          break;
      }

      String eng = optEngine.Text;
      if (eng == optEngine.Items[0].ToString())
        eng = "";

      tableEditorBE.set_table_option_by_name("ENGINE", eng);

      // set charset/collation
      if (optCollation.SelectedIndex == 0)
      {
        tableEditorBE.set_table_option_by_name("CHARACTER SET - COLLATE", "");
      }
      else
      {
        int cs_sep = optCollation.Text.IndexOf('-');
        if (cs_sep != -1)
        {
          tableEditorBE.set_table_option_by_name("CHARACTER SET - COLLATE", optCollation.Text);
        }
      }
    }

    private void tableOptChanged(object sender, EventArgs e)
    {
      // When a UI value changes, update the table options
      if (!InitializingControls && !insideRefreshFormData)
      {
        insideRefreshFormData = true;
        try
        {
          setTableOpt();
        }
        finally
        {
          insideRefreshFormData = false;
        }
      }
    }

    private void nameTextBox_Leave(object sender, EventArgs e)
    {
      // Make sure a text change is now applied
      nameTextBox.CheckTextChangedNow();
    }

    private void DbMysqlTableEditor_KeyDown(object sender, KeyEventArgs e)
    {
      if (e.KeyCode == Keys.Right && e.Control && e.Alt)
      {
        if (mainTabControl.SelectedIndex < mainTabControl.TabCount - 1)
          mainTabControl.SelectedIndex = mainTabControl.SelectedIndex + 1;
        else
          mainTabControl.SelectedIndex = 0;

        e.Handled = true;
      }

      if (e.KeyCode == Keys.Left && e.Control && e.Alt)
      {
        if (mainTabControl.SelectedIndex > 0)
          mainTabControl.SelectedIndex = mainTabControl.SelectedIndex - 1;
        else
          mainTabControl.SelectedIndex = mainTabControl.TabCount - 1;

        e.Handled = true;
      }
    }

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
      Application.AddMessageFilter(wheelFilter);

      base.OnFormClosing(e);
    }

    #endregion

    #region Partitioning


    private void partEnable_CheckedChanged(object sender, EventArgs e)
    {
      bool flag = partEnable.Checked;

      partFunction.Enabled = flag;
      partParams.Enabled = flag;
      partCount.Enabled = flag;
      partManual.Enabled = flag;
      //partPanel.Enabled = flag;

      if (flag)
      {
        if (tableEditorBE.get_partition_type() == "")
        {
          // this will set partition function to HASH only if
          // nothing is selected in the drop-down, otherwise
          // the currect selection will be applied to backend
          tableEditorBE.set_partition_type("HASH");
          partFunction_SelectedIndexChanged(this, null);
        }
      }
      else
        tableEditorBE.set_partition_type("");

      if (partFunction.SelectedItem == null || !(partFunction.SelectedItem.ToString() == "RANGE" || partFunction.SelectedItem.ToString() == "LIST"))
        flag = false;

      subpartFunction.Enabled = flag;
      subpartParams.Enabled = flag;
      subpartCount.Enabled = flag;
      subpartManual.Enabled = flag;
    }

    private void partFunction_SelectedIndexChanged(object sender, EventArgs e)
    {
      if (partFunction.SelectedItem == null)
      {
        partFunction.SelectedItem = tableEditorBE.get_partition_type();
        return;
      }

      if (partFunction.SelectedItem.ToString() != tableEditorBE.get_partition_type())
      {
        if (!tableEditorBE.set_partition_type(partFunction.SelectedItem.ToString()))
        {
          partFunction.SelectedItem = tableEditorBE.get_partition_type();
          return;
        }
      }
      if (partFunction.SelectedItem != null && (partFunction.SelectedItem.ToString() == "RANGE" || partFunction.SelectedItem.ToString() == "LIST"))
      {
        subpartFunction.Enabled = true;
        subpartParams.Enabled = true;
        subpartCount.Enabled = true;
        subpartManual.Enabled = true;
      }
      else
      {
        subpartFunction.Enabled = false;
        subpartParams.Enabled = false;
        subpartCount.Enabled = false;
        subpartManual.Enabled = false;
      }
    }


    void subpartFunction_SelectedIndexChanged(object sender, System.EventArgs e)
    {
      if (subpartFunction.SelectedItem.ToString() != tableEditorBE.get_subpartition_type())
      {
        if (subpartFunction.SelectedItem == null || !tableEditorBE.set_subpartition_type(subpartFunction.SelectedItem.ToString()))
        {
          subpartFunction.SelectedItem = tableEditorBE.get_subpartition_type();
          return;
        }
      }
    }


    private void refreshPartitioningList()
    {
      partitionTreeModel.RefreshModel();

      partitionTreeView.ExpandAll();
    }


    void partParams_TextChanged(object sender, System.EventArgs e)
    {
      if (!insideRefreshFormData)
        tableEditorBE.set_partition_expression(partParams.Text);
    }

    void subpartParams_TextChanged(object sender, System.EventArgs e)
    {
      if (!insideRefreshFormData)
        tableEditorBE.set_subpartition_expression(subpartParams.Text);
    }

    private void partCount_SelectedIndexChanged(object sender, EventArgs e)
    {
      if (partCount.SelectedItem != null)
        tableEditorBE.set_partition_count(int.Parse(partCount.SelectedItem.ToString()));

      refreshPartitioningList();
    }

    private void subpartCount_SelectedIndexChanged(object sender, EventArgs e)
    {
      if (subpartCount.SelectedItem != null)
        tableEditorBE.set_subpartition_count(int.Parse(subpartCount.SelectedItem.ToString()));

      refreshPartitioningList();
    }

    private void partManual_CheckedChanged(object sender, EventArgs e)
    {
      tableEditorBE.set_explicit_partitions(partManual.Checked);

      partCount.SelectedValue = tableEditorBE.get_partition_count();

      refreshPartitioningList();
    }

    private void subpartManual_CheckedChanged(object sender, EventArgs e)
    {
      tableEditorBE.set_explicit_subpartitions(subpartManual.Checked);

      subpartCount.SelectedValue = tableEditorBE.get_subpartition_count();

      refreshPartitioningList();
    }

    #endregion

    #region Foreign Keys

    private void fkTreeView_SelectionChanged(object sender, EventArgs e)
    {
      // remove old virtual value events
      if (fkColumnsTreeView.Model != null)
        fkColumnsListModel.DetachEvents();

      // if a new node was selected, create an inspector for it
      if (fkTreeView.SelectedNode != null && fkTreeView.SelectedNode.NextNode != null)
      {
        GrtListNode node = fkTreeView.SelectedNode.Tag as GrtListNode;

        if (node != null)
        {
          String text;

          // select the fk
          tableEditorBE.get_fks().select_fk(node.NodeId);

          // create new inspector model for the selected value, pass valueNodeTextBox so virtual value events can be attached
          fkColumnsListModel = new DbMysqlTableFkColumnListModel(fkColumnsTreeView,
            tableEditorBE.get_fks().get_columns(),
            columnEnabledFkNodeControl, columnFkNodeControl,
            targetColumnFkNodeControl, tableEditorBE);

          tableEditorBE.get_fks().get_field(node.NodeId, (int)MySQL.Grt.Db.FKConstraintListBE.FKConstraintListColumns.OnDelete, out text);
          if (String.IsNullOrEmpty(text))
            onDeleteActionComboBox.SelectedIndex = -1;
          else
            onDeleteActionComboBox.SelectedItem = text;

          tableEditorBE.get_fks().get_field(node.NodeId, (int)MySQL.Grt.Db.FKConstraintListBE.FKConstraintListColumns.OnUpdate, out text);
          if (String.IsNullOrEmpty(text))
            onUpdateActionComboBox.SelectedIndex = -1;
          else
            onUpdateActionComboBox.SelectedItem = text;

          tableEditorBE.get_fks().get_field(node.NodeId, (int)MySQL.Grt.Db.FKConstraintListBE.FKConstraintListColumns.Comment, out text);
          fkCommentText.Text = text;

          // assign model to treeview
          fkColumnsTreeView.Model = fkColumnsListModel;
        }
        else
        {
          fkColumnsTreeView.Model = null;
          onDeleteActionComboBox.SelectedIndex= -1;
          onUpdateActionComboBox.SelectedIndex = -1;
          fkCommentText.Text = "";
        }
      }
      else
      {
        fkColumnsTreeView.Model = null;
        onDeleteActionComboBox.SelectedIndex = -1;
        onUpdateActionComboBox.SelectedIndex = -1;
        fkCommentText.Text = "";
      }
    }

    private void fkTreeView_KeyPress(object sender, KeyPressEventArgs e)
    {
      // initially was intended for deleting fk columns using keyboard
    }

    private void deleteSelectedFKsToolStripMenuItem_Click(object sender, EventArgs e)
    {
      // Loop over all selected Nodes and delete them
      if (fkTreeView.SelectedNodes.Count > 0)
      {
        List<NodeId> nodes = new List<NodeId>();

        foreach (TreeNodeAdv node in fkTreeView.SelectedNodes)
        {
          GrtListNode listNode = node.Tag as GrtListNode;
          nodes.Add(listNode.NodeId);
        }
        nodes.Reverse();

        foreach (NodeId node in nodes)
          tableEditorBE.remove_fk(node);

        fkListModel.RefreshModel();
      }
    }

    private void onUpdateActionComboBox_SelectedIndexChanged(object sender, EventArgs e)
    {
      if (onUpdateActionComboBox.SelectedItem != null)
      {
        foreach (TreeNodeAdv node in fkTreeView.SelectedNodes)
        {
          GrtListNode listNode = node.Tag as GrtListNode;

          tableEditorBE.get_fks().set_field(listNode.NodeId,
            (int)MySQL.Grt.Db.FKConstraintListBE.FKConstraintListColumns.OnUpdate,
            onUpdateActionComboBox.SelectedItem.ToString());
        }
      }
    }

    private void onDeleteActionComboBox_SelectedIndexChanged(object sender, EventArgs e)
    {
      if (onDeleteActionComboBox.SelectedItem != null)
      {
        foreach (TreeNodeAdv node in fkTreeView.SelectedNodes)
        {
          GrtListNode listNode = node.Tag as GrtListNode;

          tableEditorBE.get_fks().set_field(listNode.NodeId,
            (int)MySQL.Grt.Db.FKConstraintListBE.FKConstraintListColumns.OnDelete,
            onDeleteActionComboBox.SelectedItem.ToString());
        }
      }
    }

    private void fkCommentText_TextChanged(object sender, EventArgs e)
    {
      foreach (TreeNodeAdv node in fkTreeView.SelectedNodes)
      {
        GrtListNode listNode = node.Tag as GrtListNode;

        tableEditorBE.get_fks().set_field(listNode.NodeId,
          (int)MySQL.Grt.Db.FKConstraintListBE.FKConstraintListColumns.Comment,
          fkCommentText.Text);
      }
    }

    #endregion

    #region Triggers

    private void triggersTabPage_Enter(object sender, EventArgs e)
    {
      ActiveControl = sqlEd.sqlEditor;
    }

    private void triggersEditorParseLogCallback(List<String> strlist)
    {
      // FIXME: this method isn't triggered at all.
      if (strlist.Count > 0)
      {
        // TODO: allow clicking the label to bring up a list of errors.
      }
    }

    #endregion

    #region Inserts

    private void openInsertsEditorButton_Click(object sender, EventArgs e)
    {
      DbMysqlInsertsEditor editor = new DbMysqlInsertsEditor(tableEditorBE);

      editor.ShowDialog();

      insertsTextBox.Text = tableEditorBE.get_inserts();
    }

    #endregion


    void default_NodeMouseDoubleClick(object sender, Aga.Controls.Tree.TreeNodeAdvMouseEventArgs e)
    {
      if (e.Node != null && e.Node.Tag != null)
      {
        if (e.Control is AdvNodeTextBox)
        {
          AdvNodeTextBox tbox = e.Control as AdvNodeTextBox;
          tbox.BeginEdit();
        }
        else if (e.Control is AdvNodeComboBox)
        {
          AdvNodeComboBox tbox = e.Control as AdvNodeComboBox;
          tbox.BeginEdit();
        }
      }
    }

    #endregion

    #region Drag & Drop

    private void columnsTreeView_DragEnter(object sender, DragEventArgs e)
    {
      if (e.Data.GetDataPresent(typeof(GrtValue)))
      {
        GrtValue value= (GrtValue)e.Data.GetData(typeof(GrtValue));

        if (value != null && value.is_object_instance_of("db.UserDatatype"))
        {
          e.Effect = DragDropEffects.Copy;
          return;
        }
      }

      e.Effect = DragDropEffects.Move;
    }

    private void columnsTreeView_DragDrop(object sender, DragEventArgs e)
    {
      if (e.Data.GetDataPresent(typeof(GrtValue)))
      {
        GrtValue value = (GrtValue)e.Data.GetData(typeof(GrtValue));

        if (value.is_object_instance_of("db.UserDatatype"))
        {
          TreeNodeAdv node = columnsTreeView.DropPosition.Node as TreeNodeAdv;
          if(node != null)
            tableEditorBE.get_columns().set_column_type(((GrtListNode)node.Tag).NodeId, value);
          return;
        }
      }


      TreeNodeAdv[] nodes = (TreeNodeAdv[])e.Data.GetData(typeof(TreeNodeAdv[]));
      TreeNodeAdv dropNode = columnsTreeView.DropPosition.Node as TreeNodeAdv;

      int src_index = columnsTreeView.Root.Children.IndexOf(nodes[0]);
      int index = columnsTreeView.Root.Children.IndexOf(dropNode);

      if (columnsTreeView.DropPosition.Position != NodePosition.Before)
        index++;

      if (index > src_index)
        index--;

      foreach (TreeNodeAdv nextNode in nodes)
      {
        GrtListNode grt_node = nextNode.Tag as GrtListNode;
        tableEditorBE.get_columns().reorder(grt_node.NodeId, index);
        columnsTreeView.SelectedNode = columnsTreeView.Root.Children[index];
        if (columnsTreeView.DropPosition.Position == NodePosition.Before)
          index--;
        else
          index++;
      }
    }

    private void columnsTreeView_ItemDrag(object sender, ItemDragEventArgs e)
    {
      columnsTreeView.DoDragDropSelectedNodes(DragDropEffects.Move);
    }

    #endregion

    private void undoItem_Click(object sender, EventArgs e)
    {
      sqlEd.sqlEditor.UndoRedo.Undo();
    }

    private void cutItem_Click(object sender, EventArgs e)
    {
      sqlEd.sqlEditor.Clipboard.Cut();
    }

    private void copyItem_Click(object sender, EventArgs e)
    {
      sqlEd.sqlEditor.Clipboard.Copy();
    }

    private void pasteItem_Click(object sender, EventArgs e)
    {
      sqlEd.sqlEditor.Clipboard.Paste();
    }

    private void selectAllItem_Click(object sender, EventArgs e)
    {
      sqlEd.sqlEditor.Selection.SelectAll();
    }

    private void redoItem_Click(object sender, EventArgs e)
    {
      sqlEd.sqlEditor.UndoRedo.Redo();
    }

    private void editorContextMenu_Opening(object sender, CancelEventArgs e)
    {
      // Enable/Disable menu items depending on the edit content.
      undoItem.Enabled = sqlEd.sqlEditor.UndoRedo.CanUndo;
      redoItem.Enabled = sqlEd.sqlEditor.UndoRedo.CanRedo;
      cutItem.Enabled = sqlEd.sqlEditor.Selection.Length > 0;
      copyItem.Enabled = sqlEd.sqlEditor.Selection.Length > 0;
      pasteItem.Enabled = sqlEd.sqlEditor.Clipboard.CanPaste;
      selectAllItem.Enabled = sqlEd.sqlEditor.TextLength > 0;
    }

  }
}