using System;
using System.Text;

namespace Orciid.Core
{
	/// <summary>
	/// Calculates SoundEx values of strings
	/// </summary>
	/// <remarks>
	/// This class calculates the SoundEx value of a string,
	/// which can be used to compare two strings phonetically
	/// </remarks>
	public class SoundEx
	{
		const int DEFAULT_LENGTH = 4;
		// values for letters  ABCDEFGHIJKLMNOPQRSTUVWXYZ
		const string VALUES = "01230120022455012623010202";

		/// <summary>
		/// Calculate SoundEx value of a string
		/// </summary>
		/// <remarks>
		/// Returns the first 4 characters of the calculated SoundEx value.
		/// If the value would be less than 4 characters, it will be padded
		/// with zeroes.
		/// </remarks>
		/// <param name="s">The source string</param>
		/// <returns>The SoundEx value</returns>
		public static string Calculate(string s)
		{
			return Calculate(s, DEFAULT_LENGTH);
		}

		/// <summary>
		/// Calculate SoundEx value of a string
		/// </summary>
		/// <remarks>
		/// Returns the calculated SoundEx value, truncated or padded with zeroes to
		/// the desired length
		/// </remarks>
		/// <param name="s">The source string</param>
		/// <param name="length">The desired length of the SoundEx value</param>
		/// <returns>The SoundEx value</returns>
		public static string Calculate(string s, int length)
		{
			if (s == null || s.Length < 2)
				return "";
			StringBuilder values = new StringBuilder(s.Length);
			string word = s.ToUpper();
			bool first = true;
			char lastval = ' ';
			foreach (char letter in word)
			{
				int idx = (int)letter - (int)'A';
				if (idx >= 0 && idx < 26 && VALUES[idx] != lastval)
				{
					lastval = VALUES[idx];
					if (first)
					{
						values.Append(letter);
						first = false;
					}
					else if (lastval != '0')
						values.Append(lastval);
					if (values.Length == length)
						break;
				}
			}
			while (values.Length < length)
				values.Append('0');
			return values.ToString();
		}

        /// <summary>
        /// Compare strings by their SoundEx value
        /// </summary>
        /// <remarks>
        /// This method compares the SoundEx values of the given strings using
        /// <see cref="String.Compare(string, string)"/>
        /// </remarks>
        /// <param name="s1">The first string</param>
        /// <param name="s2">The second string</param>
        /// <returns>The result of <see cref="String.Compare(string, string)"/> on
        /// the SoundEx values</returns>
		public static int Compare(string s1, string s2)
		{
			return String.Compare(Calculate(s1), Calculate(s2));
		}

        /// <summary>
        /// Compare strings by their SoundEx value
        /// </summary>
        /// <remarks>
        /// This method compares the SoundEx values of the given strings using
        /// <see cref="String.Compare(string, string)"/>
        /// </remarks>
        /// <param name="s1">The first string</param>
        /// <param name="s2">The second string</param>
        /// <param name="length">The desired length of the SoundEx values</param>
        /// <returns>The result of <see cref="String.Compare(string, string)"/> on
        /// the SoundEx values</returns>
		public static int Compare(string s1, string s2, int length)
		{
			return String.Compare(Calculate(s1, length), Calculate(s2, length));
		}
	}
}
