using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Documents;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Data;
using System.ComponentModel;

namespace Microsoft.Research.DynamicDataDisplay.Charts
{
	public static class LiveToolTipService
	{

		# region Properties

		public static object GetToolTip(DependencyObject obj)
		{
			return (object)obj.GetValue(ToolTipProperty);
		}

		public static void SetToolTip(DependencyObject obj, object value)
		{
			obj.SetValue(ToolTipProperty, value);
		}

		public static readonly DependencyProperty ToolTipProperty = DependencyProperty.RegisterAttached(
		  "ToolTip",
		  typeof(object),
		  typeof(LiveToolTipService),
		  new FrameworkPropertyMetadata(null, OnToolTipChanged));

		private static LiveToolTip GetLiveToolTip(DependencyObject obj)
		{
			return (LiveToolTip)obj.GetValue(LiveToolTipProperty);
		}

		private static void SetLiveToolTip(DependencyObject obj, LiveToolTip value)
		{
			obj.SetValue(LiveToolTipProperty, value);
		}

		private static readonly DependencyProperty LiveToolTipProperty = DependencyProperty.RegisterAttached(
		  "LiveToolTip",
		  typeof(LiveToolTip),
		  typeof(LiveToolTipService),
		  new FrameworkPropertyMetadata(null));

		#region Opacity

		public static double GetTooltipOpacity(DependencyObject obj)
		{
			return (double)obj.GetValue(TooltipOpacityProperty);
		}

		public static void SetTooltipOpacity(DependencyObject obj, double value)
		{
			obj.SetValue(TooltipOpacityProperty, value);
		}

		public static readonly DependencyProperty TooltipOpacityProperty = DependencyProperty.RegisterAttached(
		  "TooltipOpacity",
		  typeof(double),
		  typeof(LiveToolTipService),
		  new FrameworkPropertyMetadata(1.0, OnTooltipOpacityChanged));

		private static void OnTooltipOpacityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			LiveToolTip liveTooltip = GetLiveToolTip(d);
			if (liveTooltip != null)
			{
				liveTooltip.Opacity = (double)e.NewValue;
			}
		}

		#endregion // end of Opacity

		#region IsPropertyProxy property

		public static bool GetIsPropertyProxy(DependencyObject obj)
		{
			return (bool)obj.GetValue(IsPropertyProxyProperty);
		}

		public static void SetIsPropertyProxy(DependencyObject obj, bool value)
		{
			obj.SetValue(IsPropertyProxyProperty, value);
		}

		public static readonly DependencyProperty IsPropertyProxyProperty = DependencyProperty.RegisterAttached(
		  "IsPropertyProxy",
		  typeof(bool),
		  typeof(LiveToolTipService),
		  new FrameworkPropertyMetadata(false));

		#endregion // end of IsPropertyProxy property

		#endregion

		private static void OnToolTipChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			FrameworkElement source = (FrameworkElement)d;

			if (e.NewValue == null)
			{
				source.Loaded -= source_Loaded;
				source.ClearValue(LiveToolTipProperty);
			}

			if (GetIsPropertyProxy(source)) return;

			var content = e.NewValue;

			DataTemplate template = content as DataTemplate;
			if (template != null)
			{
				content = template.LoadContent();
			}

			LiveToolTip tooltip = null;
			if (e.NewValue is LiveToolTip)
			{
				tooltip = e.NewValue as LiveToolTip;
			}
			else
			{
				tooltip = new LiveToolTip { Content = content };
			}

			if (tooltip == null && e.OldValue == null)
			{
				tooltip = new LiveToolTip { Content = content };
			}

			if (tooltip != null)
			{
				SetLiveToolTip(source, tooltip);
				if (!source.IsLoaded)
				{
					source.Loaded += source_Loaded;
				}
				else
				{
					AddTooltip(source);
				}
			}
		}

		private static void AddTooltipForElement(FrameworkElement source, LiveToolTip tooltip)
		{
			AdornerLayer layer = AdornerLayer.GetAdornerLayer(source);

			LiveToolTipAdorner adorner = new LiveToolTipAdorner(source, tooltip);
			layer.Add(adorner);
		}

		private static void source_Loaded(object sender, RoutedEventArgs e)
		{
			FrameworkElement source = (FrameworkElement)sender;

			if (source.IsLoaded)
			{
				AddTooltip(source);
			}
		}

		private static void AddTooltip(FrameworkElement source)
		{
			if (DesignerProperties.GetIsInDesignMode(source)) return;

			LiveToolTip tooltip = GetLiveToolTip(source);

			Window window = Window.GetWindow(source);
			FrameworkElement child = source;
			FrameworkElement parent = null;
			if (window != null)
			{
				while (parent != window)
				{
					parent = (FrameworkElement)VisualTreeHelper.GetParent(child);
					child = parent;
					var nameScope = NameScope.GetNameScope(parent);
					if (nameScope != null)
					{
						string nameScopeName = nameScope.ToString();
						if (nameScopeName != "System.Windows.TemplateNameScope")
						{
							NameScope.SetNameScope(tooltip, nameScope);
							break;
						}
					}
				}
			}

			var binding = BindingOperations.GetBinding(tooltip, LiveToolTip.ContentProperty);
			if (binding != null)
			{
				BindingOperations.ClearBinding(tooltip, LiveToolTip.ContentProperty);
				BindingOperations.SetBinding(tooltip, LiveToolTip.ContentProperty, binding);
			}

			Binding dataContextBinding = new Binding { Path = new PropertyPath("DataContext"), Source = source };
			tooltip.SetBinding(LiveToolTip.DataContextProperty, dataContextBinding);

			tooltip.Owner = source;
			if (GetTooltipOpacity(source) != (double)LiveToolTipService.TooltipOpacityProperty.DefaultMetadata.DefaultValue)
			{
				tooltip.Opacity = LiveToolTipService.GetTooltipOpacity(source);
			}

			AddTooltipForElement(source, tooltip);
		}
	}
}
