// 
// DeleteActions.cs
// 
// Author:
//   Mike Krüger <mkrueger@novell.com>
//   Michael Hutchinson <mhutchinson@novell.com>
// 
// Copyright (C) 2007-2008 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 System.Collections.Generic;
using System.Diagnostics;
using System.Text;

using Gtk;
using Mono.TextEditor.Highlighting;

namespace Mono.TextEditor
{
	public static class DeleteActions
	{
		public static Action<TextEditorData> FromMoveAction (Action<TextEditorData> moveAction)
		{
			return delegate (TextEditorData data) {
				SelectionActions.StartSelection (data);
				moveAction (data);
				SelectionActions.EndSelection (data);
				data.DeleteSelectedText ();
			};
		}
		
		static void PreviousWord (TextEditorData data, bool subword)
		{
			int oldLine = data.Caret.Line;
			int offset = subword? data.FindPrevSubwordOffset (data.Caret.Offset) : data.FindPrevWordOffset (data.Caret.Offset);
			if (data.Caret.Offset != offset && data.CanEdit (oldLine) && data.CanEdit (data.Caret.Line)) {
				data.Remove (offset, data.Caret.Offset - offset);
				data.Caret.Offset = offset;
				if (oldLine != data.Caret.Line)
					data.Document.CommitLineToEndUpdate (data.Caret.Line);
			}
		}
		
		static void NextWord (TextEditorData data, bool subword)
		{
			int oldLine = data.Caret.Line;
			int offset = subword? data.FindNextSubwordOffset (data.Caret.Offset) : data.FindNextWordOffset (data.Caret.Offset);
			if (data.Caret.Offset != offset && data.CanEdit (oldLine) && data.CanEdit (data.Caret.Line))  {
				data.Remove (data.Caret.Offset, offset - data.Caret.Offset);
				data.Document.CommitLineToEndUpdate (data.Caret.Line);
			}
		}
		
		public static void PreviousWord (TextEditorData data)
		{
			PreviousWord (data, false);
		}
		
		public static void PreviousSubword (TextEditorData data)
		{
			PreviousWord (data, true);
		}
		
		public static void NextWord (TextEditorData data)
		{
			NextWord (data, false);
		}
		
		public static void NextSubword (TextEditorData data)
		{
			NextWord (data, true);
		}
		
		public static void CaretLine (TextEditorData data)
		{
			if (data.Document.LineCount <= 1 || !data.CanEdit (data.Caret.Line))
				return;
			LineSegment line = data.Document.GetLine (data.Caret.Line);
			data.Remove (line.Offset, line.Length);
			data.Document.CommitLineToEndUpdate (data.Caret.Line);
			data.Caret.CheckCaretPosition ();
		}
		
		public static void CaretLineToEnd (TextEditorData data)
		{
			if (!data.CanEdit (data.Caret.Line))
				return;
			LineSegment line = data.Document.GetLine (data.Caret.Line);
			data.EnsureCaretIsNotVirtual ();
			if (data.Caret.Column == line.EditableLength) {
				// Nothing after the cursor, delete the end-of-line sequence
				data.Remove (line.Offset + data.Caret.Column, line.Length - data.Caret.Column);
			} else {
				// Delete from cursor position to the end of the line
				data.Remove (line.Offset + data.Caret.Column, line.EditableLength - data.Caret.Column);
			}
			data.Document.CommitLineUpdate (data.Caret.Line);
		}
		
		public static void Backspace (TextEditorData data)
		{
			Backspace (data, RemoveCharBeforeCaret);
		}
		
		public static void Backspace (TextEditorData data, Action<TextEditorData> removeCharBeforeCaret)
		{
			if (!data.CanEditSelection)
				return;
			if (data.IsSomethingSelected) {
				if (!data.MainSelection.IsDirty) {
					data.DeleteSelectedText (data.MainSelection.SelectionMode != SelectionMode.Block);
				} else {
					bool preserve = data.Caret.PreserveSelection;
					data.Caret.PreserveSelection = true;
					for (int lineNumber = data.MainSelection.MinLine; lineNumber <= data.MainSelection.MaxLine; lineNumber++) {
						data.Remove (data.Document.GetLine (lineNumber).Offset + data.Caret.Column - 1, 1);
					}
					data.Caret.Column--;
					data.MainSelection.Lead = new DocumentLocation (data.MainSelection.Lead.Line, data.Caret.Column);
					data.MainSelection.IsDirty = true;
					data.Caret.PreserveSelection = preserve;
					data.Document.CommitMultipleLineUpdate (data.MainSelection.MinLine, data.MainSelection.MaxLine);
				}
				return;
			}
			if (data.Caret.Offset == 0)
				return;
			LineSegment line = data.Document.GetLine (data.Caret.Line);
			if (data.Caret.Column > line.EditableLength) {
				data.Caret.Column = line.EditableLength;
			} else if (data.Caret.Offset == line.Offset) {
				LineSegment lineAbove = data.Document.GetLine (data.Caret.Line - 1);
				data.Caret.Location = new DocumentLocation (data.Caret.Line - 1, lineAbove.EditableLength);
				data.Remove (lineAbove.EndOffset - lineAbove.DelimiterLength, lineAbove.DelimiterLength);
			} else {
				removeCharBeforeCaret (data);
			}
		}
		
		public static void RemoveCharBeforeCaret (TextEditorData data)
		{
			int offset = data.Caret.Offset;
			if (offset <= 0)
				return;
			data.Remove (offset - 1, 1);
			data.Caret.Column--;
		}
		
		public static void Delete (TextEditorData data)
		{
			if (!data.CanEditSelection)
				return;
			if (data.IsSomethingSelected) {
				data.DeleteSelectedText ();
				return;
			}
			if (data.Caret.Offset >= data.Document.Length)
				return;
			LineSegment line = data.Document.GetLine (data.Caret.Line);
			if (data.Caret.Column == line.EditableLength) {
				if (data.Caret.Line < data.Document.LineCount) { 
					data.Remove (line.EndOffset - line.DelimiterLength, line.DelimiterLength);
					if (line.EndOffset == data.Document.Length)
						line.DelimiterLength = 0;
				}
			} else {
				data.Remove (data.Caret.Offset, 1); 
				data.Document.CommitLineUpdate (data.Caret.Line);
			}
		}
		
		public static void DeleteSelection (TextEditorData data)
		{
			if (!data.CanEditSelection)
				return;
			if (data.IsSomethingSelected) {
				data.DeleteSelectedText ();
				return;
			}
		}
	}
}
