
#region ================== Copyright (c) 2007 Pascal vd Heiden

/*
 * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
 * This program is released under GNU General Public License
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 */

#endregion

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

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using CodeImp.DoomBuilder.GZBuilder;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
using System.Drawing;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Types;

#endregion

namespace CodeImp.DoomBuilder.BuilderModes
{
	[FindReplace("Thing Action and Arguments", BrowseButton = true)]
	internal class FindThingAction : BaseFindThing
	{
		#region ================== Constants

		#endregion

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

		#endregion

		#region ================== Properties

		public override Presentation RenderPresentation { get { return Presentation.Things; } }
		public override Image BrowseImage { get { return Properties.Resources.List; } }
		
		#endregion

		#region ================== Constructor / Destructor

		#endregion

		#region ================== Methods

		// This is called to test if the item should be displayed
		public override bool DetermineVisiblity()
		{
			return General.Map.FormatInterface.HasThingAction;
		}

		// This is called when the browse button is pressed
		public override string Browse(string initialvalue)
		{
			int action;
			int.TryParse(initialvalue, out action);
			return General.Interface.BrowseLinedefActions(BuilderPlug.Me.FindReplaceForm, action, true).ToString();
		}

		// This is called when the browse replace button is pressed
		public override string BrowseReplace(string initialvalue)
		{
			int action;
			int.TryParse(initialvalue, out action);
			return General.Interface.BrowseLinedefActions(BuilderPlug.Me.FindReplaceForm, action).ToString();
		}

		// This is called to perform a search (and replace)
		// Returns a list of items to show in the results list
		// replacewith is null when not replacing
		public override FindReplaceObject[] Find(string value, bool withinselection, bool replace, string replacewith, bool keepselection)
		{
			List<FindReplaceObject> objs = new List<FindReplaceObject>();

			// Interpret the replacement
			int replaceaction = 0;
			string replacearg0str = string.Empty; //mxd
			int[] replaceargs = null; //mxd
			if(replace) 
			{
				string[] replaceparts = replacewith.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

				// If it cannot be interpreted, set replacewith to null (not replacing at all)
				if(replaceparts.Length == 0) replacewith = null; //mxd
				if(!int.TryParse(replaceparts[0], out replaceaction)) replacewith = null;
				if(replaceaction < 0) replacewith = null;
				if(replaceaction > Int16.MaxValue) replacewith = null;
				if(replacewith == null)
				{
					MessageBox.Show("Invalid replace value for this search type!", "Find and Replace", MessageBoxButtons.OK, MessageBoxIcon.Error);
					return objs.ToArray();
				}

				//mxd. Now try parsing the args
				if(replaceparts.Length > 1) 
				{
					replaceargs = new[] { int.MinValue, int.MinValue, int.MinValue, int.MinValue, int.MinValue };
					int i = 1;

					//mxd. Named script search support...
					if(General.Map.UDMF && Array.IndexOf(GZGeneral.ACS_SPECIALS, replaceaction) != -1) 
					{
						string possiblescriptname = replaceparts[1].Trim().Replace("\"", "").ToLowerInvariant();
						int tmp;
						if(!string.IsNullOrEmpty(possiblescriptname) && possiblescriptname != "*" && !int.TryParse(possiblescriptname, out tmp)) 
						{
							replacearg0str = possiblescriptname;
							i = 2;
						}
					}

					for(; i < replaceparts.Length && i < replaceargs.Length + 1; i++) 
					{
						int argout;
						if(replaceparts[i].Trim() == "*") continue; //mxd. Any arg value support
						if(int.TryParse(replaceparts[i].Trim(), out argout)) replaceargs[i - 1] = argout;
					}
				}
			}

			// Interpret the number given
			int action;
			string[] parts = value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

			//For the search, the user may make the following queries:
			//	action arg0 arg1 arg2 arg3 arg4
			//	action arg0str arg1 arg2 arg3 arg4
			//
			//this allows users to search for lines that contain actions with specific arguments.
			//useful for locating script lines

			if(int.TryParse(parts[0], out action)) 
			{
				int[] args = null;
				string arg0str = string.Empty; //mxd

				//parse the arg values out
				if(parts.Length > 1) 
				{
					args = new[] { int.MinValue, int.MinValue, int.MinValue, int.MinValue, int.MinValue };
					int i = 1;

					//mxd. Named script search support...
					if(General.Map.UDMF && Array.IndexOf(GZGeneral.ACS_SPECIALS, action) != -1) 
					{
						string possiblescriptname = parts[1].Trim().Replace("\"", "").ToLowerInvariant();
						int tmp;
						if(!string.IsNullOrEmpty(possiblescriptname) && possiblescriptname != "*" && !int.TryParse(possiblescriptname, out tmp)) 
						{
							arg0str = possiblescriptname;
							i = 2;
						}
					}

					for(; i < parts.Length && i < args.Length + 1; i++) 
					{
						int argout;
						if(parts[i].Trim() == "*") continue; //mxd. Any arg value support
						if(int.TryParse(parts[i].Trim(), out argout)) args[i - 1] = argout;
					}
				}

				// Where to search?
				ICollection<Thing> list = withinselection ? General.Map.Map.GetSelectedThings(true) : General.Map.Map.Things;

				// Go for all things
				foreach(Thing t in list) 
				{
					// Action matches? -1 means any action (mxd)
					if((action == -1 && t.Action == 0) || (action > -1 && t.Action != action)) continue;

					bool match = true;
					string argtext = "";

					//if args were specified, then process them
					if(args != null) 
					{
						int x = 0;
						argtext = ". Args: ";

						//mxd. Check script name...
						if(!string.IsNullOrEmpty(arg0str)) 
						{
							string s = t.Fields.GetValue("arg0str", string.Empty);
							if(s.ToLowerInvariant() != arg0str)
								match = false;
							else
								argtext += "\"" + s + "\"";

							x = 1;
						}

						for(; x < args.Length; x++) 
						{
							if(args[x] != int.MinValue && args[x] != t.Args[x]) 
							{
								match = false;
								break;
							}
							argtext += (x == 0 ? "" : ", ") + t.Args[x];
						}
					}
					//mxd. Add action args
					else
					{
						List<string> argslist = new List<string>();

						// Process args, drop trailing zeroes
						for(int i = t.Args.Length - 1; i > -1; i--)
						{
							if(t.Args[i] == 0 && argslist.Count == 0) continue; // Skip tail zeroes
							argslist.Insert(0, t.Args[i].ToString());
						}

						// Process arg0str...
						if(Array.IndexOf(GZGeneral.ACS_SPECIALS, t.Action) != -1)
						{
							string s = t.Fields.GetValue("arg0str", string.Empty);
							if(!string.IsNullOrEmpty(s))
							{
								if(argslist.Count > 0)
									argslist[0] = "\"" + s + "\"";
								else
									argslist.Add("\"" + s + "\"");
							}
						}

						// Create args string
						if(argslist.Count > 0)
						{
							argtext = ". Args: " + string.Join(", ", argslist.ToArray());
						}
					}

					if(match) 
					{
						// Replace
						if(replace) 
						{
							t.Action = replaceaction;

							//mxd. Replace args as well?
							if(replaceargs != null) 
							{
								int i = 0;
								if(!string.IsNullOrEmpty(replacearg0str)) 
								{
									t.Fields["arg0str"] = new UniValue(UniversalType.String, replacearg0str);
									i = 1;
								}

								for(; i < replaceargs.Length; i++) 
								{
									if(replaceargs[i] != int.MinValue) t.Args[i] = replaceargs[i];
								}
							}
						}

						// Add to list
						ThingTypeInfo ti = General.Map.Data.GetThingInfo(t.SRB2Type);
						objs.Add(new FindReplaceObject(t, "Thing " + t.Index + " (" + ti.Title + argtext + ")"));
					}
				}
			}

			return objs.ToArray();
		}

		#endregion
	}
}
