using System;
using System.Collections.Generic;
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;

namespace APE.presentation {
	/// <summary>
	/// Interaction logic for ListItem.xaml
	/// </summary>
	public partial class ListItem : ListViewItem {
		public AbstractNode node;
		List.ViewingModes mode;

		#region Dependency Properties

		public bool IsHardSelected {
			get { return (bool)GetValue(IsHardSelectedProperty); }
			set { SetValue(IsHardSelectedProperty, value); }
		}

		public static readonly DependencyProperty IsHardSelectedProperty =
			DependencyProperty.Register("IsHardSelected", typeof(bool), typeof(ListItem), new UIPropertyMetadata(false));

		public bool MarkedForCopy {
			get { return (bool)GetValue(MarkedForCopyProperty); }
			set { SetValue(MarkedForCopyProperty, value); }
		}

		public static readonly DependencyProperty MarkedForCopyProperty =
			DependencyProperty.Register("MarkedForCopy", typeof(bool), typeof(ListItem), new UIPropertyMetadata(false));

		public bool MarkedForMove {
			get { return (bool)GetValue(MarkedForMoveProperty); }
			set { SetValue(MarkedForMoveProperty, value); }
		}

		public static readonly DependencyProperty MarkedForMoveProperty =
			DependencyProperty.Register("MarkedForMove", typeof(bool), typeof(ListItem), new UIPropertyMetadata(false));

		#endregion

		#region NodeSelected Routed Event

		public static readonly RoutedEvent NodeSelectedEvent = EventManager.RegisterRoutedEvent(
			"NodeSelected",
			RoutingStrategy.Bubble,
			typeof(RoutedEventHandler),
			typeof(ListItem));

		public event RoutedEventHandler NodeSelected {
			add { AddHandler(NodeSelectedEvent, value); }
			remove { RemoveHandler(NodeSelectedEvent, value); }
		}

		public class NodeSelectedEventArgs : RoutedEventArgs {
			public AbstractNode selectedNode { get; private set; }

			public NodeSelectedEventArgs(AbstractNode node) {
				selectedNode = node;
				RoutedEvent = ListItem.NodeSelectedEvent;
			}
		}

		private void RaiseNodeSelectedEvent() {
			NodeSelectedEventArgs newEventArgs = new NodeSelectedEventArgs(node);
			RaiseEvent(newEventArgs);
		}

		#endregion

		#region RangeSelected Routed Event

		public static readonly RoutedEvent RangeSelectedEvent = EventManager.RegisterRoutedEvent(
			"RangeSelected",
			RoutingStrategy.Bubble,
			typeof(RoutedEventHandler),
			typeof(ListItem));

		public event RoutedEventHandler RangeSelected {
			add { AddHandler(RangeSelectedEvent, value); }
			remove { RemoveHandler(RangeSelectedEvent, value); }
		}

		public class RangeSelectedEventArgs : RoutedEventArgs {
			public ListItem item { get; private set; }

			public RangeSelectedEventArgs(ListItem li) {
				item = li;
				RoutedEvent = ListItem.RangeSelectedEvent;
			}
		}

		private void RaiseRangeSelectedEvent(ListItem li) {
			RoutedEventArgs newEventArgs = new RangeSelectedEventArgs(li);
			RaiseEvent(newEventArgs);
		}

		#endregion
		
		public ListItem(AbstractNode f, List.ViewingModes m) : base() {
			InitializeComponent();
			mode = m;
			node = f;
			
			Content = node;
			changeLook(mode); 

			// mouseDown on FSItem does not work;
			// textbox catches it and sets it as handled;
			// solutions: 
			// 1. catch previewMouseDown event or
			// 2. add handler with special method

			/* 2. */ AddHandler(ListViewItem.MouseDownEvent, new RoutedEventHandler(mouseDown), true);
			// 1. PreviewMouseDown += mouseDown;
			/* 2. */
			AddHandler(ListViewItem.KeyDownEvent, new KeyEventHandler(keyDown), true);
			MouseEnter += mouseEnter;
			MouseLeave += mouseLeave;
			Loaded += loaded;
		}

		void loaded(object sender, RoutedEventArgs e) {
			setCorrectIcon();
		}

		public void changeLook(List.ViewingModes m) {
			mode = m;
			if (m == List.ViewingModes.Tiles) {
				ContentTemplate = FindResource("TileView") as DataTemplate;
			} else if (m == List.ViewingModes.Details) {
				ContentTemplate = FindResource("DetailsView") as DataTemplate;
			} else if (m == List.ViewingModes.List) {
				ContentTemplate = FindResource("ListView") as DataTemplate;
			}
		}

		/// <summary>
		/// FindVisualChild has to be after loading, after InitializeComponent is not enough.
		/// Otherwise FindVisualChild will not work, because the children probably are not
		/// instantiated yet.
		/// </summary>
		private void setCorrectIcon() {
			ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(this);
			DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
			Rectangle iconHolder = (Rectangle)myDataTemplate.FindName("iconHolder", myContentPresenter);

			DrawingBrush iconBrush = null;
			if (node is FileNode) {
				iconBrush = (DrawingBrush)FindResource("fileIcon");
			} else if (node is DirectoryNode) {
				iconBrush = (DrawingBrush)FindResource("folderIcon");
			} else if (node is DriveNode) {
				iconBrush = (DrawingBrush)FindResource("driveIcon");
			}
			iconHolder.Fill = iconBrush;
		}

		private childItem FindVisualChild<childItem>(DependencyObject obj)
			where childItem : DependencyObject {
			for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) {
				DependencyObject child = VisualTreeHelper.GetChild(obj, i);
				if (child != null && child is childItem)
					return (childItem)child;
				else {
					childItem childOfChild = FindVisualChild<childItem>(child);
					if (childOfChild != null)
						return childOfChild;
				}
			}
			return null;
		}

		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;
			}
			if (e.IsRepeat || !IsMouseOver /* Focus stays on another Item while mouse is over void area. In this case we want to prevent toggling selection of focused item. */) {
				return;
			}
			if (Keyboard.IsKeyDown(Key.LeftAlt)) {
				IsHardSelected = !IsHardSelected;
			} else if (e.Key == Key.LeftShift) {
				RaiseRangeSelectedEvent(this);
			}
		}
		
		/// <summary>
		/// In the end, Ctrl serves as marking modifier, while demarking is done either explicitly by or just by coming with mouse over ListItem. This approach has advantage of simpler usage, when deselecting does not demands pressed Ctrl and also, work is not confusing with regard to checkerboard effect: While going with mouse over partially selected fields, their selection status gets toggled. More transparent solution is the one described above, when Ctrl adds to selection, normal mouse hover deselects.
		/// </summary>
		private void mouseEnter(object sender, RoutedEventArgs e) {
			// Bad solution for above mentioned reason.
			//if (Keyboard.IsKeyDown(Key.LeftAlt)) {
			//    IsHardSelected = !IsHardSelected;
			//}
			Focus();
			IsHardSelected = Keyboard.IsKeyDown(Key.LeftAlt);
			IsSelected = true;
		}

		private void mouseLeave(object sender, RoutedEventArgs e) {
			IsSelected = false;
		}

		private void mouseDown(object sender, RoutedEventArgs e) {
			if (node is FileNode) {
				node.accept(new FSExecuteVisitor());
			} else {
				RaiseNodeSelectedEvent();
			}
		}

		private void TextBlock_KeyDown(object sender, KeyEventArgs e) {
			e.Handled = true;
		}
	}
}
