#region ================== Namespaces

using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.GZBuilder.Data;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Types;
using CodeImp.DoomBuilder.GZBuilder;

#endregion

namespace CodeImp.DoomBuilder.Controls
{
	public partial class ArgumentsControlSRB2 : UserControl
	{
		#region ================== Native stuff

		[DllImport("user32.dll")]
		private static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam);

		private const int WM_SETREDRAW = 11;

		#endregion

		#region ================== Variables

		private int action;
		private ArgumentInfo[] arginfo;
		private ArgumentInfo[] stringarginfo;
		private Label[] labels;
		private ArgumentBox[] args;
		private Label[] stringlabels;
		private TextBox[] stringargs;

		#endregion

		#region ================== Constructor

		public ArgumentsControlSRB2()
		{
			InitializeComponent();

			Reset();

			labels = new Label[] { arg0label, arg1label, arg2label, arg3label, arg4label, arg5label, arg6label, arg7label, arg8label, arg9label };
			args = new ArgumentBox[] { arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 };
			stringlabels = new Label[] { stringarg0label, stringarg1label };
			stringargs = new TextBox[] { stringarg0, stringarg1 };
		}

		#endregion

		#region ================== Setup

		public void Reset()
		{
			// Only when running (this.DesignMode won't do when not this, but one of parent controls is in design mode)
			if (LicenseManager.UsageMode != LicenseUsageMode.Designtime)
			{
				// do nothing.
			}
		}

		public void SetValue(Linedef l, bool first)
		{
			SetValue(l.Fields, l.Args, first);
		}

		public void SetValue(Thing t, bool first)
		{
			SetValue(t.Fields, t.Args, first);
		}

		private void SetValue(UniFields fields, int[] newargs, bool first)
		{
			// Update arguments
			for (int i = 0; i < args.Length; i++)
			{
				if (first)
					args[i].SetValue(newargs[i]);
				else
					if (!string.IsNullOrEmpty(args[i].Text) && newargs[i] != args[i].GetResult(int.MinValue)) args[i].ClearValue();
			}

			for (int i = 0; i < stringargs.Length; i++)
			{
				if (first)
					stringargs[i].Text = fields.GetValue("stringarg" + i, string.Empty);
				else
					if (fields.GetValue("stringarg" + i, string.Empty) != stringargs[i].Text) stringargs[i].Text = string.Empty;
			}
		}

		#endregion

		#region ================== Apply

		public void Apply(Linedef l, int step)
		{
			for (int i = 0; i < args.Length; i++)
				l.Args[i] = args[i].GetResult(l.Args[i], step);

			for (int i = 0; i < stringargs.Length; i++)
				if (!string.IsNullOrEmpty(stringargs[i].Text))
					l.Fields["stringarg" + i] = new UniValue(UniversalType.String, stringargs[i].Text);
		}

		public void Apply(Thing t, int step)
		{
			for (int i = 0; i < args.Length; i++)
				t.Args[i] = args[i].GetResult(t.Args[i], step);

			for (int i = 0; i < stringargs.Length; i++)
				if (!string.IsNullOrEmpty(stringargs[i].Text))
					t.Fields["stringarg" + i] = new UniValue(UniversalType.String, stringargs[i].Text);
		}

		#endregion

		#region ================== Update

		//TODO: Info for string args
		public void UpdateAction(int action, bool setuponly)
		{
			// Update arguments
			int showaction = 0;
			ArgumentInfo[] oldarginfo = (arginfo != null ? (ArgumentInfo[])arginfo.Clone() : null); //mxd
			ArgumentInfo[] oldstringarginfo = (stringarginfo != null ? (ArgumentInfo[])stringarginfo.Clone() : null);

			// Only when action type is known
			if (General.Map.Config.LinedefActions.ContainsKey(action)) showaction = action;

			// Update argument infos
			arginfo = General.Map.Config.LinedefActions[showaction].Args;
			stringarginfo = General.Map.Config.LinedefActions[showaction].StringArgs;

			//mxd. Don't update action args when old and new argument infos match
			if (arginfo != null && oldarginfo != null && stringarginfo != null && oldstringarginfo != null && ArgumentInfosMatch(arginfo, oldarginfo) && ArgumentInfosMatch(stringarginfo, oldstringarginfo))
				return;

			// Change the argument descriptions
			this.BeginUpdate();

			for (int i = 0; i < args.Length; i++)
				UpdateArgument(args[i], labels[i], arginfo[i]);
			
			for (int i = 0; i < stringargs.Length; i++)
				UpdateStringArgument(stringargs[i], stringlabels[i], stringarginfo[i]);
			
			if (!setuponly)
			{
				// Apply action's default arguments
				if (showaction != 0)
				{
					for (int i = 0; i < args.Length; i++)
						if (!ArgumentInfoMatches(arginfo, oldarginfo, i))
							args[i].SetDefaultValue();
				}
				else //or set them to 0
				{
					for (int i = 0; i < args.Length; i++)
						args[i].SetValue(0);
				}

				for (int i = 0; i < stringargs.Length; i++)
					if (!ArgumentInfoMatches(stringarginfo, oldstringarginfo, i))
						stringargs[i].Text = string.Empty;
			}

			// Store current action
			this.action = showaction;

			this.EndUpdate();
		}

		public void UpdateThingType(ThingTypeInfo info)
		{
			// Update arguments
			ArgumentInfo[] oldarginfo = (arginfo != null ? (ArgumentInfo[])arginfo.Clone() : null); //mxd
			ArgumentInfo[] oldstringarginfo = (stringarginfo != null ? (ArgumentInfo[])stringarginfo.Clone() : null);

			// Update argument infos
			if (info != null)
			{
				arginfo = info.Args;
				stringarginfo = info.StringArgs;
			}
			else
			{
				arginfo = General.Map.Config.LinedefActions[0].Args;
				stringarginfo = General.Map.Config.LinedefActions[0].Args;
			}

			//mxd. Don't update args when old and new argument infos match
			if (arginfo != null && oldarginfo != null && stringarginfo != null && oldstringarginfo != null && ArgumentInfosMatch(arginfo, oldarginfo) && ArgumentInfosMatch(stringarginfo, oldstringarginfo))
				return;

			// Change the argument descriptions
			this.BeginUpdate();

			for (int i = 0; i < args.Length; i++)
				UpdateArgument(args[i], labels[i], arginfo[i]);

			for (int i = 0; i < stringargs.Length; i++)
				UpdateStringArgument(stringargs[i], stringlabels[i], stringarginfo[i]);

			// Apply thing's default arguments
			if (info != null)
			{
				for (int i = 0; i < args.Length; i++)
					if (!ArgumentInfoMatches(arginfo, oldarginfo, i))
						args[i].SetDefaultValue();
			}
			else //or set them to 0
			{
				for (int i = 0; i < args.Length; i++)
					args[i].SetValue(0);
			}

			for (int i = 0; i < stringargs.Length; i++)
				if (!ArgumentInfoMatches(stringarginfo, oldstringarginfo, i))
					stringargs[i].Text = string.Empty;

			this.EndUpdate();
		}

		public void UpdateScriptControls()
		{
		}

		private void UpdateArgument(ArgumentBox arg, Label label, ArgumentInfo info)
		{
			// Update labels
			label.Text = info.Title + ":";
			label.Enabled = info.Used;
			arg.ForeColor = (label.Enabled ? SystemColors.WindowText : SystemColors.GrayText);
			arg.Setup(info);

			// Update tooltip
			UpdateToolTip(label, info);
		}
		private void UpdateStringArgument(TextBox arg, Label label, ArgumentInfo info)
		{
			// Update labels
			label.Text = info.Title + ":";
			label.Enabled = info.Used;
			arg.ForeColor = (label.Enabled ? SystemColors.WindowText : SystemColors.GrayText);

			// Update tooltip
			UpdateToolTip(label, info);
		}

		private void UpdateToolTip(Label label, ArgumentInfo info)
		{
			if (info.Used && !string.IsNullOrEmpty(info.ToolTip))
			{
				tooltip.SetToolTip(label, info.ToolTip);
				label.Font = new Font(label.Font, FontStyle.Underline);
				label.ForeColor = SystemColors.HotTrack;
			}
			else
			{
				tooltip.SetToolTip(label, null);
				label.Font = new Font(label.Font, FontStyle.Regular);
				label.ForeColor = SystemColors.WindowText;
			}
		}

		//mxd
		private static bool ArgumentInfosMatch(ArgumentInfo[] info1, ArgumentInfo[] info2)
		{
			if (info1.Length != info2.Length) return false;
			bool haveusedargs = false; // Arguments should still be reset if all arguments are unused

			for (int i = 0; i < info1.Length; i++)
			{
				if (info1[i].Used != info2[i].Used || info1[i].Type != info2[i].Type
					|| info1[i].Title.ToUpperInvariant() != info2[i].Title.ToUpperInvariant())
					return false;

				haveusedargs |= info1[i].Used;
			}

			return haveusedargs;
		}

		private static bool ArgumentInfoMatches(ArgumentInfo[] info1, ArgumentInfo[] info2, int i)
		{
			if (info1 == null || info2 == null) return false;
			return !(info1[i].Used != info2[i].Used || info1[i].Type != info2[i].Type
				|| info1[i].Title.ToUpperInvariant() != info2[i].Title.ToUpperInvariant());
		}

		#endregion

		#region ================== Redraw control

		private void BeginUpdate()
		{
			SendMessage(this.Parent.Handle, WM_SETREDRAW, false, 0);
		}

		private void EndUpdate()
		{
			SendMessage(this.Parent.Handle, WM_SETREDRAW, true, 0);
			this.Parent.Refresh();
		}

		#endregion
	}
}
