// Copyright © 2008, 2016, Oracle and/or its affiliates. All rights reserved.
//
// MySQL for Visual Studio is licensed under the terms of the GPLv2
// <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most
// MySQL Connectors. There are special exceptions to the terms and
// conditions of the GPLv2 as it is applied to this software, see the
// FLOSS License Exception
// <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
//
// 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 Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell.Interop;
using MySql.Utility.Enums;
using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;

namespace MySql.Data.VisualStudio.Editors
{
  /// <summary>
  /// VSCodeEditor class will handle COM objects related to the code panel at the Editor class.
  /// </summary>
  internal class VSCodeEditor
  {
    private ServiceBroker services;
    private IVsCodeWindow codeWindow;
    private IntPtr parentHandle;
    private IntPtr hwnd;
    private bool noContent;
    private IVsTextBuffer textBuffer;
    private VSCodeEditorUserControl parent;

    /// <summary>
    /// Initializes a new instance of the <see cref="VSCodeEditor"/> class.
    /// </summary>
    public VSCodeEditor()
    {
      hwnd = IntPtr.Zero;
      parentHandle = IntPtr.Zero;
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="VSCodeEditor"/> class.
    /// </summary>
    /// <param name="windowParent">The parent window.</param>
    /// <param name="services">The services broker.</param>
    public VSCodeEditor(VSCodeEditorUserControl windowParent, ServiceBroker services)
      : this()
    {
      parent = windowParent;
      parentHandle = parent.Handle;
      this.services = services;
      CreateCodeWindow();
    }

    public VSCodeEditor(IOleServiceProvider psp)
      : this()
    {
      services = new ServiceBroker(psp);
      CreateCodeWindow();
    }

    public IntPtr Hwnd
    {
      get { return hwnd; }
    }

    public IVsCodeWindow CodeWindow
    {
      get { return codeWindow; }
    }

    public bool Dirty
    {
      get
      {
        uint flags;
        textBuffer.GetStateFlags(out flags);
        return (flags & (uint)BUFFERSTATEFLAGS.BSF_MODIFIED) != 0;
      }
      set
      {
        uint flags;
        textBuffer.GetStateFlags(out flags);
        if (value)
          flags |= (uint)BUFFERSTATEFLAGS.BSF_MODIFIED;
        else
          flags &= ~(uint)BUFFERSTATEFLAGS.BSF_MODIFIED;
        textBuffer.SetStateFlags(flags);
      }
    }

    public string Text
    {
      get
      {
        IVsTextLines lines = (IVsTextLines)textBuffer;
        int lineCount, lineLength;
        string text;
        lines.GetLineCount(out lineCount);
        lines.GetLengthOfLine(lineCount - 1, out lineLength);
        lines.GetLineText(0, 0, lineCount - 1, lineLength, out text);
        return text;
      }
      set
      {
        if (noContent)
          Initialize(value);
        else
          Replace(value);
        noContent = false;
      }
    }

    private void Initialize(string value)
    {
      IVsTextLines lines = (IVsTextLines)textBuffer;
      lines.InitializeContent(value, value.Length);
    }

    private void Replace(string value)
    {
      int endLine, endCol;
      IVsTextLines lines = (IVsTextLines)textBuffer;
      lines.GetLastLineIndex(out endLine, out endCol);

      IntPtr pText = Marshal.StringToCoTaskMemAuto(value);

      try
      {
        lines.ReplaceLines(0, 0, endLine, endCol, pText, value.Length, null);
      }
      finally
      {
        Marshal.FreeCoTaskMem(pText);
      }
    }

    private void CreateCodeWindow()
    {
      // create code window
      Guid guidVsCodeWindow = typeof(VsCodeWindowClass).GUID;
      codeWindow = services.CreateObject(services.LocalRegistry, guidVsCodeWindow, typeof(IVsCodeWindow).GUID) as IVsCodeWindow;

      CustomizeCodeWindow();

      // set buffer
      Guid guidVsTextBuffer = typeof(VsTextBufferClass).GUID;
      textBuffer = services.CreateObject(services.LocalRegistry, guidVsTextBuffer,
          typeof(IVsTextBuffer).GUID) as IVsTextBuffer;
      textBuffer.InitializeContent("ed", 2);

      Guid langSvc = new Guid();
      if (parent == null || typeof(SqlEditor) == parent.Editor.GetType())
      {
        langSvc = new Guid(MySqlLanguageService.IID);
      }
      else
      {
        //TODO: [MYSQLFORVS-276] - CREATE A CLASSIFIER EXTENSION THAT COLORS THE APPROPRIATE TEXT (MYJS FILES).
        if (parent == null || typeof(MySqlHybridScriptEditor) == parent.Editor.GetType() && ((MySqlHybridScriptEditor)parent.Editor).ScriptLanguageType == ScriptLanguageType.JavaScript)
          langSvc = new Guid(MyJsLanguageService.IID);
        //else
        //TODO: [MYSQLFORVS-402] - CREATE A CLASSIFIER EXTENSION THAT COLORS THE APPROPRIATE TEXT (MYPY FILES).
        else if (parent == null || typeof(MySqlHybridScriptEditor) == parent.Editor.GetType() && ((MySqlHybridScriptEditor)parent.Editor).ScriptLanguageType == ScriptLanguageType.Python)
        {
          langSvc = new Guid(MyPyLanguageService.IID);
        }

      }

      int hr = textBuffer.SetLanguageServiceID(ref langSvc);
      if (hr != VSConstants.S_OK)
        Marshal.ThrowExceptionForHR(hr);

      hr = codeWindow.SetBuffer(textBuffer as IVsTextLines);
      if (hr != VSConstants.S_OK)
        Marshal.ThrowExceptionForHR(hr);

      // this is necessary for the adapters to work in VS2010
      Initialize(String.Empty);

      // create pane window
      IVsWindowPane windowPane = codeWindow as IVsWindowPane;
      hr = windowPane.SetSite(services.IOleServiceProvider);
      if (hr != VSConstants.S_OK)
        Marshal.ThrowExceptionForHR(hr);
      if (parentHandle != IntPtr.Zero)
      {
        hr = windowPane.CreatePaneWindow(parentHandle, 0, 0, 100, 100, out hwnd);
        if (hr != VSConstants.S_OK)
          Marshal.ThrowExceptionForHR(hr);
      }
    }

    private void CustomizeCodeWindow()
    {
    }
  }
}
