using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Controls;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.Geometry;

namespace CodeImp.DoomBuilder.SoundPropagationMode
{
	public partial class SoundEnvironmentPanel : UserControl
	{
		public event EventHandler OnShowWarningsOnlyChanged; //mxd
		public BufferedTreeView SoundEnvironments { get { return soundenvironments; } set { soundenvironments = value; } }
		private readonly int warningiconindex; //mxd
		private bool treeisupdating; //mxd
		private int nodewarningscount; //mxd
		private const string shownodewarningstext = "Show nodes with warnings only (N)";
		private delegate void UpdateCallback(); //mxd

		public SoundEnvironmentPanel()
		{
			InitializeComponent();

			soundenvironments.ImageList = new ImageList();
			foreach(Bitmap icon in BuilderPlug.Me.DistinctIcons)
			{
				soundenvironments.ImageList.Images.Add(icon);
			}
			soundenvironments.ImageList.Images.Add(Properties.Resources.Warning);
			warningiconindex = soundenvironments.ImageList.Images.Count - 1; //mxd
		}

		//mxd
		public void BeginUpdate()
		{
			if(treeisupdating) return;

			if(soundenvironments.InvokeRequired)
			{
				UpdateCallback d = BeginUpdate;
				soundenvironments.Invoke(d);
			}
			else
			{
				soundenvironments.BeginUpdate();
				nodewarningscount = 0;
			}
		}

		//mxd
		public void EndUpdate()
		{
			if(soundenvironments.InvokeRequired)
			{
				UpdateCallback d = EndUpdate;
				soundenvironments.Invoke(d);
			}
			else
			{
				treeisupdating = false;
				soundenvironments.Sort();
				soundenvironments.EndUpdate();

				// Update the checkbox
				showwarningsonly.Text = shownodewarningstext.Replace("(N)", "(" + nodewarningscount + ")");
				showwarningsonly.Enabled = (showwarningsonly.Checked || nodewarningscount > 0);
			}
		}

		public void AddSoundEnvironment(SoundEnvironment se) 
		{
			TreeNode topnode = new TreeNode(se.Name);
			topnode.Tag = se; //mxd
			TreeNode thingsnode = new TreeNode("Things (" + se.Things.Count + ")");
			TreeNode linedefsnode = new TreeNode("Linedefs (" + se.Linedefs.Count + ")");
			int notdormant = 0;
			int iconindex = BuilderPlug.Me.DistinctColors.IndexOf(se.Color); //mxd
			int topindex = iconindex; //mxd
			bool nodehaswarnings = false; //mxd

			thingsnode.ImageIndex = iconindex; //mxd
			thingsnode.SelectedImageIndex = iconindex; //mxd
			linedefsnode.ImageIndex = iconindex; //mxd
			linedefsnode.SelectedImageIndex = iconindex; //mxd

			// Add things
			foreach(Thing t in se.Things)
			{
				TreeNode thingnode = new TreeNode("Thing " + t.Index);
				thingnode.Tag = t;
				thingnode.ImageIndex = iconindex; //mxd
				thingnode.SelectedImageIndex = iconindex; //mxd
				thingsnode.Nodes.Add(thingnode);

				if(!BuilderPlug.ThingDormant(t))
				{
					notdormant++;
				}
				else
				{
					thingnode.Text += " (dormant)";
				}
			}

			// Set the icon to warning sign and add the tooltip when there are more than 1 non-dormant things
			if(notdormant > 1)
			{
				thingsnode.ImageIndex = warningiconindex;
				thingsnode.SelectedImageIndex = warningiconindex;
				topindex = warningiconindex;

				foreach(TreeNode tn in thingsnode.Nodes)
				{
					if(!BuilderPlug.ThingDormant((Thing)tn.Tag))
					{
						tn.ImageIndex = warningiconindex;
						tn.SelectedImageIndex = warningiconindex;
						tn.ToolTipText = "More than one thing in this\nsound environment is set to be\nactive. Set all but one thing\nto dormant.";
						nodewarningscount++; //mxd
						nodehaswarnings = true; //mxd
					}
				}
			}

			// Add linedefs
			foreach(Linedef ld in se.Linedefs)
			{
				bool showwarning = false;
				TreeNode linedefnode = new TreeNode("Linedef " + ld.Index);
				linedefnode.Tag = ld;
				linedefnode.ImageIndex = iconindex; //mxd
				linedefnode.SelectedImageIndex = iconindex; //mxd

				if(ld.Back == null || ld.Front == null)
				{
					showwarning = true;
					linedefnode.ToolTipText = "This line is single-sided, but has\nthe sound boundary flag set.";
				}
				else if(se.Sectors.Contains(ld.Front.Sector) && se.Sectors.Contains(ld.Back.Sector))
				{
					showwarning = true;
					linedefnode.ToolTipText = "More than one thing in this\nThe sectors on both sides of\nthe line belong to the same\nsound environment.";
				}

				if(showwarning)
				{
					linedefnode.ImageIndex = warningiconindex;
					linedefnode.SelectedImageIndex = warningiconindex;

					linedefsnode.ImageIndex = warningiconindex;
					linedefsnode.SelectedImageIndex = warningiconindex;

					topindex = warningiconindex;
					nodewarningscount++; //mxd
					nodehaswarnings = true; //mxd
				}

				linedefsnode.Nodes.Add(linedefnode);
			}

			//mxd
			if(!showwarningsonly.Checked || nodehaswarnings)
			{
				topnode.Nodes.Add(thingsnode);
				topnode.Nodes.Add(linedefsnode);

				topnode.Tag = se;
				topnode.ImageIndex = topindex;
				topnode.SelectedImageIndex = topindex;

				// Sound environments will no be added in consecutive order, so we'll have to find
				// out where in the tree to add the node
				int insertionplace = 0;

				foreach(TreeNode tn in soundenvironments.Nodes)
				{
					if(se.ID < ((SoundEnvironment)tn.Tag).ID) break;
					insertionplace++;
				}

				soundenvironments.Nodes.Insert(insertionplace, topnode);
			}
		}

		public void HighlightSoundEnvironment(SoundEnvironment se)
		{
			if(soundenvironments.SelectedNode != null) return; //mxd
			if(!treeisupdating) soundenvironments.BeginUpdate();
			soundenvironments.CollapseAll(); //mxd

			foreach(TreeNode tn in soundenvironments.Nodes)
			{
				if(se != null && ((SoundEnvironment)tn.Tag).ID == se.ID)
				{
					if(tn.NodeFont == null || tn.NodeFont.Style != FontStyle.Bold)
						tn.NodeFont = new Font(soundenvironments.Font.FontFamily, soundenvironments.Font.Size, FontStyle.Bold);

					//mxd
					tn.Expand();
					if(soundenvironments.SelectedNode == null) tn.EnsureVisible();
				}
				else
				{
					if(tn.NodeFont == null || tn.NodeFont.Style != FontStyle.Regular)
						tn.NodeFont = new Font(soundenvironments.Font.FontFamily, soundenvironments.Font.Size);
				}
			}

			if(!treeisupdating) soundenvironments.EndUpdate();
		}

		//mxd. This (de)selects sound environment
		public void SelectSoundEnvironment(SoundEnvironment se)
		{
			if((se == null && soundenvironments.SelectedNode != null) || (se != null && soundenvironments.SelectedNode != null && soundenvironments.SelectedNode.Tag is SoundEnvironment && ((SoundEnvironment)soundenvironments.SelectedNode.Tag).ID == se.ID))
			{
				soundenvironments.SelectedNode = null;
				return;
			}

			if(se == null) return;

			foreach(TreeNode tn in soundenvironments.Nodes)
			{
				if(((SoundEnvironment)tn.Tag).ID == se.ID)
				{
					soundenvironments.SelectedNode = tn;
					tn.EnsureVisible();
					return;
				}
			}
		}

		private static bool IsClickOnText(TreeView treeView, TreeNode node, Point location)
		{
			var hitTest = treeView.HitTest(location);
			return hitTest.Node == node	&& (hitTest.Location == TreeViewHitTestLocations.Label || hitTest.Location == TreeViewHitTestLocations.Image);
		}

		private static void ProcessNodeClick(TreeNode node)
		{
			if(node == null) return;

			List<Vector2D> points = new List<Vector2D>();
			RectangleF area = MapSet.CreateEmptyArea();

			if(node.Parent == null)
			{
				if(node.Tag is SoundEnvironment)
				{
					SoundEnvironment se = (SoundEnvironment)node.Tag;

					foreach(Sector s in se.Sectors)
					{
						foreach(Sidedef sd in s.Sidedefs)
						{
							points.Add(sd.Line.Start.Position);
							points.Add(sd.Line.End.Position);
						}
					}
				}
				else
				{
					// Don't zoom if the wrong nodes are selected
					return;
				}
			}
			else
			{
				if(node.Tag is Thing)
				{
					Thing t = (Thing)node.Tag;

					// We don't want to be zoomed too closely, so add somepadding
					points.Add(t.Position - 200);
					points.Add(t.Position + 200);
				}
				else if(node.Tag is Linedef)
				{
					Linedef ld = (Linedef)node.Tag;

					points.Add(ld.Start.Position);
					points.Add(ld.End.Position);
				}
				else
				{
					// Don't zoom if the wrong nodes are selected
					return;
				}
			}

			area = MapSet.IncreaseArea(area, points);

			// Add padding
			area.Inflate(100f, 100f);

			// Zoom to area
			ClassicMode editmode = (General.Editing.Mode as ClassicMode);
			editmode.CenterOnArea(area, 0.0f);
		}

		#region ================== Events

		private void soundenvironments_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
		{
			if(IsClickOnText(soundenvironments, e.Node, e.Location))
			{
				ProcessNodeClick(e.Node);
			}
		}

		private void soundenvironments_BeforeSelect(object sender, TreeViewCancelEventArgs e)
		{
			if(e.Action != TreeViewAction.ByMouse) return;
			var position = soundenvironments.PointToClient(Cursor.Position);
			e.Cancel = !IsClickOnText(soundenvironments, e.Node, position);
		}

		//mxd
		private void showwarningsonly_CheckedChanged(object sender, EventArgs e)
		{
			if(OnShowWarningsOnlyChanged != null) OnShowWarningsOnlyChanged(this, EventArgs.Empty);
		}

		#endregion
	}
}
