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;

namespace MySQL.GUI.Shell
{
	public partial class GrtPromptForm : DockContent
  {
    #region Win32 wrappers

    /*[DllImport("user32")]
    private static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, int lParam);

    private int EM_LINEINDEX = 0xbb;
    private int EM_LINEFROMCHAR = 0xc9;
    private int EM_GETSEL = 0xb0;*/

    #endregion

    #region Member variables

    /// <summary>
		/// The GRT Manager
		/// </summary>
		private GrtManager grtManager;

		/// <summary>
		/// The GRT Shell
		/// </summary>
		private GrtShell grtShell;

    // TODO: the following members aren't used anywhere and produce warnings. Check if they can be removed.
		/// <summary>
		/// The line the current command starts
		/// </summary>
		// private int cmdStartLine = 1;

		/// <summary>
		/// The current shell prompt
		/// </summary>
		// private string currentPrompt = "";

		/// <summary>
		/// The prompt used when the user adds another line to the current command
		/// </summary>
		// private string nextLinePrompt = "";

		/// <summary>
		/// The length of the current prompt
		/// </summary>
		// private int currentPromptLength = 0;

		/// <summary>
		/// The character position for the start of current command
		/// </summary>
		// private int currentCmdStartPos = 0;

		/// <summary>
		/// Indicates if current command is the first one to be executed
		/// </summary>
		private bool firstCmd = true;

    private bool multiLineMode = false;

    #endregion

    #region Constructor

    /// <summary>
		/// Hide default constructor
		/// </summary>
		private GrtPromptForm()
		{
			InitializeComponent();
    }

    #endregion

    #region Public functions

    /// <summary>
		/// Constructor taking the GRT Manager as parameter
		/// </summary>
		/// <param name="GrtManager">The GRT Manager of the application</param>
		public GrtPromptForm(GrtManager GrtManager)
			: this()
		{
			grtManager = GrtManager;
			grtShell = GrtManager.get_shell();

			ActiveControl = outputTextBox;
		}

		/// <summary>
		/// Prints text output to the shell
		/// </summary>
		/// <param name="text">Text to print</param>
		public void PrintText(String text)
		{
			outputTextBox.AppendText(text);

      ScrollToBottom();
    }

    /// <summary>
    /// Scrolls the output text to the very bottom and sets the focus back to the input text box
    /// </summary>
    public void ScrollToBottom()
    {
      outputTextBox.Focus();
      outputTextBox.Select(outputTextBox.Text.Length, 0);
      outputTextBox.ScrollToCaret();

      inputTextBox.Focus();
    }

    /// <summary>
    /// Sets the current text selection to the given text
    /// </summary>
    /// <param name="text">The text to set</param>
    public void SetCurrentSelectionText(string text)
    {
      if (shellTabControl.SelectedTab == promptTabPage)
      {
        if (isSelectionInCommand() == true)
          inputTextBox.SelectedText = text;

        inputTextBox.Focus();
      }
      else if (shellTabControl.SelectedTab == snippetsTabPage)
      {
        snippetsTextBox.SelectedText = text;
        snippetsTextBox.Focus();
      }
    }

    /// <summary>
    /// Prepares the shell for the next command by printing the current prompt and setting current command variables
    /// </summary>
    /// <param name="prompt">The current prompt</param>
    public void PrepareNextCommand(string prompt)
    {
      if (firstCmd)
      {
        // turn the history on
        grtShell.set_saves_history(true);
        firstCmd = false;
      }

      promptLabel.Text = prompt.TrimEnd();
      inputTextBox.Focus();
    }

    /// <summary>
    /// Stores the snippets in the user data dir
    /// </summary>
    public void StoreSnippets()
    {
      grtShell.set_snippet_data(snippetsTextBox.Text);
    }

    #endregion

    #region Properties

    public bool MultiLineMode
    {
      get { return multiLineMode; }
      set
      {
        if (multiLineMode != value)
        {
          multiLineMode = value;

          if (multiLineMode)
          {
            promptSplitContainer.IsSplitterFixed = false;

            inputTextBox.Multiline = true;
            promptSplitContainer.SplitterDistance =
              promptSplitContainer.Height - 60;

            ScrollToBottom();
          }
          else
          {
            promptSplitContainer.IsSplitterFixed = true;

            promptSplitContainer.SplitterDistance =
              promptSplitContainer.Height - 23;
            inputTextBox.Multiline = false;            
          }
        }
      }
    }

    #endregion

    #region Form handling

    /// <summary>
		/// Handles the KeyDown events
		/// </summary>
		/// <param name="sender">The object triggering the event</param>
		/// <param name="e">the event parameters</param>
		/*private void promptTextEditor_KeyDown(object sender, KeyEventArgs e)
		{
			// Handle Return or Enter, execute current command
			if ((e.KeyCode == Keys.Return || e.KeyCode == Keys.Enter) && !e.Shift)
			{
				string cmd = getCmd();

				if (cmd.Trim().CompareTo("cls") == 0)
				{
					cmdStartLine = 1;
					outputTextBox.Text = currentPrompt;
					outputTextBox.SelectionStart = outputTextBox.Text.Length;
				}
				else
				{
					// Set cursor to next line
					outputTextBox.AppendText("\n");

					// Process the command the user entered
					grtShell.process_line_async(cmd);

					// Disable the promptTextEditor so the user cannot type in anything
					outputTextBox.ReadOnly = true;
				}
				e.Handled = true;
			}
			// Handle Shift + Enter, add a new line
			else if ((e.KeyCode == Keys.Return || e.KeyCode == Keys.Enter) && e.Shift)
			{
				outputTextBox.AppendText("\n" + nextLinePrompt);

				e.Handled = true;
			}
			// Handle Up, go to previous command in history
			else if (e.KeyCode == Keys.Up && e.Control)
			{
				String cmd;

				if (grtShell.previous_history_line(getCmd(), out cmd))
					setCmd(cmd);

				e.Handled = true;
			}
			// Handle Ctrl, go to next command in history
      else if (e.KeyCode == Keys.Down && e.Control)
			{
				String cmd;

				if (grtShell.next_history_line(out cmd))
					setCmd(cmd);

				e.Handled = true;
			}
			// Handle Escape key, clear current cmd
			else if (e.KeyCode == Keys.Escape)
			{
				setCmd("");
			}
			// Handle the Back key, prevent the deletion of the prompt
			else if (e.KeyCode == Keys.Back)
			{
				int index = outputTextBox.SelectionStart;
				int line = outputTextBox.GetLineFromCharIndex(index) + 1;


        if (line < cmdStartLine)
					e.Handled = true;
        else if (line >= cmdStartLine)
				{
					Point pt = outputTextBox.GetPositionFromCharIndex(index);
					pt.X = 0;
					int col = index - outputTextBox.GetCharIndexFromPosition(pt);

          if (col <= currentPromptLength)
						e.Handled = true;
				}
			}
			// Handle the Delete key, prevent the deletion of the next lines
			else if (e.KeyCode == Keys.Delete)
			{
				int index = outputTextBox.SelectionStart;
				int line = outputTextBox.GetLineFromCharIndex(index) + 1;

				if (line < cmdStartLine)
					e.Handled = true;
				else if (line >= cmdStartLine && line < outputTextBox.Lines.Length)
				{
					Point pt = outputTextBox.GetPositionFromCharIndex(index);
					pt.X = 0;
					int col = index - outputTextBox.GetCharIndexFromPosition(pt);

					if (col >= outputTextBox.Lines[line].Length)
						e.Handled = true;
				}
			}
			// Handle the Home key
			else if (e.KeyCode == Keys.Home)
			{
				int line = outputTextBox.GetLineFromCharIndex(outputTextBox.SelectionStart) + 1;

				if (line >= cmdStartLine)
				{
					outputTextBox.SelectionStart = currentCmdStartPos;
					e.Handled = true;
				}
			}
			// Handle Ctrl + Shift + S, paste the contents of the snippets tabsheet
			else if (e.KeyCode == Keys.S && e.Shift && e.Control)
			{
				setCmd(snippetsTextBox.Text.Trim());
				e.Handled = true;
			}

			// if any other keys than navigation keys (+ shift) or Ctrl + C is pressed, set cursor to end of current cmd
			if (e.KeyCode != Keys.Left && e.KeyCode != Keys.Right && e.KeyCode != Keys.Up && e.KeyCode != Keys.Down &&
				e.KeyCode != Keys.ShiftKey && e.KeyCode != Keys.ControlKey && e.KeyCode != Keys.End &&
				!(e.KeyCode == Keys.C && e.Control))
			{
				int index = outputTextBox.SelectionStart;
				int line = outputTextBox.GetLineFromCharIndex(index) + 1;

				// if the user presses a key and is above the current cmd, make sure to set the 
				// cursor to the end of the current doc
				if (line < cmdStartLine)
					outputTextBox.SelectionStart = outputTextBox.Text.Length;
				else if (line >= cmdStartLine)
				{
					Point pt = outputTextBox.GetPositionFromCharIndex(index);
					pt.X = 0;
					int col = index - outputTextBox.GetCharIndexFromPosition(pt);

					if (col < currentPromptLength)
						outputTextBox.SelectionStart = outputTextBox.Text.Length;
				}
			}

			// Handle Ctrl + V
			if (e.KeyCode == Keys.V && e.Control)
			{
				string cmd = Clipboard.GetText();

				cmd = cmd.Replace("\n", "\n" + nextLinePrompt);

				outputTextBox.SelectedText = cmd;

				e.Handled = true;
			}
			// Handle Ctrl + Shift + C
			else if (e.KeyCode == Keys.C && e.Control && e.Shift)
			{
				Clipboard.SetText(getCmd());

				e.Handled = true;
			}
		}*/

		/// <summary>
		/// Checks is the current selection is inside the current command
		/// </summary>
		/// <returns>true if selection is inside the current command</returns>
		private bool isSelectionInCommand()
		{
			return true;
		}

		/// <summary>
		/// Function to set the current command
		/// </summary>
		/// <param name="cmd">Command to set</param>
		private void setCmd(string cmd)
		{
      string[] lines = cmd.Split('\n');

      MultiLineMode = (lines.Length > 1);

      inputTextBox.Text = cmd;
      inputTextBox.SelectionStart = inputTextBox.Text.Length;
      inputTextBox.SelectionLength = 0;
		}

		/// <summary>
		/// Setting the focus to the editors when the user switches between the tabsheets
		/// </summary>
		/// <param name="sender">Object triggering the event</param>
		/// <param name="e">The event parameters</param>
		private void shellTabControl_SelectedIndexChanged(object sender, EventArgs e)
		{
			ActivateShell();
		}

		public void ActivateShell()
		{
			if (shellTabControl.SelectedTab == promptTabPage)
        ActiveControl = inputTextBox;
			else if (shellTabControl.SelectedTab == snippetsTabPage)
				ActiveControl = snippetsTextBox;
		}

		/// <summary>
		/// Global KeyDown events captured to enable the switching of the tabsheets by keys
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void GrtPromptForm_KeyDown(object sender, KeyEventArgs e)
		{
			// Handle Ctrl + Shift + Right
			if (e.KeyCode == Keys.Right && e.Control && shellTabControl.SelectedIndex < shellTabControl.TabCount)
			{
				shellTabControl.SelectedIndex += 1;
				e.Handled = true;
			}
			// Handle Ctrl + Shift + Left
			else if (e.KeyCode == Keys.Left && e.Control && shellTabControl.SelectedIndex > 0)
			{
				shellTabControl.SelectedIndex -= 1;
				e.Handled = true;
			}
    }

    /// <summary>
    /// Restores the snippets from the snippets file in the user data dir
    /// </summary>
    public void RestoreSnippets()
    {
      snippetsTextBox.Text = grtShell.get_snippet_data();
    }

    private void inputTextBox_DragEnter(object sender, DragEventArgs e)
    {
      bool dragObjectIsMyClass =
                (e.Data.GetDataPresent(typeof(String)) == true);
      if (dragObjectIsMyClass)
        e.Effect = DragDropEffects.Copy;
    }

    private void inputTextBox_DragDrop(object sender, DragEventArgs e)
    {
      String val = (String)e.Data.GetData(typeof(String));

      SetCurrentSelectionText(val);
    }

    #endregion

    private void inputTextBox_KeyDown(object sender, KeyEventArgs e)
    {
      // Handle Return or Enter, execute current command
      if ((e.KeyCode == Keys.Return || e.KeyCode == Keys.Enter) && !e.Shift 
        && (!MultiLineMode || e.Control))
      {
        if (inputTextBox.Text.Trim().CompareTo("cls") == 0)
        {
          outputTextBox.Text = "";
          inputTextBox.Text = "";
        }
        else
        {
          // Print cmd to output text box
          for (int i = 0; i < inputTextBox.Lines.Length; i++)
          {
            if (i == 0)
              PrintText(promptLabel.Text + " " + inputTextBox.Lines[i] 
                + Environment.NewLine);
            else
              PrintText(new String(' ', promptLabel.Text.Length - 1) + "> "
                + inputTextBox.Lines[i]
                + Environment.NewLine);
          }

          // Process the command the user entered
          grtShell.process_line_async(inputTextBox.Text);

          inputTextBox.Text = "";

          MultiLineMode = false;
        }
        e.Handled = true;
        e.SuppressKeyPress = true;
      }
      // Handle Ctrl + Up / Shift + Return to switch to multiline editing
      else if ((e.KeyCode == Keys.Up && e.Control && !e.Shift) || (e.KeyCode == Keys.Return && e.Shift))
      {
        MultiLineMode = true;

        inputTextBox.SelectedText = Environment.NewLine;

        e.Handled = true;
        e.SuppressKeyPress = true;
      }
      // Handle Ctrl + Down to switch to single line editing
      else if (e.KeyCode == Keys.Down && e.Control && !e.Shift)
      {
        MultiLineMode = false;

        e.Handled = true;
        e.SuppressKeyPress = true;
      }
      // Handle Up, go to previous command in history
      else if (e.KeyCode == Keys.Up)
      {
        if ((e.Control && e.Shift) || !MultiLineMode 
          || (inputTextBox.Lines.Length > 0 
            && inputTextBox.SelectionStart <= inputTextBox.Lines[0].Length))
        {
          String cmd;

          if (grtShell.previous_history_line(inputTextBox.Text, out cmd))
            setCmd(cmd);

          e.Handled = true;
        }
      }
      // Handle Ctrl, go to next command in history
      else if (e.KeyCode == Keys.Down)
      {
        if ((e.Control && e.Shift) || !MultiLineMode 
          || (inputTextBox.Lines.Length >= 1 &&
          inputTextBox.SelectionStart >= inputTextBox.Text.Length - inputTextBox.Lines[inputTextBox.Lines.Length - 1].Length))
        {
          String cmd;

          if (grtShell.next_history_line(out cmd))
            setCmd(cmd);

          e.Handled = true;
        }
      }
      // Handle Escape key, clear current cmd
      else if (e.KeyCode == Keys.Escape)
      {
        setCmd("");
        grtShell.reset_history_position();
      }
    }
	}
}