using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using APE.model;
using System.Collections.ObjectModel;

namespace APE.presentation {
	public partial class List : ListView {
		ViewingModes modes;
		StackPanel panel = new StackPanel();

		public enum ViewingModes {
			Tiles, List, Details
		}

		#region Back Routed Event

		public static readonly RoutedEvent BackEvent = EventManager.RegisterRoutedEvent(
			"Back",
			RoutingStrategy.Bubble,
			typeof(RoutedEventHandler),
			typeof(List));

		public event RoutedEventHandler Back {
			add { AddHandler(BackEvent, value); }
			remove { RemoveHandler(BackEvent, value); }
		}

		private void RaiseBackEvent() {
			RoutedEventArgs newEventArgs = new RoutedEventArgs(List.BackEvent);
			RaiseEvent(newEventArgs);
		}

		#endregion

		#region CopyMove Routed Event

		public static readonly RoutedEvent CopyMoveEvent = EventManager.RegisterRoutedEvent(
			"Copy",
			RoutingStrategy.Bubble,
			typeof(RoutedEventHandler),
			typeof(List));

		public event RoutedEventHandler CopyMove {
			add { AddHandler(CopyMoveEvent, value); }
			remove { RemoveHandler(CopyMoveEvent, value); }
		}

		public class CopyMoveEventArgs : RoutedEventArgs {
			public FSClipboard clippy;

			public CopyMoveEventArgs(FSClipboard clippy_in) {
				clippy = clippy_in;
				RoutedEvent = List.CopyMoveEvent;
			}
		}

		private void RaiseCopyMoveEvent(List<AbstractNode> nodes_in, bool copy) {
			var operation = copy ? DragDropEffects.Copy : DragDropEffects.Move;
			var clippy = new FSClipboard(nodes_in, operation);

			CopyMoveEventArgs newEventArgs = new CopyMoveEventArgs(clippy);
			RaiseEvent(newEventArgs);
		}

		#endregion

		#region Paste Routed Event

		public static readonly RoutedEvent PasteEvent = EventManager.RegisterRoutedEvent(
			"Paste",
			RoutingStrategy.Bubble,
			typeof(RoutedEventHandler),
			typeof(List));

		public event RoutedEventHandler Paste {
			add { AddHandler(PasteEvent, value); }
			remove { RemoveHandler(PasteEvent, value); }
		}

		public class PasteEventArgs : RoutedEventArgs {
			public AbstractNode DestinationNode { get; set; }

			public PasteEventArgs() {
				RoutedEvent = List.PasteEvent;
			}
		}

		private void RaisePasteEvent() {
			RoutedEventArgs newEventArgs = new PasteEventArgs();
			RaiseEvent(newEventArgs);
		}

		#endregion

		#region ClearClipboard Routed Event

		public static readonly RoutedEvent ClearClipboardEvent = EventManager.RegisterRoutedEvent(
			"ClearClipboard",
			RoutingStrategy.Bubble,
			typeof(RoutedEventHandler),
			typeof(MainWindow));

		public event RoutedEventHandler ClearClipboard {
			add { AddHandler(ClearClipboardEvent, value); }
			remove { RemoveHandler(ClearClipboardEvent, value); }
		}

		public class ClearClipboardEventArgs : RoutedEventArgs {
			public ClearClipboardEventArgs() {
				RoutedEvent = List.ClearClipboardEvent;
			}
		}

		private void RaiseClearClipboardEvent() {
			RoutedEventArgs newEventArgs = new RoutedEventArgs(List.ClearClipboardEvent);
			RaiseEvent(newEventArgs);
		}

		#endregion

		#region Refresh Routed Event

		public static readonly RoutedEvent RefreshEvent = EventManager.RegisterRoutedEvent(
			"Refresh",
			RoutingStrategy.Bubble,
			typeof(RoutedEventHandler),
			typeof(List));

		public event RoutedEventHandler Refresh {
			add { AddHandler(RefreshEvent, value); }
			remove { RemoveHandler(RefreshEvent, value); }
		}

		public class RefreshEventArgs : RoutedEventArgs {
			public RefreshEventArgs() {
				RoutedEvent = List.RefreshEvent;
			}
		}

		private void RaiseRefreshEvent() {
			RoutedEventArgs newEventArgs = new RoutedEventArgs(List.RefreshEvent);
			RaiseEvent(newEventArgs);
		}

		#endregion

		public List() {
			InitializeComponent();
			//View = new GridView();
			//View = new RowView();
			View = FindResource("TileView") as ViewBase;
			modes = ViewingModes.Tiles;
			MouseEnter += mouseEnter;
			MouseDown += mouseDown;
			KeyDown += keyDown;
		}

		#region Event Handlers

		private void keyDown(object sender, KeyEventArgs e) {
			if (Keyboard.IsKeyDown(Key.F4)) { // Alt + F4 has to be sent for further processing 
				return;
			}
			if (Keyboard.IsKeyDown(Key.LeftAlt)) {
				e.Handled = true;
				return;
			}
			if (e.Key == Key.Escape) {
				unselectItems(Items);
			} else if (e.Key == Key.Tab || e.Key == Key.Back) {
				RaiseBackEvent();
			} else if ((e.Key == Key.C || e.Key == Key.X) && Keyboard.IsKeyDown(Key.LeftCtrl)) {
				bool copy = e.Key == Key.C;
				bool areHardSelected = existHardSelectedItems(Items);

				unmarkItems(Items);
				markItems(Items, copy, areHardSelected);
				var nodes = chooseMarkedNodes(Items);

				putFilesIntoClipboard(nodes, copy);
				RaiseCopyMoveEvent(nodes, copy);
			} else if (e.Key == Key.V && Keyboard.IsKeyDown(Key.LeftCtrl)) {
				if (existMarkedItems(Items)) {
					unmarkItems(Items);
					RaiseClearClipboardEvent();
					return;
				}
				RaisePasteEvent();
			} else if (e.Key == Key.Delete) {
				bool areHardSelected = existHardSelectedItems(Items);
				foreach (var node in chooseSelectedNodes(Items, areHardSelected)) {
					node.accept(new FSDeleteVisitor());
				}
				RaiseRefreshEvent();
			}
		}

		/// <summary>
		/// Following algorithm is somewhat remarkable. It does process of range selection (selection with Shift) as human being should expect it. When there are selected nodes in List, algorithm selects all nodes between the nearest selected and current one. If there are selected nodes before and after the current one, algorithm selects all nodes between nearest predecessor and successor. 
		/// </summary>
		private void OnRangeSelected(object sender, RoutedEventArgs e) {
			var args = e as ListItem.RangeSelectedEventArgs;
			int lastPredecessorSelectedIndex = int.MinValue;
			int firstSuccessorSelectedIndex = int.MaxValue;
			int currentSelectedIndex = Items.IndexOf(args.item);
			for (int i = 0; i < currentSelectedIndex; i++) {
				if ((Items[i] as ListItem).IsHardSelected) {
					lastPredecessorSelectedIndex = i;
				}
			}
			for (int i = Items.Count - 1; i > currentSelectedIndex; i--) {
				if ((Items[i] as ListItem).IsHardSelected) {
					firstSuccessorSelectedIndex = i;
				}
			}
			if (lastPredecessorSelectedIndex > int.MinValue) {
				for (int i = lastPredecessorSelectedIndex; i <= currentSelectedIndex; i++) {
					(Items[i] as ListItem).IsHardSelected = true;
				}
			} 
			if (firstSuccessorSelectedIndex < int.MaxValue) {
				for (int i = firstSuccessorSelectedIndex; i >= currentSelectedIndex; i--) {
					(Items[i] as ListItem).IsHardSelected = true;
				}
			}
		}

		private void mouseDown(object sender, RoutedEventArgs e) {
			unselectItems(Items);
		}

		private void mouseEnter(object sender, RoutedEventArgs e) {
			Focus();
		}

		#endregion

		#region Utilities

		private bool existHardSelectedItems(ItemCollection col) {
			foreach (ListItem i in col) {
				if (i.IsHardSelected) {
					return true;
				}
			}
			return false;
		}

		private bool existMarkedItems(ItemCollection col) {
			foreach (ListItem i in col) {
				if (i.MarkedForCopy || i.MarkedForMove) {
					return true;
				}
			}
			return false;
		}

		private void unselectItems(ItemCollection col) {
			foreach (ListItem i in col) {
				i.IsHardSelected = false;
			}
		}

		private void unmarkItems(ItemCollection col) {
			foreach (ListItem i in col) {
				i.MarkedForCopy = i.MarkedForMove = false;
			}
		}

		private List<AbstractNode> chooseSelectedNodes(ItemCollection col, bool areHardSelected) {
			var result = new List<AbstractNode>();
			foreach (ListItem item in col) {
				if (!item.IsHardSelected && !item.IsSelected) { // are there any selected nodes at all?
					continue;
				}
				if (areHardSelected && item.IsSelected && !item.IsHardSelected) { // filter out soft-only selected items
					continue;
				}
				var node = item.node;
				if (node is FileNode || node is DirectoryNode) {
					result.Add(node);
					item.IsHardSelected = false;
				}
			}
			return result;
		}

		private void markItems(ItemCollection col, bool copy, bool areHardSelected) {
			foreach (ListItem item in col) {
				if (!item.IsHardSelected && !item.IsSelected) {
					continue;
				}
				if (item.IsSelected && areHardSelected && !item.IsHardSelected) {
					continue;
				}
				if (copy) {
					item.MarkedForCopy = true;
				} else {
					item.MarkedForMove = true;
				}
			}
		}

		private List<AbstractNode> chooseMarkedNodes(ItemCollection col) {
			var result = new List<AbstractNode>();
			foreach (ListItem item in col) {
				var node = item.node;
				if ((item.MarkedForCopy || item.MarkedForMove) && (node is FileNode || node is DirectoryNode)) {
					result.Add(node);
				} 
			}
			return result;
		}

		private void putFilesIntoClipboard(List<AbstractNode> items_in, bool copy) {
			if (items_in.Count == 0) {
				return;
			}
			var fileNames = new List<string>();
			foreach (AbstractNode node in items_in) {
				fileNames.Add(node.getFullPath());
			}

			var obj = new DataObject();
			// 1. file list
			var files = new System.Collections.Specialized.StringCollection();
			files.AddRange(fileNames.ToArray());
			obj.SetFileDropList(files);
			// 2. operation types
			var strm = new System.IO.MemoryStream();
			strm.WriteByte(copy ? (byte)DragDropEffects.Copy : (byte)DragDropEffects.Move);
			obj.SetData("Preferred Dropeffect", strm);

			Clipboard.SetDataObject(obj);
		}

		#endregion
	}
}
