// TextEditor.cs
//
// Author:
//   Lluis Sanchez Gual <lluis@novell.com>
//
// Copyright (c) 2007 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//


using System;
using MonoDevelop.Ide.Gui.Content;
using MonoDevelop.Ide.CodeCompletion;
using Mono.TextEditor.Highlighting;
using System.Collections.Generic;

namespace MonoDevelop.Ide.Gui
{
	public class TextEditor
	{
		IBookmarkBuffer bookmarkBuffer;
		IEditableTextBuffer textBuffer;
		IEncodedTextContent encodedTextContent;
		ICompletionWidget completionWidget;
		IClipboardHandler clipboardHandler;
		string newLine = null;
		// All line and column numbers are 1-based
		
		public static TextEditor GetTextEditor (IBaseViewContent content)
		{
			IEditableTextBuffer tb = (IEditableTextBuffer) content.GetContent (typeof(IEditableTextBuffer));
			if (tb == null)
				return null;
			
			TextEditor ed = new TextEditor ();
			ed.textBuffer = tb;
			ed.bookmarkBuffer = (IBookmarkBuffer) content.GetContent (typeof(IBookmarkBuffer));
			ed.encodedTextContent = (IEncodedTextContent) content.GetContent (typeof(IEncodedTextContent));
			ed.completionWidget = (ICompletionWidget) content.GetContent (typeof(ICompletionWidget));
			ed.clipboardHandler = (IClipboardHandler) content.GetContent (typeof(IClipboardHandler));
			return ed;
		}
		
		public bool HasInputFocus {
			get { return textBuffer.HasInputFocus; }
		}
		
		public bool SupportsBookmarks {
			get { return bookmarkBuffer != null; }
		}
		
		public void SetBookmarked (int position, bool mark)
		{
			if (bookmarkBuffer != null)
				bookmarkBuffer.SetBookmarked (position, mark);
		}
		
		public bool IsBookmarked (int position)
		{
			if (bookmarkBuffer != null)
				return bookmarkBuffer.IsBookmarked (position);
			else
				return false;
		}
		
		public void PrevBookmark ()
		{
			if (bookmarkBuffer != null)
				bookmarkBuffer.PrevBookmark ();
		}
		
		public void NextBookmark ()
		{
			if (bookmarkBuffer != null)
				bookmarkBuffer.NextBookmark ();
		}
		
		public void ClearBookmarks ()
		{
			if (bookmarkBuffer != null)
				bookmarkBuffer.ClearBookmarks ();
		}
		
		public IClipboardHandler ClipboardHandler {
			get { return clipboardHandler; }
		}
		
		public void Undo()
		{
			textBuffer.Undo ();
		}
		
		public void Redo()
		{
			textBuffer.Redo ();
		}
		
		public void BeginAtomicUndo ()
		{
			textBuffer.BeginAtomicUndo ();
		}
		
		public void EndAtomicUndo ()
		{
			textBuffer.EndAtomicUndo ();
		}
		
		public string SelectedText {
			get { return textBuffer.SelectedText; } 
			set { textBuffer.SelectedText = value; }
		}
		
		public event EventHandler<TextChangedEventArgs> TextChanged {
			add { textBuffer.TextChanged += value; }
			remove { textBuffer.TextChanged -= value; }
		}
		
		public event EventHandler CursorPositionChanged {
			add { textBuffer.CaretPositionSet += value; }
			remove { textBuffer.CaretPositionSet -= value; }
		}
		
		public int CursorPosition { 
			get { return textBuffer.CursorPosition; }
			set { textBuffer.CursorPosition = value; }
		}

		public int SelectionStartPosition {
			get { return textBuffer.SelectionStartPosition; }
		}
		
		public int SelectionEndPosition {
			get { return textBuffer.SelectionEndPosition; }
		}
		
		public int CursorLine {
			get {
				int line, column;
				textBuffer.GetLineColumnFromPosition (textBuffer.CursorPosition, out line, out column);
				return line;
			}
			set {
				textBuffer.CursorPosition = textBuffer.GetPositionFromLineColumn (value, CursorColumn);
			}
		}
		
		public int CursorColumn {
			get {
				int line, column;
				textBuffer.GetLineColumnFromPosition (textBuffer.CursorPosition, out line, out column);
				return column;
			}
			set {
				textBuffer.CursorPosition = textBuffer.GetPositionFromLineColumn (CursorLine, value);
			}
		}
		
		public void Select (int startPosition, int endPosition)
		{
			textBuffer.Select (startPosition, endPosition);
		}
		
		public void ShowPosition (int position)
		{
			textBuffer.ShowPosition (position);
		}
		
		public void ShowCursorPosition ()
		{
			textBuffer.ShowPosition (textBuffer.CursorPosition);
		}
		
		public string Text {
			get { return textBuffer.Text; }
		}
		
		public int TextLength {
			get { return textBuffer.Length; }
		}
		
		public string GetText (int startPosition, int endPosition)
		{
			return textBuffer.GetText (startPosition, endPosition);
		}
		
		public char GetCharAt (int position)
		{
			return textBuffer.GetCharAt (position);
		}
		
		public int GetPositionFromLineColumn (int line, int column)
		{
			return textBuffer.GetPositionFromLineColumn (line, column);
		}
		
		public void GetLineColumnFromPosition (int position, out int line, out int column)
		{
			textBuffer.GetLineColumnFromPosition (position, out line, out column);
		}
		
		public int InsertText (int position, string text)
		{
			return textBuffer.InsertText (position, text);
		}
		
		public void DeleteText (int position, int length)
		{
			textBuffer.DeleteText (position, length);
		}
		
		public void DeleteLine (int line)
		{
			int pos1 = GetPositionFromLineColumn (line, 1);
			int pos2 = GetPositionFromLineColumn (line + 1, 1);
			if (pos2 == -1)
				DeleteText (pos1, TextLength - pos1);
			else
				DeleteText (pos1, pos2 - pos1);
		}
		
		public void JumpTo (int line, int col)
		{
			if (textBuffer != null)
				textBuffer.SetCaretTo (line, col);
			else {
				int pos = GetPositionFromLineColumn (line, col);
				CursorPosition = pos;
			}
		}
		
		public string SourceEncoding {
			get {
				if (encodedTextContent != null)
					return encodedTextContent.SourceEncoding;
				else
					return null;
			}
		}
		
		public int LineCount {
			get {
				return textBuffer.LineCount;
			}
		}
		
		public string NewLine
		{
			get {
				if (newLine == null) {
					int pos1 = GetPositionFromLineColumn (1, 1);
					if (pos1 > 1)
						newLine = GetCharAt (pos1 - 1).ToString ();
					if (pos1 > 2) {
						char c1 = GetCharAt (pos1 - 2);
						if (c1 == '\n' || c1 == '\r')
							newLine = c1 + newLine;
					}
					if (newLine == null)
						newLine = Environment.NewLine;
				}
				return newLine;
			}
		}
		
		public int GetLineLength (int line)
		{
			int pos1 = GetPositionFromLineColumn (line, 1);
			int pos2 = GetPositionFromLineColumn (line + 1, 1);
			if (pos2 == -1)
				return TextLength - pos1;
			pos2--;
			while (pos2 >= pos1 && (GetCharAt (pos2) == '\n' || GetCharAt (pos2) == '\r'))
				pos2--;
			return pos2 - pos1 + 1;
		}
		
		public string GetLineText (int line)
		{
			int len = GetLineLength (line);
			int pos = GetPositionFromLineColumn (line, 1);
			return GetText (pos, pos + len);
		}
		
		public void ReplaceLine (int line, string txt)
		{
			int len = GetLineLength (line);
			int pos = GetPositionFromLineColumn (line, 1);
			textBuffer.DeleteText (pos, len);
			textBuffer.InsertText (pos, txt);
		}
		
		public static char GetMatchingBrace (char ch)
		{
			switch (ch) {
			case '(':
				return ')';
			case ')':
				return '(';
			case '{':
				return '}';
			case '}':
				return '{';
			case '[':
				return ']';
			case ']':
				return '[';
			}
			throw new System.ArgumentException (ch.ToString (), "ch");
		}
	
		public CodeCompletionContext CurrentCodeCompletionContext {
			get {
				if (completionWidget == null)
					return null;
				return completionWidget.CreateCodeCompletionContext (this.CursorPosition);
			}
		}
		
		public int SearchChar (int startPos, char searchChar)
		{
			bool isInString = false, isInChar = false, isVerbatimString = false;
			bool isInLineComment  = false, isInBlockComment = false;
			
			for (int pos = startPos; pos < TextLength; pos++) {
				char ch = GetCharAt (pos);
				switch (ch) {
					case '\r':
					case '\n':
						isInLineComment = false;
						if (!isVerbatimString)
							isInString = false;
						break;
					case '/':
						if (isInBlockComment) {
							if (pos > 0 && GetCharAt (pos - 1) == '*') 
								isInBlockComment = false;
						} else  if (!isInString && !isInChar && pos + 1 < TextLength) {
							char nextChar = GetCharAt (pos + 1);
							if (nextChar == '/')
								isInLineComment = true;
							if (!isInLineComment && nextChar == '*')
								isInBlockComment = true;
						}
						break;
					case '\\':
						if (isInChar || (isInString && !isVerbatimString))
							pos++;
						break;
					case '@':
						if (!(isInString || isInChar || isInLineComment || isInBlockComment) && pos + 1 < TextLength && GetCharAt (pos + 1) == '"') {
							isInString = true;
							isVerbatimString = true;
							pos++;
						}
						break;
					case '"':
						if (!(isInChar || isInLineComment || isInBlockComment)) {
							if (isInString && isVerbatimString && pos + 1 < TextLength && GetCharAt (pos + 1) == '"') {
								pos++;
							} else {
								isInString = !isInString;
								isVerbatimString = false;
							}
						}
						break;
					case '\'':
						if (!(isInString || isInLineComment || isInBlockComment)) 
							isInChar = !isInChar;
						break;
					default :
						if (ch == searchChar) {
							if (!(isInString || isInChar || isInLineComment || isInBlockComment))
								return pos;
						}
						break;
				}
			}
			return -1;
		}
		
		public static string[] GetCommentTags (string fileName)
		{
			//Document doc = IdeApp.Workbench.ActiveDocument;
			string loadedMimeType = DesktopService.GetMimeTypeForUri (fileName);
			
			SyntaxMode mode = null;
			foreach (string mt in DesktopService.GetMimeTypeInheritanceChain (loadedMimeType)) {
				mode = SyntaxModeService.GetSyntaxMode (mt);
				if (mode != null)
					break;
			}
			
			if (mode == null)
				return null;
			
			List<string> ctags;
			if (mode.Properties.TryGetValue ("LineComment", out ctags) && ctags.Count > 0) {
				return new string [] { ctags [0] };
			}
			List<string> tags = new List<string> ();
			if (mode.Properties.TryGetValue ("BlockCommentStart", out ctags))
				tags.Add (ctags [0]);
			if (mode.Properties.TryGetValue ("BlockCommentEnd", out ctags))
				tags.Add (ctags [0]);
			if (tags.Count == 2)
				return tags.ToArray ();
			else
				return null;
		}
		
		/*
		public void GotoMatchingBrace ()
		{
			int offset = textBuffer.CursorPosition;
			if (!IsBrace (textBuffer.GetCharAt (offset)))
				offset--;
			
			if (offset >= 0 && IsBrace (textBuffer.GetCharAt (offset))) {
				char open = textBuffer.GetCharAt (offset);
				char close = open;
				bool searchForward = IsOpenBrace (open);
				if (searchForward) {
					close = GetMatchingBrace (open);
					offset = SearchBracketForward (offset + 1, open, close);
				} else {
					open  = GetMatchingBrace (open);
					offset = SearchBracketBackward (offset - 1, open, close);
				}
				if (offset >= 0)
					textBuffer.CursorPosition = offset;
			}
		}*/
	}
}
