using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using WeifenLuo.WinFormsUI.Docking;
using MySQL.Grt;
using MySQL.Utilities;
using MySQL.Grt.Db;

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

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

    private int refreshBlocked = 0;

    /// <summary>
    /// Used to implement the IWorkbenchDocument interface and support closing notification
    /// </summary>
    protected WorkbenchDocumentClosing onWorkbenchDocumentClosing = null;

    protected BaseEditor editorBE;

    #endregion

    #region SqlEditorHelper

    protected class SqlEditorHelper
    {
      private BaseEditor editorBE;
      private DBObjectEditorBE dbObjEditorBE;
      public Control parent;
      public MySQL.Grt.Db.Sql.SqlEditor sqlEditor;
      public delegate void ProcessParserLogDelegate(List<String> messages);
      private ProcessParserLogDelegate processParserLog;
      public delegate void SetSqlDelegate(string sql);
      private SetSqlDelegate setSql;

      class Token
      {
        public int line;
        public int errTokLinePos;
        public int errTokLen;
        public string msg;
      }
      List<Token> errTokens;

      public SqlEditorHelper(GrtManager grtManager, Control parent, ProcessParserLogDelegate processParserLog)
      {
        errTokens = new List<Token>();

        sqlEditor = new MySQL.Grt.Db.Sql.SqlEditor(grtManager);
        sqlEditor.BackgroundAction = Parse;

        this.parent = parent;
        parent.Controls.Add(sqlEditor);
        sqlEditor.Dock = DockStyle.Fill;
        sqlEditor.Scrolling.ScrollBars = ScrollBars.Both;
        sqlEditor.Scrolling.HorizontalWidth = 20000;

        this.processParserLog = processParserLog;
      }

      public void SetEditorBackend(BaseEditor editorBE, SetSqlDelegate setSql)
      {
        if (this.editorBE != editorBE)
        {
          this.editorBE = editorBE;
          dbObjEditorBE = editorBE as DBObjectEditorBE;
          sqlEditor.BE = editorBE.get_sql_editor();
          if (null != dbObjEditorBE)
          {
            //!dbObjEditorBE.set_sql_parser_log_cb(new DBObjectEditorBE.VoidStringListDelegate(processParserLog));
            dbObjEditorBE.set_sql_parser_log_cb(ProcessParserLog);
            dbObjEditorBE.set_sql_parser_err_cb(ProcessSyntaxError);
          }
        }

        this.setSql = setSql;
      }

      public void UnregisterCallbacks()
      {
        if (null != dbObjEditorBE)
        {
          dbObjEditorBE.set_sql_parser_log_cb(null);
          dbObjEditorBE.set_sql_parser_err_cb(null);
        }
      }

      public void ForceBackgroundActionTimer()
      {
        sqlEditor.ForceBackgroundActionTimer();
      }

      public BaseEditor EditorBE
      {
        get { return editorBE; }
      }

      public void Parse()
      {
        errTokens.Clear();
        if (null != sqlEditor)
        {
          sqlEditor.ResetSyntaxErrors();
          if (null != setSql)
            setSql(sqlEditor.Text);
        }
      }

      public int ProcessSyntaxError(int line, int errTokLinePos, int errTokLen, String msg)
      {
        Token tok = new Token();
        tok.line = line;
        tok.errTokLinePos = errTokLinePos;
        tok.errTokLen = errTokLen;
        tok.msg = msg;

        errTokens.Add(tok);

        return 0;
      }

      private void ProcessParserLog(List<String> messages)
      {
        if (null != sqlEditor)
          foreach (Token tok in errTokens)
            sqlEditor.ProcessSyntaxError(tok.line, tok.errTokLinePos, tok.errTokLen, tok.msg);

        if (null != processParserLog)
          processParserLog(messages);
      }
    };

    protected SqlEditorHelper sqlEd;

    #endregion

    #region Constructors

    /// <summary>
    /// Standard constructor
    /// </summary>
    protected ObjectEditorPlugin() : base()
    {
    }

    /// <summary>
    /// Overloaded constructor taking the GRT Manager and GRT object to edit
    /// </summary>
    /// <param name="GrtManager">The GRT Manager</param>
    /// <param name="GrtObject">The object to edit</param>
    public ObjectEditorPlugin(GrtManager GrtManager, GrtValue GrtList)
      : base(GrtManager, GrtList)
    {
    }

    public ObjectEditorPlugin(BaseEditor editor)
      : base(editor.get_grt_manager(), editor.get_object())
    {
      editorBE = editor;
    }

    #endregion

    #region Properties

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

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

    #endregion

    #region IWorkbenchDocument Interface

    public WorkbenchDocumentClosing OnWorkbenchDocumentClosing
    {
      get { return onWorkbenchDocumentClosing; }
      set { onWorkbenchDocumentClosing = value; }
    }

    public virtual UIForm BackendClass 
    {
      get { return null; } 
    }

    #endregion

    #region Form Implementation

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

    public virtual bool ChangeGrtList(GrtManager GrtManager, GrtValue GrtList)
    {
      return false;
    }

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
      if (null != sqlEd)
        sqlEd.ForceBackgroundActionTimer();

      base.OnFormClosing(e);

      if (onWorkbenchDocumentClosing != null)
        onWorkbenchDocumentClosing(this, e);

      if (editorBE != null)
        editorBE.disable_auto_refresh();
    }

    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 delegate void RefreshFormDataCallback();
    protected virtual void RefreshFormData() {}

    protected virtual void CallRefreshFormData() 
    {
      if (insideRefreshFormData || refreshBlocked > 0)
        return;

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

    // this method might be called from non-UI thread
    protected void RefreshFormDataInvoke() 
    {
       this.Invoke(new RefreshFormDataCallback(CallRefreshFormData));
    }
    #endregion
  }
}