using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using agree;
using agree.Wpf.Util;

namespace agree.Wpf
{
	class Main3D : Viewport3D
	{
		List<Tfs> edges;
		Grammar g;
		ContainerUIElement3D cont3;

		const int spacing = 100;

		public Main3D(Grammar g, IEnumerable<Tfs> ie, Grid main_wnd)
		{
			this.InheritanceBehavior = InheritanceBehavior.SkipToThemeNext;

			this.edges = ie.ToList();
			this.g = g;
			this.Width = 1280;
			this.Height = 1024;

			this.ClipToBounds = false;

			cont3 = new ContainerUIElement3D();

			ModelVisual3D alv = new ModelVisual3D();
			alv.Content = new AmbientLight(Colors.White);
			cont3.Children.Add(alv);

			Random rnd = new Random();

			Size max = new Size(0, 0);

			double z = 0;
			foreach (Tfs e in ie)
			{
				//				TfsControl tc = new TfsControl(g, e);
				TfsControl tc = new TfsControl();
				tc._TfsEdge = edges[0];

				tc.FontFamily = new FontFamily("Arial");

				tc.Background = new SolidColorBrush(Color.FromArgb(0x40, (byte)rnd.Next(), (byte)rnd.Next(), (byte)rnd.Next()));

				tc.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
				Size sz = tc.DesiredSize;

				Debug.WriteLine(sz.Width);
				Debug.WriteLine(sz.Height);

				max.Width = Math.Max(max.Width, sz.Width);
				max.Height = Math.Max(max.Height, sz.Height);

				MeshGeometry3D g3 = new MeshGeometry3D();
				g3.Positions.Add(new Point3D(0, -sz.Height, z));	// 0
				g3.Positions.Add(new Point3D(sz.Width, -sz.Height, z));	// 1
				g3.Positions.Add(new Point3D(0, 0, z));	// 2
				g3.Positions.Add(new Point3D(sz.Width, 0, z));	// 3

				g3.Normals.Add(new Vector3D(0, -sz.Height, 1));
				g3.Normals.Add(new Vector3D(sz.Width, -sz.Height, 1));
				g3.Normals.Add(new Vector3D(0, 0, 1));
				g3.Normals.Add(new Vector3D(sz.Width, 0, 1));

				g3.TriangleIndices.Add(0);
				g3.TriangleIndices.Add(1);
				g3.TriangleIndices.Add(2);
				g3.TriangleIndices.Add(2);
				g3.TriangleIndices.Add(1);
				g3.TriangleIndices.Add(3);

				g3.TextureCoordinates.Add(new Point(0, 1));
				g3.TextureCoordinates.Add(new Point(1, 1));
				g3.TextureCoordinates.Add(new Point(0, 0));
				g3.TextureCoordinates.Add(new Point(1, 0));


				DiffuseMaterial dm = new DiffuseMaterial();
				dm.SetValue(Viewport2DVisual3D.IsVisualHostMaterialProperty, true);

				Viewport2DVisual3D me3 = new Viewport2DVisual3D();
				me3.Visual = tc;
				me3.Geometry = g3;
				me3.Material = dm;

				// Depth of Diffuse materials trumps transparency. They must be sorted by depth to achieve a transparent effect.
				cont3.Children.Insert(0, me3);

				z -= spacing;
			}
			double stack_thick = -z;

			Point3D interest = new Point3D(max.Width / 2, 0, -stack_thick * .8);

			PerspectiveCamera cam = new PerspectiveCamera();
			cam.FieldOfView = 35;
			cam.Position = new Point3D(max.Width * .2, -max.Height * 1.5, stack_thick * .2);
			cam.LookDirection = new Vector3D(interest.X - cam.Position.X, interest.Y - cam.Position.Y, interest.Z - cam.Position.Z);
			cam.UpDirection = new Vector3D(0, 0, 1);

			this.Camera = cam;
			this.Children.Add(cont3);

		}
	};

	class Zoom3D : Viewport3D
	{
		public static readonly RoutedCommand ZoomInCommand;
		public static readonly RoutedCommand ZoomOutCommand;
		public static readonly RoutedCommand PanLeftCommand;
		public static readonly RoutedCommand PanRightCommand;
		public static readonly RoutedCommand PanUpCommand;
		public static readonly RoutedCommand PanDownCommand;

		public static readonly RoutedCommand LookUpCommand;
		public static readonly RoutedCommand LookDownCommand;
		public static readonly RoutedCommand LookLeftCommand;
		public static readonly RoutedCommand LookRightCommand;


		static Zoom3D()
		{
			ZoomInCommand = new RoutedCommand("ZoomIn", typeof(Zoom3D));
			ZoomOutCommand = new RoutedCommand("ZoomOut", typeof(Zoom3D));
			PanLeftCommand = new RoutedCommand("PanLeft", typeof(Zoom3D));
			PanRightCommand = new RoutedCommand("PanRight", typeof(Zoom3D));
			PanUpCommand = new RoutedCommand("PanUp", typeof(Zoom3D));
			PanDownCommand = new RoutedCommand("PanDown", typeof(Zoom3D));

			LookUpCommand = new RoutedCommand("LookUp", typeof(Zoom3D));
			LookDownCommand = new RoutedCommand("LookDown", typeof(Zoom3D));
			LookLeftCommand = new RoutedCommand("LookLeft", typeof(Zoom3D));
			LookRightCommand = new RoutedCommand("LookRight", typeof(Zoom3D));
			//LookClockwiseCommand = new RoutedCommand("LookUp", typeof(Zoom3D));
			//LookCommand = new RoutedCommand("LookDown", typeof(Zoom3D));

			ZoomInCommand.InputGestures.Add(new KeyGesture(Key.OemPlus));
			ZoomInCommand.InputGestures.Add(new KeyGesture(Key.Up, ModifierKeys.Control));

			ZoomOutCommand.InputGestures.Add(new KeyGesture(Key.OemMinus));
			ZoomOutCommand.InputGestures.Add(new KeyGesture(Key.Down, ModifierKeys.Control));

			PanLeftCommand.InputGestures.Add(new KeyGesture(Key.Left));
			PanRightCommand.InputGestures.Add(new KeyGesture(Key.Right));
			PanUpCommand.InputGestures.Add(new KeyGesture(Key.Up));
			PanDownCommand.InputGestures.Add(new KeyGesture(Key.Down));

			LookUpCommand.InputGestures.Add(new KeyGesture(Key.Up, ModifierKeys.Alt));
			LookDownCommand.InputGestures.Add(new KeyGesture(Key.Down, ModifierKeys.Alt));
			LookLeftCommand.InputGestures.Add(new KeyGesture(Key.Left, ModifierKeys.Alt));
			LookRightCommand.InputGestures.Add(new KeyGesture(Key.Right, ModifierKeys.Alt));

		}


		//List<Edge> edges;
		Grammar g;
		ContainerUIElement3D cont3;
		Window main_wnd;

		const int spacing = 100;

		public Zoom3D(Grammar g, Tfs ie, Window main_wnd)
		{
			this.main_wnd = main_wnd;
			this.CommandBindings.Add(new CommandBinding(ZoomInCommand, this.HandleZoomIn));
			this.CommandBindings.Add(new CommandBinding(ZoomOutCommand, this.HandleZoomOut));
			this.CommandBindings.Add(new CommandBinding(PanLeftCommand, this.HandlePanLeft));
			this.CommandBindings.Add(new CommandBinding(PanRightCommand, this.HandlePanRight));
			this.CommandBindings.Add(new CommandBinding(PanUpCommand, this.HandlePanUp));
			this.CommandBindings.Add(new CommandBinding(PanDownCommand, this.HandlePanDown));
			this.CommandBindings.Add(new CommandBinding(LookUpCommand, this.HandleLookUp));
			this.CommandBindings.Add(new CommandBinding(LookDownCommand, this.HandleLookDown));
			this.CommandBindings.Add(new CommandBinding(LookLeftCommand, this.HandleLookLeft));
			this.CommandBindings.Add(new CommandBinding(LookRightCommand, this.HandleLookRight));

			this.InheritanceBehavior = InheritanceBehavior.SkipToThemeNext;

			//this.InitializeComponent();

			//TransformGroup transform = new TransformGroup();
			//transform.Children.Add(this.zoom);
			//transform.Children.Add(this.translation);

			//this.RenderTransform = transform;

			//this.edges = ie.ToList();
			this.g = g;
			this.Width = 1024;
			this.Height = 1024;
			//this.
			//this.SetValue(Window.SizeToContentProperty, SizeToContent.WidthAndHeight);


			this.ClipToBounds = false;

			cont3 = new ContainerUIElement3D();

			ModelVisual3D alv = new ModelVisual3D();
			alv.Content = new AmbientLight(Colors.White);
			cont3.Children.Add(alv);

			Random rnd = new Random();

			Size max = new Size(0, 0);

			double z = 0;
			for (int j=0; j < 5; j++)
			{
				TfsControl tc = new TfsControl();
				tc._TfsEdge = ie;

				tc.FontFamily = new FontFamily("Arial");

				//tc.Background = new SolidColorBrush(Color.FromArgb(0x40, (byte)rnd.Next(), (byte)rnd.Next(), (byte)rnd.Next()));
				tc.Background = new SolidColorBrush(Color.FromArgb((byte)(0x10 * j), 0, 0, 0));

				tc.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
				Size sz = tc.DesiredSize;

				Debug.WriteLine(sz.Width);
				Debug.WriteLine(sz.Height);

				max.Width = Math.Max(max.Width, sz.Width);
				max.Height = Math.Max(max.Height, sz.Height);

				MeshGeometry3D g3 = new MeshGeometry3D();
				g3.Positions.Add(new Point3D(0, -sz.Height, z));	// 0
				g3.Positions.Add(new Point3D(sz.Width, -sz.Height, z));	// 1
				g3.Positions.Add(new Point3D(0, 0, z));	// 2
				g3.Positions.Add(new Point3D(sz.Width, 0, z));	// 3

				g3.Normals.Add(new Vector3D(0, -sz.Height, 1));
				g3.Normals.Add(new Vector3D(sz.Width, -sz.Height, 1));
				g3.Normals.Add(new Vector3D(0, 0, 1));
				g3.Normals.Add(new Vector3D(sz.Width, 0, 1));

				g3.TriangleIndices.Add(0);
				g3.TriangleIndices.Add(1);
				g3.TriangleIndices.Add(2);
				g3.TriangleIndices.Add(2);
				g3.TriangleIndices.Add(1);
				g3.TriangleIndices.Add(3);

				g3.TextureCoordinates.Add(new Point(0, 1));
				g3.TextureCoordinates.Add(new Point(1, 1));
				g3.TextureCoordinates.Add(new Point(0, 0));
				g3.TextureCoordinates.Add(new Point(1, 0));


				DiffuseMaterial dm = new DiffuseMaterial();
				dm.SetValue(Viewport2DVisual3D.IsVisualHostMaterialProperty, true);

				Viewport2DVisual3D me3 = new Viewport2DVisual3D();
				me3.Visual = tc;
				me3.Geometry = g3;
				me3.Material = dm;

				cont3.Children.Insert(0, me3);

				z -= spacing;
			}
			double stack_thick = -z;

			center = new Point3D(max.Width / 2, -max.Height * .8, -stack_thick / 2);

			Point3D interest = new Point3D(max.Width / 2, 0, -stack_thick * .8);

			cam = new PerspectiveCamera();
			cam.FieldOfView = 35;
			cam.Position = new Point3D(max.Width * .2, -max.Height * 1.5, stack_thick * .2);
			cam.LookDirection = new Vector3D(interest.X - cam.Position.X, interest.Y - cam.Position.Y, interest.Z - cam.Position.Z);
			cam.UpDirection = new Vector3D(0, 0, 1);

			this.Camera = cam;
			this.Children.Add(cont3);

		}

		Point3D center;
		const int nav_inc = 20;

		PerspectiveCamera cam;

		private void HandleZoomIn(object target, ExecutedRoutedEventArgs args)
		{
			Point3D pt = cam.Position;
			pt.Y += nav_inc;
			cam.Position = pt;
			//LookToCenter();
			ReportPositions();
		}
		private void HandleZoomOut(object target, ExecutedRoutedEventArgs args)
		{
			Point3D pt = cam.Position;
			pt.Y -= nav_inc;
			cam.Position = pt;
			//LookToCenter();
			ReportPositions();
		}
		private void HandlePanLeft(object target, ExecutedRoutedEventArgs args)
		{
			Point3D pt = cam.Position;
			pt.X += nav_inc;
			cam.Position = pt;
			//LookToCenter();
			ReportPositions();
		}
		private void HandlePanRight(object target, ExecutedRoutedEventArgs args)
		{
			Point3D pt = cam.Position;
			pt.X -= nav_inc;
			cam.Position = pt;
			//LookToCenter();
			ReportPositions();
		}
		private void HandlePanUp(object target, ExecutedRoutedEventArgs args)
		{
			Point3D pt = cam.Position;
			pt.Z += nav_inc;
			cam.Position = pt;
			//LookToCenter();
			ReportPositions();
		}
		private void HandlePanDown(object target, ExecutedRoutedEventArgs args)
		{
			Point3D pt = cam.Position;
			pt.Z -= nav_inc;
			if (pt.Z > 0)
				cam.Position = pt;
			//LookToCenter();
			ReportPositions();
		}
#if false
		private void HandleLookUp(object target, ExecutedRoutedEventArgs args)
		{
			Point3D pos = cam.Position;
			Point3D interest = new Point3D(pos.X + cam.LookDirection.X, pos.Y + cam.LookDirection.Y, pos.Z + cam.LookDirection.Z);
			interest.Z += nav_inc;
			cam.LookDirection = new Vector3D(interest.X - cam.Position.X, interest.Y - cam.Position.Y, interest.Z - cam.Position.Z);
			ReportPositions();
		}
		private void HandleLookDown(object target, ExecutedRoutedEventArgs args)
		{
			Point3D pos = cam.Position;
			Point3D interest = new Point3D(pos.X + cam.LookDirection.X, pos.Y + cam.LookDirection.Y, pos.Z + cam.LookDirection.Z);
			interest.Z -= nav_inc;
			cam.LookDirection = new Vector3D(interest.X - cam.Position.X, interest.Y - cam.Position.Y, interest.Z - cam.Position.Z);
			ReportPositions();
		}
#else
		private void HandleLookUp(object target, ExecutedRoutedEventArgs args)
		{
			Vector3D lp = cam.LookDirection;
			lp.Z += nav_inc;
			cam.LookDirection = lp;
			ReportPositions();
		}
		private void HandleLookDown(object target, ExecutedRoutedEventArgs args)
		{
			Vector3D lp = cam.LookDirection;
			lp.Z -= nav_inc;
			cam.LookDirection = lp;
			ReportPositions();
		}
		private void HandleLookLeft(object target, ExecutedRoutedEventArgs args)
		{
			Vector3D lp = cam.LookDirection;
			lp.X -= nav_inc;
			cam.LookDirection = lp;
			ReportPositions();
		}
		private void HandleLookRight(object target, ExecutedRoutedEventArgs args)
		{
			Vector3D lp = cam.LookDirection;
			lp.X += nav_inc;
			cam.LookDirection = lp;
			ReportPositions();
		}


#endif

		private void ReportPositions()
		{
			Debug.WriteLine("position: {0}   look: {0}", cam.Position, cam.LookDirection);
		}

		private void LookToCenter()
		{
			cam.LookDirection = new Vector3D(center.X - cam.Position.X, center.Y - cam.Position.Y, center.Z - cam.Position.Z);
			ReportPositions();
		}

		private void Content_MouseDown(object sender, MouseButtonEventArgs e)
		{
			//this.downPoint = e.GetPosition(this.main_wnd);
			//this.main_wnd.CaptureMouse();
		}
		private void Content_MouseMove(object sender, MouseEventArgs e)
		{
#if false
			if (this.main_wnd.IsMouseCaptured)
			{
				Vector delta = e.GetPosition(this.main_wnd) - this.downPoint;
				this.translation.X += delta.X;
				this.translation.Y += delta.Y;

				this.downPoint = e.GetPosition(this.main_wnd);
			}
#endif
		}
		private void Content_MouseUp(object sender, MouseEventArgs e)
		{
			//this.main_wnd.ReleaseMouseCapture();
		}
		private void Content_MouseWheel(object sender, MouseWheelEventArgs e)
		{
//			double zoom = Math.Pow(Zoom3D.ZoomFactor, e.Delta / 120.0);
	//		Point offset = e.GetPosition(this);
		//	this.Zoom(zoom, offset);
		}

		private void Zoom(double zoom, Point offset)
		{
#if false
			Vector v = new Vector((1 - zoom) * offset.X, (1 - zoom) * offset.Y);

			Vector translationVector = v * RenderTransform.Value;
			this.translation.X += translationVector.X;
			this.translation.Y += translationVector.Y;

			this.zoom.ScaleX = this.zoom.ScaleX * zoom;
			this.zoom.ScaleY = this.zoom.ScaleY * zoom;
#endif
		}


		//private TranslateTransform translation = new TranslateTransform();
		//private ScaleTransform zoom = new ScaleTransform();
		//private Point downPoint;

		//private const double ZoomFactor = 1.1;

	};



}