//-----------------------------------------------------------------------------
// Copyright (c) 2013 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

/*!
   @defgroup StringFunctions String
      @ingroup TorqueScriptFunctions
      @{
*/

/*! Use the strcmp function to do a lexicographic case sensitive string comparison between string1 and string2.
    @param string1 String to be compared to string2.
    @param string2 String to be compared to string1.
    @return Returns a numeric value: <-1 (string1 is less than string2, including case), 0 (string1 is equal to string2, including case), 1 (string1 is greater than string2, including case)>.
    @sa see stricmp, strstr
*/
ConsoleFunctionWithDocs(strcmp, ConsoleInt, 3, 3, ( string1 , string2 ))
{
   TORQUE_UNUSED( argc );
   return dStrcmp(argv[1], argv[2]);
}

/*! Use the stricmp function to do a lexicographic case in-sensitive string comparison between string1 and string2.
    @param string1 String to be compared to string2.
    @param string2 String to be compared to string1.
    @return Returns a numeric value: <-1 (string1 is less than string2, ignoring case), 0 (string1 is equal to string2, ignoring case), 1 (string1 is greater than string2, ignoring case)>.
    @sa see strcmp, strstr
*/
ConsoleFunctionWithDocs(stricmp, ConsoleInt, 3, 3, ( string1 , string2 ))
{
   TORQUE_UNUSED( argc );
   return dStricmp(argv[1], argv[2]);
}

/*! Use the strlen function to determine how many characters there are in string.
    @param string The string to count characters for.
    @return Returns the number of characters in string, or 0 if string is invalid or a NULL string
*/
ConsoleFunctionWithDocs(strlen, ConsoleInt, 2, 2, ( string ))
{
   TORQUE_UNUSED( argc );
   return dStrlen(argv[1]);
}

/*! Use the strstr function to locate the first instance of searchString in sourceString.
    @param sourceString The string in which to search for searchString.
    @param searchString The string for which to search for in sourceString.
    @return Returns a numeric character index representing the position in sourceString at which searchString was found, or -1 to indicate that no instance of searchString was found.
    @sa strpos
*/
ConsoleFunctionWithDocs(strstr, ConsoleInt, 3, 3, ( sourceString , searchString ))
{
   TORQUE_UNUSED( argc );
   // returns the start of the sub string argv[2] in argv[1]
   // or -1 if not found.

   const char *retpos = dStrstr(argv[1], argv[2]);
   if(!retpos)
      return -1;
   return (S32)(retpos - argv[1]);
}

/*! Use the strPos function to locate the first instance of searchString in sourceString, starting at character 0, or at an optional offset.
    @param sourceString The string in which to search for searchString.
    @param searchString The string for which to search for in sourceString.
    @param offset An optional non-negative integer value representing the character offset within sourceString at which to begin the search.
    @return Returns a numeric character index representing the postion in sourceString at which searchString was found, or -1 to indicate that no instance of searchString was found.
    @sa strstr
*/
ConsoleFunctionWithDocs(strpos, ConsoleInt, 3, 4, ( sourceString , searchString, [offset]? ))
{
   S32 start = 0;
   if(argc == 4)
      start = dAtoi(argv[3]);
   U32 sublen = dStrlen(argv[2]);
   U32 strlen = dStrlen(argv[1]);
   if(start < 0)
      return -1;
   if(sublen + start > strlen)
      return -1;
   for(; start + sublen <= strlen; start++)
      if(!dStrncmp(argv[1] + start, argv[2], sublen))
         return start;
   return -1;
}

/*! Use the ltrim function to strip the leading white space from sourceString.
    White space is any character in this set: spaces, TABs, and NULL strings.
    @param sourceString The string to be trimmed.
    @return Returns sourceString with all the leading white spaces removed.
    @sa stripChars, stripMLControlChars, stripTrailingSpaces, rtrim, trim
*/
ConsoleFunctionWithDocs(ltrim, ConsoleString,2,2, ( sourceString ))
{
   TORQUE_UNUSED( argc );
   const char *ret = argv[1];
   while(*ret == ' ' || *ret == '\n' || *ret == '\t')
      ret++;
   return ret;
}

/*! Use the rtrim function to strip the trailing white space from sourceString.
    White space is any character in this set: spaces, TABs, and NULL strings.
    @param sourceString The string to be trimmed.
    @return Returns sourceString with all the trailing white spaces removed.
    @sa stripChars, stripMLControlChars, stripTrailingSpaces, ltrim, trim
*/
ConsoleFunctionWithDocs(rtrim, ConsoleString,2,2, ( sourceString ))
{
   TORQUE_UNUSED( argc );
   S32 firstWhitespace = 0;
   S32 pos = 0;
   const char *str = argv[1];
   while(str[pos])
   {
      if(str[pos] != ' ' && str[pos] != '\n' && str[pos] != '\t')
         firstWhitespace = pos + 1;
      pos++;
   }
   char *ret = Con::getReturnBuffer(firstWhitespace + 1);
   dStrncpy(ret, argv[1], firstWhitespace);
   ret[firstWhitespace] = 0;
   return ret;
}

/*! Use the trim function to strip the leading and trailing white space from sourceString.
    White space is any character in this set: spaces, TABs, and NULL strings.
    @param sourceString The string to be trimmed.
    @return Returns sourceString with all the leading and trailing white spaces removed.
    @sa stripChars, stripMLControlChars, stripTrailingSpaces, ltrim, rtrim
*/
ConsoleFunctionWithDocs(trim, ConsoleString,2,2, ( sourceString ))
{
   TORQUE_UNUSED( argc );
   const char *ptr = argv[1];
   while(*ptr == ' ' || *ptr == '\n' || *ptr == '\t')
      ptr++;
   S32 firstWhitespace = 0;
   S32 pos = 0;
   const char *str = ptr;
   while(str[pos])
   {
      if(str[pos] != ' ' && str[pos] != '\n' && str[pos] != '\t')
         firstWhitespace = pos + 1;
      pos++;
   }
   char *ret = Con::getReturnBuffer(firstWhitespace + 1);
   dStrncpy(ret, ptr, firstWhitespace);
   ret[firstWhitespace] = 0;
   return ret;
}

/*! Use the stripChars function to remove chars from sourceString.
    @param sourceString The string to be modified.
    @param chars The character or characters to search for and remove.
    @return Returns a copy of sourceString, from which all instances of chars have been removed. This may be the original sourceString, if chars was not found.
    @sa stripMLControlChars, stripTrailingSpaces
*/
ConsoleFunctionWithDocs(stripChars, ConsoleString, 3, 3, ( sourceString , chars ))
{
   TORQUE_UNUSED( argc );
   char* ret = Con::getReturnBuffer( dStrlen( argv[1] ) + 1 );
   dStrcpy( ret, argv[1] );
   U32 pos = dStrcspn( ret, argv[2] );
   while ( pos < dStrlen( ret ) )
   {
      dStrcpy( ret + pos, ret + pos + 1 );
      pos = dStrcspn( ret, argv[2] );
   }
   return( ret );
}

/*! 
    remove TorqueML color codes from the string.
    @param stringtoString The string from which to remove TorqueML color codes
    @return A string consisting of the original string minus color codes
*/
ConsoleFunctionWithDocs(stripColorCodes, ConsoleString, 2,2, (stringtoStrip))
{
   char* ret = Con::getReturnBuffer( dStrlen( argv[1] ) + 1 );
   dStrcpy(ret, argv[1]);
   Con::stripColorChars(ret);
   return ret;
}

/*! Use the strlwr function to convert all alpha characters in sourceString to lower-case equivalents.
    @param sourceString The string to be modified.
    @return Returns a copy of sourceString in which all upper-case characters have been converted to lower-case letters.
    @sa strupr
*/
ConsoleFunctionWithDocs(strlwr, ConsoleString,2,2, ( sourceString ))
{
   TORQUE_UNUSED( argc );
   char *ret = Con::getReturnBuffer(dStrlen(argv[1]) + 1);
   dStrcpy(ret, argv[1]);
   return dStrlwr(ret);
}

/*! Use the strupr function to convert all alpha characters in sourceString to upper-case equivalents.
    @param sourceString The string to be modified.
    @return Returns a copy of sourceString in which all lower-case characters have been converted to upper-case letters.
    @sa strlwr
*/
ConsoleFunctionWithDocs(strupr, ConsoleString,2,2, ( sourceString ))
{
   TORQUE_UNUSED( argc );
   char *ret = Con::getReturnBuffer(dStrlen(argv[1]) + 1);
   dStrcpy(ret, argv[1]);
   return dStrupr(ret);
}

/*! Use the strchr function to extract a sub-string of sourceString, where the sub-string is equal to the first occurence of char in sourceString followed by the remainder of sourceString.
    @param sourceString The string from which to extract a sub-string.
    @param char The character to search for in sourceString.
    @return Returns a string composed of first instance of char in sourceString, and all of the characters after it. If char is not found, a NULL string is returned.
    @sa getSubStr
*/
ConsoleFunctionWithDocs(strchr, ConsoleString,3,3, ( sourceString , char ))
{
   TORQUE_UNUSED( argc );
   const char *ret = dStrchr(argv[1], argv[2][0]);
   return ret ? ret : "";
}

/*! strrchr searches the sourceString for the last occurance of the giuven char
    @param sourceString The string to search
    @return Either a string consisting of the given string from the last occurance of the given char on or an empty string if not found
*/
ConsoleFunctionWithDocs(strrchr, ConsoleString,3,3, ( sourceString , char ))
{
   TORQUE_UNUSED( argc );
   const char *ret = dStrrchr(argv[1], argv[2][0]);
   return ret ? ret : "";
}

/*! Use the strreplace function to replace every instance of from in sourceString with to.
    This function is case-sensitive and only does exact matching
    @param sourceString The string to do replacement operations on.
    @param from The old value to be replaced.
    @param to The new value to replace old values with.
    @return Returns a new version of sourceString in which every instance of the value in from was replaced with the value in to.
*/
ConsoleFunctionWithDocs(strreplace, ConsoleString, 4, 4, ( sourceString , from , to ))
{
   TORQUE_UNUSED( argc );
   S32 fromLen = dStrlen(argv[2]);
   if(!fromLen)
      return argv[1];

   S32 toLen = dStrlen(argv[3]);
   S32 count = 0;
   const char *scan = argv[1];
   while(scan)
   {
      scan = dStrstr(scan, argv[2]);
      if(scan)
      {
         scan += fromLen;
         count++;
      }
   }
   char *ret = Con::getReturnBuffer(dStrlen(argv[1]) + 1 + (toLen - fromLen) * count);
   U32 scanp = 0;
   U32 dstp = 0;
   for(;;)
   {
      const char *scan = dStrstr(argv[1] + scanp, argv[2]);
      if(!scan)
      {
         dStrcpy(ret + dstp, argv[1] + scanp);
         return ret;
      }
      U32 len = (U32)(scan - (argv[1] + scanp));
      dStrncpy(ret + dstp, argv[1] + scanp, len);
      dstp += len;
      dStrcpy(ret + dstp, argv[3]);
      dstp += toLen;
      scanp += len + fromLen;
   }
   return ret;
}

/*! Use the getSubStr function to get a sub-string of sourceString, starting at character index start and ending at character index start + count, or the end-of-string, which ever comes first.
    If start + count is greater than the length of sourceString, the extraction will return a string shorter than count.
    @param sourceString The string from which to extract a sub-string.
    @param start The character index at which the extraction starts.
    @param count The length of the sub-string to extract.
    @return Returns a string made up of the character at start in sourceString and ending at the end of the original sourceString, or start + count, whichever comes first.
    @sa strchr
*/
ConsoleFunctionWithDocs(getSubStr, ConsoleString, 4, 4, ( sourceString , start , count ))
{
   TORQUE_UNUSED( argc );
   // Returns the substring of argv[1], starting at argv[2], and continuing
   //  to either the end of the string, or argv[3] characters, whichever
   //  comes first.
   //
   S32 startPos   = dAtoi(argv[2]);
   S32 desiredLen = dAtoi(argv[3]);
   if (startPos < 0 || desiredLen < 0) {
      Con::errorf(ConsoleLogEntry::Script, "getSubStr(...): error, starting position and desired length must be >= 0: (%d, %d)", startPos, desiredLen);

      return "";
   }

   S32 baseLen = dStrlen(argv[1]);
   if (baseLen < startPos)
      return "";

   U32 actualLen = desiredLen;
   if (startPos + desiredLen > baseLen)
      actualLen = baseLen - startPos;

   char *ret = Con::getReturnBuffer(actualLen + 1);
   dStrncpy(ret, argv[1] + startPos, actualLen);
   ret[actualLen] = '\0';

   return ret;
}

// Used?
/*! Removes all spaces after the final 
    @param string from which to remove trailing spaces
    @return the source string minus trailing spaces
*/
ConsoleFunctionWithDocs( stripTrailingSpaces, ConsoleString, 2, 2, ( string ))
{
   TORQUE_UNUSED( argc );
   S32 temp = S32(dStrlen( argv[1] ));
   if ( temp )
   {
      while ( ( argv[1][temp - 1] == ' ' || argv[1][temp - 1] == '_' ) && temp >= 1 )
         temp--;

      if ( temp )
      {
         char* returnString = Con::getReturnBuffer( temp + 1 );
         dStrncpy( returnString, argv[1], U32(temp) );
         returnString[temp] = '\0';
         return( returnString );			
      }
   }

   return( "" );	
}

/*! @} */ // group StringFunctions