
#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.Drawing;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Windows;
using System.Reflection;
using System.Globalization;

#endregion

namespace CodeImp.DoomBuilder.BuilderModes
{
	public partial class FindReplaceForm : DelayedForm
	{
		#region ================== Constants
		
		#endregion

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

		private FindReplaceMode mode; //mxd
		private FindReplaceType newfinder;
		private FindReplaceType finder;
		private List<FindReplaceType> findtypeslist;
		private bool controlpressed;
		private bool shiftpressed;
		private bool suppressevents;
		private Font hintfont; //mxd

		#endregion

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

		internal FindReplaceType Finder { get { return finder; } }

		#endregion

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

		// Constructor
		public FindReplaceForm()
		{
			// Initialize
			InitializeComponent();

			//mxd. Create hint font
			hintfont = new Font(this.Font, FontStyle.Underline);

			// Find all find/replace types
			Type[] findtypes = BuilderPlug.Me.FindClasses(typeof(FindReplaceType));
			findtypeslist = new List<FindReplaceType>(findtypes.Length);
			foreach(Type t in findtypes)
			{
				object[] attr = t.GetCustomAttributes(typeof(FindReplaceAttribute), true);
				if(attr.Length > 0)
				{
					FindReplaceType finderinst;
					try
					{
						// Create instance
						finderinst = (FindReplaceType)Assembly.GetExecutingAssembly().CreateInstance(t.FullName, false, BindingFlags.Default, null, null, CultureInfo.CurrentCulture, new object[0]);
					}
					catch(TargetInvocationException ex)
					{
						// Error!
						General.ErrorLogger.Add(ErrorType.Error, "Failed to create class instance \"" + t.Name + "\"");
						General.WriteLogLine(ex.InnerException.GetType().Name + ": " + ex.InnerException.Message);
						throw;
					}
					catch(Exception ex)
					{
						// Error!
						General.ErrorLogger.Add(ErrorType.Error, "Failed to create class instance \"" + t.Name + "\"");
						General.WriteLogLine(ex.GetType().Name + ": " + ex.Message);
						throw;
					}
					
					// Add the finder to the list
					findtypeslist.Add(finderinst);
				}
			}
		}
		
		#endregion

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

		// Replace (un)checked
		private void doreplace_CheckedChanged(object sender, EventArgs e)
		{
			findbutton.Text = (doreplace.Checked ? "Replace" : "Find");
			replaceinput.Enabled = doreplace.Checked;
			browsereplace.Enabled = doreplace.Checked && newfinder != null && newfinder.Attributes.BrowseButton;
		}

		// Search type selected
		private void searchtypes_SelectedIndexChanged(object sender, EventArgs e)
		{
			// Create instance of the selected type
			newfinder = (FindReplaceType)searchtypes.SelectedItem;
			
			// Now setup the interface
			browsefind.Enabled = newfinder.Attributes.BrowseButton;
			browsefind.Image = newfinder.BrowseImage;
			browsereplace.Enabled = newfinder.Attributes.BrowseButton;
			browsereplace.Image = newfinder.BrowseImage;
			if(!newfinder.CanReplace()) doreplace.Checked = false;
			doreplace_CheckedChanged(this, EventArgs.Empty); //mxd. Update the rest of replace controls
			doreplace.Enabled = newfinder.CanReplace();

			//mxd. Update hint text
			if (!string.IsNullOrEmpty(newfinder.UsageHint))
			{
				tooltip.SetToolTip(labelfind, newfinder.UsageHint);
				labelfind.Font = hintfont;
				labelfind.ForeColor = SystemColors.HotTrack;
				labelfind.Cursor = Cursors.Hand;
			}
			else if (labelfind.ForeColor == SystemColors.HotTrack)
			{
				tooltip.SetToolTip(labelfind, string.Empty);
				labelfind.Font = this.Font;
				labelfind.ForeColor = SystemColors.ControlText;
				labelfind.Cursor = Cursors.Default;
			}
		}
		
		// Browse find button clicked
		private void browsefind_Click(object sender, EventArgs e)
		{
			findinput.Text = newfinder.Browse(findinput.Text);
		}

		// Browse replacement clicked
		private void browsereplace_Click(object sender, EventArgs e)
		{
			replaceinput.Text = newfinder.BrowseReplace(replaceinput.Text);
		}

		//mxd
		private void findinput_TextChanged(object sender, EventArgs e)
		{
			findbutton.Enabled = !string.IsNullOrEmpty(findinput.Text);
		}

		// Find / Replace clicked
		private void findbutton_Click(object sender, EventArgs e)
		{
			// Reset results
			suppressevents = true;
			resultslist.Items.Clear();
			
			// Hide object information
			General.Interface.HideInfo();
			
			// Keep the finder we used for the search
			finder = newfinder;
			
			// Enable/disable buttons
			editbutton.Enabled = false;
			deletebutton.Visible = finder.AllowDelete;
			deletebutton.Enabled = false;
			resultslist.BeginUpdate();
			
			// Perform the search / replace and show the results
			if(doreplace.Checked)
			{
				General.Map.UndoRedo.CreateUndo("Replace " + searchtypes.SelectedItem);

				resultslist.Items.AddRange(finder.Find(findinput.Text, withinselection.Checked, true, replaceinput.Text, false));
				resultscount.Text = resultslist.Items.Count + " items found and replaced.";

				// Withdraw the undo step if nothing was replaced
				if(resultslist.Items.Count < 1)
				{
					mode.Volatile = false; //mxd. Otherwice UndoManager.PerformUndo will cancel the mode...
					General.Map.UndoRedo.WithdrawUndo();
					mode.Volatile = true; //mxd
				}
			}
			else
			{
				resultslist.Items.AddRange(finder.Find(findinput.Text, withinselection.Checked, false, string.Empty, false));
				resultscount.Text = resultslist.Items.Count + " items found.";
			}
			
			// Select all results
			General.Map.Map.ClearAllSelected();
			for(int i = 0; i < resultslist.Items.Count; i++)
				resultslist.SelectedIndices.Add(i);
			
			// Let the finder know about the selection
			FindReplaceObject[] selection = GetSelection();
			finder.ObjectSelected(selection);
			
			// Open results part of window
			this.Size = new Size(this.Width, this.Height - this.ClientSize.Height + resultspanel.Top + resultspanel.Height);
			resultspanel.Visible = true;
			resultslist.EndUpdate();
			suppressevents = false;
			
			// Enable/disable buttons
			editbutton.Enabled = (resultslist.SelectedIndex > -1);
			deletebutton.Enabled = (resultslist.SelectedIndex > -1);
			
			// Set correct presentation mode
			General.Map.Renderer2D.SetPresentation(finder.RenderPresentation);
			
			// Redraw the screen, this will show the selection
			General.Interface.RedrawDisplay();
		}
		
		// Found item selected
		private void resultslist_SelectedIndexChanged(object sender, EventArgs e)
		{
			if(!suppressevents)
			{
				// Let the finder know about the selection
				FindReplaceObject[] selection = GetSelection();
				finder.ObjectSelected(selection);
				
				// Enable/disable buttons
				editbutton.Enabled = (resultslist.SelectedIndex > -1);
				deletebutton.Enabled = (resultslist.SelectedIndex > -1);

				// Redraw the screen, this will show the selection
				General.Interface.RedrawDisplay();
			}
		}

		// Mouse released
		private void resultslist_MouseUp(object sender, MouseEventArgs e)
		{
			// Right-clicked?
			if(e.Button == MouseButtons.Right)
			{
				int index = resultslist.IndexFromPoint(e.Location);
				if(index != ListBox.NoMatches)
				{
					// Select the right-clicked item, if not selected
					if(!resultslist.SelectedIndices.Contains(index))
					{
						if(!controlpressed && !shiftpressed) resultslist.SelectedItems.Clear();
						resultslist.SelectedIndices.Add(index);
						Update();
					}
					
					// Edit selected objects
					editbutton_Click(this, EventArgs.Empty);
				}
			}
			
			// Enable/disable buttons
			editbutton.Enabled = (resultslist.SelectedIndex > -1);
			deletebutton.Enabled = (resultslist.SelectedIndex > -1);
		}
		
		// Double clicked
		private void resultslist_MouseDoubleClick(object sender, MouseEventArgs e)
		{
			resultslist_MouseUp(sender, new MouseEventArgs(MouseButtons.Right, 1, e.X, e.Y, e.Delta));
		}
		
		// Window closing
		private void FindReplaceForm_FormClosing(object sender, FormClosingEventArgs e)
		{
			// If the user closes the form, then just cancel the mode
			if(e.CloseReason == CloseReason.UserClosing)
			{
				e.Cancel = true;
				General.Interface.Focus();
				General.Editing.CancelMode();
			}

			//mxd
			hintfont.Dispose();
		}

		// Close button clicked
		private void closebutton_Click(object sender, EventArgs e)
		{
			General.Interface.Focus();
			General.Editing.CancelMode();
		}

		// Key pressed
		private void FindReplaceForm_KeyDown(object sender, KeyEventArgs e)
		{
			// Keep modifiers
			controlpressed = e.Control;
			shiftpressed = e.Shift;

			// Enable/disable buttons
			editbutton.Enabled = (resultslist.SelectedIndex > -1);
			deletebutton.Enabled = (resultslist.SelectedIndex > -1);
		}

		// Key released
		private void FindReplaceForm_KeyUp(object sender, KeyEventArgs e)
		{
			// Keep modifiers
			controlpressed = e.Control;
			shiftpressed = e.Shift;

			// Enable/disable buttons
			editbutton.Enabled = (resultslist.SelectedIndex > -1);
			deletebutton.Enabled = (resultslist.SelectedIndex > -1);
		}

		// Edit Selection
		private void editbutton_Click(object sender, EventArgs e)
		{
			FindReplaceObject[] items = GetSelection();
			if(items.Length > 0)
			{
				// Edit selected objects
				suppressevents = true;
				finder.EditObjects(items);
				suppressevents = false;
				resultslist_SelectedIndexChanged(sender, e);
				
				// Redraw the screen, this will show the selection
				General.Interface.RedrawDisplay();
			}
		}

		// Delete Selection
		private void deletebutton_Click(object sender, EventArgs e)
		{
			// Delete selected objects
			FindReplaceObject[] items = GetSelection();
			if(items.Length > 0)
			{
				suppressevents = true;
				finder.DeleteObjects(items);
				foreach(FindReplaceObject obj in items)
				{
					resultslist.Items.Remove(obj);
				}
				suppressevents = false;
				resultslist_SelectedIndexChanged(sender, e);
				
				// Redraw the screen, this will show the selection
				General.Interface.RedrawDisplay();
			}
		}

		private void FindReplaceForm_HelpRequested(object sender, HelpEventArgs hlpevent)
		{
			General.ShowHelp("e_findreplace.html");
		}
		
		#endregion

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

		// This shows the window
		public void Show(Form owner, FindReplaceMode mode)
		{
			//mxd
			this.mode = mode;
			
			// Position at left-top of owner
			this.Location = new Point(owner.Location.X + 20, owner.Location.Y + 90);
			
			// Re-fill the search types list
			searchtypes.Items.Clear();
			foreach(FindReplaceType t in findtypeslist)
			{
				// Check if it should be visible
				if(t.DetermineVisiblity())
				{
					searchtypes.Items.Add(t);
				}
			}
			
			// Select previously selected item
			for(int i = 0; i < searchtypes.Items.Count; i++)
			{
				if(searchtypes.Items[i] == newfinder)
					searchtypes.SelectedIndex = i;
			}
			
			// Select first if none was selected
			if(searchtypes.SelectedIndex < 0) searchtypes.SelectedIndex = 0;
			
			// Close results part
			resultspanel.Visible = false;
			this.Size = new Size(this.Width, this.Height - this.ClientSize.Height + resultspanel.Top);

			// Show window
			base.Show(owner);
		}

		// This hides the window
		new public void Hide()
		{
			base.Hide();
			
			// Clear search results
			resultslist.Items.Clear();
		}
		
		// This returns the selected item(s)
		internal FindReplaceObject[] GetSelection()
		{
			// Return selected objects
			FindReplaceObject[] list = new FindReplaceObject[resultslist.SelectedItems.Count];
			int index = 0;
			foreach(object obj in resultslist.SelectedItems)
			{
				list[index++] = (FindReplaceObject)obj;
			}
			return list;
		}
		
		#endregion

	}
}