/******************************************************************************
 Snippet: Soundex parser.
 Author:  Richard Woolcock (aka KaVir).
 Date:    20th December 2000.
 ******************************************************************************
 This code is copyright (C) 2000 by Richard Woolcock.  It may be used and
 distributed freely, as long as you don't remove this copyright notice.
 ******************************************************************************/

/******************************************************************************
 Required library files.
 ******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "merc.h"

/******************************************************************************
 Local literals
 ******************************************************************************/

#define KEY_SIZE 4 /* Size of the soundex key */

/******************************************************************************
 Local operation prototypes.
 ******************************************************************************/

static char LetterConversion ( char chLetter );

/******************************************************************************
 Global operations.
 ******************************************************************************/

/* Function: GetSoundexKey
 *
 * This function determines a soundex key from the string argument and returns 
 * the address of the key (which is stored in a static variable).  Because the 
 * most common use of the soundex key is to compare it with _another_ soundex 
 * key, this function uses two internal storage buffers, which are alternated 
 * between every time the function is called.
 *
 * The function takes one parameter, as follows:
 *
 * szTxt: A pointer to the string from which the soundex key is calculated.
 *
 * The return value is a pointer to the soundex key string.
 */
char *GetSoundexKey( const char *szTxt )
{
   int iOldIndex = 0; /* Loop index for the old (szTxt) string */
   int iNewIndex = 0; /* Loop index for the new (s_a_chSoundex) string */
   static char s_a_chSoundex[2][KEY_SIZE+1]; /* Stores the new string */
   static unsigned iSoundex; /* Determines which s_a_chSoundex is used */

   iSoundex++; /* Switch to the other s_a_chSoundex array */

   s_a_chSoundex[iSoundex%2][0] = '\0'; /* Clear any previous data */

   /* Copy the first character without conversion */
   if ( ( s_a_chSoundex[iSoundex%2][iNewIndex++] = tolower(szTxt[iOldIndex++]) ) )
   {
      do /* Loop through szTxt */
      {
         char chLetter; /* Stores the soundex value of a letter */

         /* Double/triple/etc letters are treated as single letters */
         while ( tolower(szTxt[iOldIndex]) == tolower(szTxt[iOldIndex+1]) )
         {
            iOldIndex++;
            continue;
         }

         /* Convert the letter into its soundex value and store it */
         chLetter = LetterConversion((char)tolower(szTxt[iOldIndex]));

         /* Ignore NUL and 0 characters and only store KEY_SIZE characters */
         if ( chLetter != '\0' && chLetter != '0' && iNewIndex < KEY_SIZE )
         {
            /* Store the soundex value */
            s_a_chSoundex[iSoundex%2][iNewIndex++] = chLetter;
         }
      }
      while ( szTxt[iOldIndex++] != '\0' );

      /* If less than KEY_SIZE characters were copied, buffer with zeros */
      while ( iNewIndex < KEY_SIZE )
      {
         s_a_chSoundex[iSoundex%2][iNewIndex++] = '0';
      }

      /* Add the NUL terminator to the end of the soundex string */
      s_a_chSoundex[iSoundex%2][iNewIndex] = '\0';
   }

   /* Return the address of the soundex string */
   return ( s_a_chSoundex[iSoundex%2] );
}

/* Function: SoundexMatch
 *
 * This function compares two soundex keys and returns a percentage match.
 *
 * The function takes two parameters, as follows:
 *
 * szFirst:  A pointer to the first soundex key.
 * szSecond: A pointer to the second soundex key.
 *
 * The return value is an integer which contains the percentage match.
 */
int SoundexMatch( char *szFirst, char *szSecond )
{
   int iMatch = 0; /* Number of matching characters found */
   int iMax   = 0; /* Total number of characters compared */

   /* Make sure that both strings are of the correct size */
   if ( strlen( szFirst ) == KEY_SIZE && strlen( szSecond ) == KEY_SIZE )
   {
      int i; /* Loop counter */

      /* Loop through both strings */
      for ( i = 0; i < KEY_SIZE; i++ )
      {
         /* If either of the values are not NUL */
         if ( szFirst[i] != '0' || szSecond[i] != '0' )
         {
            iMax++; /* Increment the maximum */
         }

         /* If BOTH values are not NUL */
         if ( szFirst[i] != '0' && szSecond[i] != '0' )
         {
            /* Check for a match */
            if ( szFirst[i] == szSecond[i] )
            {
               iMatch++; /* A match was found */
            }
         }
      }
   }

   /* Return the percentage match */
   return ( iMatch * 100 / iMax );
}

/******************************************************************************
 Local operations.
 ******************************************************************************/

/* Function: LetterConversion
 *
 * This function converts a single letter into it's appropriate soundex value.
 *
 * The function takes one parameter, as follows:
 *
 * chLetter: The letter to be converted.
 *
 * The return value is a single character which contains the converted value.
 */
static char LetterConversion( char chLetter )
{
   const char * kszSoundexData = "01230120022455012623010202";
   char chResult; /* Store the soundex value, or NUL */

   if ( islower(chLetter) )
   {
      /* Determine the soundex value associated with the letter */
      chResult = kszSoundexData[ (chLetter - 'a') ];
   }
   else /* it's not a lowercase letter */
   {
      /* NUL means there is no associated soundex value */
      chResult = '\0';
   }

   /* Return the soundex value, or NUL if there isn't one */
   return ( chResult );
}