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

#endregion

namespace CodeImp.DoomBuilder.Controls
{
	public class NumericTextbox : AutoSelectTextbox
	{
		#region ================== Constants

		#endregion

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

		private bool allownegative;		// Allow negative numbers
		private bool allowrelative;		// Allow ++, --, * and / prefix for relative changes
		private bool allowdecimal;		// Allow decimal (float) numbers
		private int maximumvalue = System.Int32.MaxValue;
		private bool controlpressed;
		private int incrementstep; //mxd. Step for +++ and  --- prefixes
		private readonly ToolTip tooltip; //mxd
		
		#endregion

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

		public bool AllowNegative { get { return allownegative; } set { allownegative = value; } }
		public bool AllowRelative { get { return allowrelative; } set { allowrelative = value; UpdateTextboxStyle(); } }
		public bool AllowDecimal  { get { return allowdecimal; } set { allowdecimal = value; } }
		public int MaximumValue { get { return maximumvalue; } set { maximumvalue = value; } }

		#endregion

		#region ================== Constructor / Disposer

		// Constructor
		public NumericTextbox()
		{
			this.ImeMode = ImeMode.Off;

			//mxd. Setup tooltip
			this.tooltip = new ToolTip { AutomaticDelay = 100, AutoPopDelay = 8000, InitialDelay = 100, ReshowDelay = 100 };
		}

		#endregion

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

		// Key pressed
		protected override void OnKeyDown(KeyEventArgs e)
		{
			controlpressed = e.Control;
			base.OnKeyDown(e);
		}

		// Key released
		protected override void OnKeyUp(KeyEventArgs e)
		{
			controlpressed = e.Control;
			base.OnKeyUp(e);
		}
		
		// When a key is pressed
		protected override void OnKeyPress(KeyPressEventArgs e)
		{
			incrementstep = 0; //mxd
			string allowedchars = "0123456789\b";
			
			// Determine allowed chars
			if(allownegative) allowedchars += CultureInfo.CurrentCulture.NumberFormat.NegativeSign;
			if(allowrelative) allowedchars += "+-*/"; //mxd
			if(controlpressed) allowedchars += "\u0018\u0003\u0016";
			if(allowdecimal || this.Text.StartsWith("*") || this.Text.StartsWith("/")) //mxd
				allowedchars += CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
			
			// Check if key is not allowed
			if(allowedchars.IndexOf(e.KeyChar) == -1)
			{
				// Cancel this
				e.Handled = true;
			}
			else
			{
				//mxd. Check if * or / is pressed
				if(e.KeyChar == '*' || e.KeyChar == '/') 
				{
					if(this.SelectionStart - 1 > -1) e.Handled = true; //only valid when at the start of the text
				}
				// Check if + or - is pressed
				else if((e.KeyChar == '+') || (e.KeyChar == '-'))
				{
					string nonselectedtext;
					
					// Determine non-selected text
					if(this.SelectionLength > 0)
					{
						nonselectedtext = this.Text.Substring(0, this.SelectionStart) +
							this.Text.Substring(this.SelectionStart + this.SelectionLength);
					}
					else if(this.SelectionLength < 0)
					{
						nonselectedtext = this.Text.Substring(0, this.SelectionStart + this.SelectionLength) +
							this.Text.Substring(this.SelectionStart);
					}
					else
					{
						nonselectedtext = this.Text;
					}
					
					// Not at the start?
					int selectionpos = this.SelectionStart - 1;
					if(this.SelectionLength < 0) selectionpos = (this.SelectionStart + this.SelectionLength) - 1;
					if(selectionpos > -1)
					{
						// Find any other characters before the insert position
						string textpart = this.Text.Substring(0, selectionpos + 1);
						textpart = textpart.Replace("+", "");
						textpart = textpart.Replace("-", "");
						if(textpart.Length > 0)
						{
							// Cancel this
							e.Handled = true;
						}
					}

					// Determine other prefix
					char otherprefix = (e.KeyChar == '+' ? '-' : '+');
					
					// Limit the number of + and - allowed
					int numprefixes = nonselectedtext.Split(e.KeyChar, otherprefix).Length;
					if(numprefixes > 3)
					{
						// Can't have more than 3 prefixes (mxd)
						e.Handled = true;
					}
					else if(numprefixes > 1)
					{
						// Must have 2 or 3 same prefixes
						if(this.Text.IndexOf(e.KeyChar) == -1) e.Handled = true;

						// Double or triple prefix must be allowed
						if(!allowrelative) e.Handled = true;
					}
				}
			}
			
			// Call base
			base.OnKeyPress(e);
		}

		// Validate contents
		protected override void OnValidating(CancelEventArgs e)
		{
			//mxd. We may want "++" and "--" on their own...
			if(allowrelative && (this.Text == "++" || this.Text == "--"))
			{
				// Call base and bail out
				base.OnValidating(e);
				return;
			}
			
			// Strip prefixes
			string textpart = this.Text.Replace("+", "").Replace("*", "").Replace("/", ""); //mxd
			if(!allownegative) textpart = textpart.Replace("-", "");
			
			// No numbers left?
			if(textpart.Length == 0)
			{
				// Make the textbox empty
				this.Text = "";
			} 
			
			// Call base
			base.OnValidating(e);
		}
		
		// This checks if the number is relative
		public bool CheckIsRelative()
		{
			// Prefixed with +++, ---, ++, --, * or /?
			return ( (this.Text.Length > 3 && (this.Text.StartsWith("+++") || this.Text.StartsWith("---"))) || //mxd
				(this.Text.Length > 2 && (this.Text.StartsWith("++") || this.Text.StartsWith("--") || this.Text.StartsWith("**") || this.Text.StartsWith("//"))) || //mxd
				(this.Text.Length > 1 && (this.Text.StartsWith("*") || this.Text.StartsWith("/"))) ); //mxd
		}
		
		//mxd. This determines the result value
		public int GetResult(int original)
		{
			return GetResult(original, incrementstep++);
		}

		//mxd. This determines the result value
		public int GetResult(int original, int step)
		{
			string textpart = this.Text;
			
			// Strip prefixes
			textpart = textpart.Replace("+", "").Replace("-", "").Replace("*", "").Replace("/", ""); //mxd
			
			// Any numbers left?
			if(textpart.Length > 0)
			{
				int result;

				//mxd. Prefixed with +++?
				if(this.Text.StartsWith("+++"))
				{
					// Add number to original
					int.TryParse(textpart, out result);
					int newvalue = original + result * step;
					if (newvalue > maximumvalue) newvalue = maximumvalue;
					return newvalue;
				}
				//mxd. Prefixed with ---?
				if(this.Text.StartsWith("---"))
				{
					// Subtract number from original
					int.TryParse(textpart, out result);
					int newvalue = original - result * step;
					if (!allownegative && (newvalue < 0)) newvalue = 0;
					return newvalue;
				}
				// Prefixed with ++?
				if(this.Text.StartsWith("++"))
				{
					// Add number to original
					int.TryParse(textpart, out result);
					int newvalue = original + result;
					if (newvalue > maximumvalue) newvalue = maximumvalue;
					return newvalue;
				}
				// Prefixed with --?
				if(this.Text.StartsWith("--"))
				{
					// Subtract number from original
					int.TryParse(textpart, out result);
					int newvalue = original - result;
					if (!allownegative && (newvalue < 0)) newvalue = 0;
					return newvalue;
				}
				//mxd. Prefixed with *?
				if(this.Text.StartsWith("*")) 
				{
					// Multiply original by number
					float resultf;
					float.TryParse(textpart, NumberStyles.Float, CultureInfo.CurrentCulture, out resultf);
					int newvalue = (int)Math.Round(original * resultf);
					if (newvalue > maximumvalue) newvalue = maximumvalue;
					if (!allownegative && (newvalue < 0)) newvalue = 0;
					return newvalue;
				}
				//mxd. Prefixed with /?
				if(this.Text.StartsWith("/")) 
				{
					// Divide original by number
					float resultf;
					float.TryParse(textpart, NumberStyles.Float, CultureInfo.CurrentCulture, out resultf);
					if(resultf == 0) return original;
					int newvalue = (int)Math.Round(original / resultf);
					if (newvalue > maximumvalue) newvalue = maximumvalue;
					if (!allownegative && (newvalue < 0)) newvalue = 0;
					return newvalue;
				}

				//mxd. Return the new value
				if(!int.TryParse(this.Text, out result)) return original;
				if (result > maximumvalue) return maximumvalue;
				if (!allownegative && (result < 0)) return 0;
				return result;
			}

			// Nothing given, keep original value
			return original;
		}

		//mxd. This determines the result value
		public float GetResultFloat(float original)
		{
			return GetResultFloat(original, incrementstep++);
		}

		// This determines the result value
		public float GetResultFloat(float original, int step)
		{
			// Strip prefixes
			string textpart = this.Text.Replace("+", "").Replace("-", "").Replace("*", "").Replace("/", ""); //mxd;

			// Any numbers left?
			if(textpart.Length > 0)
			{
				float result;
				
				//mxd. Prefixed with +++?
				if(this.Text.StartsWith("+++"))
				{
					// Add number to original
					if(!float.TryParse(textpart, NumberStyles.Float, CultureInfo.CurrentCulture, out result)) result = 0;
					return original + result * step;
				}
				//mxd. Prefixed with ---?
				if(this.Text.StartsWith("---"))
				{
					// Subtract number from original
					if(!float.TryParse(textpart, NumberStyles.Float, CultureInfo.CurrentCulture, out result)) result = 0;
					float newvalue = original - result * step;
					if(!allownegative && (newvalue < 0)) newvalue = 0;
					return newvalue;
				}
				// Prefixed with ++?
				if(this.Text.StartsWith("++"))
				{
					// Add number to original
					if(!float.TryParse(textpart, NumberStyles.Float, CultureInfo.CurrentCulture, out result)) result = 0;
					return original + result;
				}
				// Prefixed with --?
				if(this.Text.StartsWith("--"))
				{
					// Subtract number from original
					if(!float.TryParse(textpart, NumberStyles.Float, CultureInfo.CurrentCulture, out result)) result = 0;
					float newvalue = original - result;
					if(!allownegative && (newvalue < 0)) newvalue = 0;
					return newvalue;
				}
				//mxd. Prefixed with *?
				if(this.Text.StartsWith("*")) 
				{
					// Multiply original by number
					if(!float.TryParse(textpart, NumberStyles.Float, CultureInfo.CurrentCulture, out result)) result = 0;
					float newvalue = original * result;
					if(!allownegative && (newvalue < 0)) newvalue = 0;
					return newvalue;
				}
				//mxd. Prefixed with /?
				if(this.Text.StartsWith("/")) 
				{
					// Divide original by number
					if(!float.TryParse(textpart, NumberStyles.Float, CultureInfo.CurrentCulture, out result) || result == 0) return original;
					float newvalue = (float)Math.Round(original / result, 3);
					if(!allownegative && (newvalue < 0)) newvalue = 0;
					return newvalue;
				}

				//mxd. Return the new value
				if(!float.TryParse(this.Text, NumberStyles.Float, CultureInfo.CurrentCulture, out result)) return original;
				if(!allownegative && (result < 0)) return 0;
				return result;
			}

			// Nothing given, keep original value
			return original;
		}

		//mxd
		public void UpdateTextboxStyle()
		{
			UpdateTextboxStyle(string.Empty);
		}

		//mxd
		public void UpdateTextboxStyle(string tip) 
		{
			this.ForeColor = (allowrelative ? SystemColors.HotTrack : SystemColors.WindowText);
			if(allowrelative)
			{
				tooltip.SetToolTip(this, "Use ++ or -- prefixes to change by given value." + Environment.NewLine +
					"Use +++ or --- prefixes to incrementally change by given value." + Environment.NewLine + 
					"Use * or / prefixes to multiply or divide by given value." + Environment.NewLine + tip);
			}
			else if(!string.IsNullOrEmpty(tip))
			{
				tooltip.SetToolTip(this, tip);
			}
			else
			{
				tooltip.RemoveAll();
			}
		}
		
		#endregion
	}
}
