#ifndef VIENNA_RNA_PACKAGE_STRING_UTILS_H
#define VIENNA_RNA_PACKAGE_STRING_UTILS_H

#ifdef VRNA_WARN_DEPRECATED
# if defined(__clang__)
#  define DEPRECATED(func, msg) func __attribute__ ((deprecated("", msg)))
# elif defined(__GNUC__)
#  define DEPRECATED(func, msg) func __attribute__ ((deprecated(msg)))
# else
#  define DEPRECATED(func, msg) func
# endif
#else
# define DEPRECATED(func, msg) func
#endif

/**
 *  @file     ViennaRNA/utils/strings.h
 *  @ingroup  utils, string_utils
 *  @brief    General utility- and helper-functions for RNA sequence and structure strings used throughout the ViennaRNA Package
 */

/**
 *  @addtogroup   string_utils
 *  @{
 */

#include <stdarg.h>
#include <ViennaRNA/datastructures/basic.h>

/**
 * @brief Stringify a macro after expansion
 */
#define XSTR(s) STR(s)

/**
 * @brief Stringify a macro argument
 */
#define STR(s) #s

#ifndef FILENAME_MAX_LENGTH

/**
 *  @brief Maximum length of filenames that are generated by our programs
 *
 *  This definition should be used throughout the complete ViennaRNA package
 *  wherever a static array holding filenames of output files is declared.
 */
#define FILENAME_MAX_LENGTH   80

/**
 *  @brief Maximum length of id taken from fasta header for filename generation
 *
 *  this has to be smaller than FILENAME_MAX_LENGTH since in most cases,
 *  some suffix will be appended to the ID
 */
#define FILENAME_ID_LENGTH    42

#endif

#ifdef HAVE_CONFIG_H
#include <config.h>
#ifndef HAVE_STRDUP
char *
strdup(const char *s);


#endif
#endif

/**
 *  @brief Safely create a formatted string
 *
 *  This function is a safe implementation for creating a formatted character array,
 *  similar to @em sprintf.
 *  Internally, it uses the @em asprintf function if available to dynamically allocate
 *  a large enough character array to store the supplied content. If @em asprintf is
 *  not available, mimic it's behavior using @em vsnprintf.
 *
 *  @note The returned pointer of this function should always be passed to @em free() to
 *        release the allocated memory
 *
 *  @see vrna_strdup_vprintf(), vrna_strcat_printf()
 *
 *  @param  format  The format string (See also asprintf)
 *  @param  ...     The list of variables used to fill the format string
 *  @return         The formatted, null-terminated string, or NULL if something has gone wrong
 */
char *
vrna_strdup_printf(const char *format,
                   ...);


/**
 *  @brief Safely create a formatted string
 *
 *  This function is the @em va_list version of vrna_strdup_printf()
 *
 *  @note The returned pointer of this function should always be passed to @em free() to
 *        release the allocated memory
 *
 *  @see vrna_strdup_printf(), vrna_strcat_printf(), vrna_strcat_vprintf()
 *
 *  @param  format  The format string (See also asprintf)
 *  @param  argp    The list of arguments to fill the format string
 *  @return         The formatted, null-terminated string, or NULL if something has gone wrong
 */
char *
vrna_strdup_vprintf(const char  *format,
                    va_list     argp);


/**
 *  @brief Safely append a formatted string to another string
 *
 *  This function is a safe implementation for appending a formatted character array,
 *  similar to a cobination of @em strcat and @em sprintf.
 *  The function automatically allocates enough memory to store both, the previous
 *  content stored at @p dest and the appended format string. If the @p dest pointer
 *  is NULL, the function allocate memory only for the format string.
 *  The function returns the number of characters in the resulting string or -1
 *  in case of an error.
 *
 *  @see vrna_strcat_vprintf(), vrna_strdup_printf(), vrna_strdup_vprintf()
 *
 *  @param  dest    The address of a char *pointer where the formatted string is to be appended
 *  @param  format  The format string (See also sprintf)
 *  @param  ...     The list of variables used to fill the format string
 *  @return         The number of characters in the final string, or -1 on error
 */
int
vrna_strcat_printf(char       **dest,
                   const char *format,
                   ...);


/**
 *  @brief Safely append a formatted string to another string
 *
 *  This function is the @em va_list version of vrna_strcat_printf()
 *
 *  @see vrna_strcat_printf(), vrna_strdup_printf(), vrna_strdup_vprintf()
 *
 *  @param  dest    The address of a char *pointer where the formatted string is to be appended
 *  @param  format  The format string (See also sprintf)
 *  @param  args    The list of argument to fill the format string
 *  @return         The number of characters in the final string, or -1 on error
 */
int
vrna_strcat_vprintf(char        **dest,
                    const char  *format,
                    va_list     args);


/**
 *  @brief Trim only characters leading the string
 *  @see vrna_strtrim()
 */
#define VRNA_TRIM_LEADING       1U

/**
 *  @brief Trim only characters trailing the string
 *  @see vrna_strtrim()
 */
#define VRNA_TRIM_TRAILING      2U

/**
 *  @brief Trim only characters within the string
 *  @see vrna_strtrim()
 */
#define VRNA_TRIM_IN_BETWEEN    4U

/**
 *  @brief  Replace remaining characters after trimming with the first delimiter in list
 *  @see  vrna_strtrim()
 */
#define VRNA_TRIM_SUBST_BY_FIRST  8U

/**
 *  @brief Default settings for trimming, i.e. trim leading and trailing
 *  @see vrna_strtrim()
 */
#define VRNA_TRIM_DEFAULT       ( VRNA_TRIM_LEADING | VRNA_TRIM_TRAILING )

/**
 *  @brief Trim characters anywhere in the string
 *  @see vrna_strtrim()
 */
#define VRNA_TRIM_ALL           ( VRNA_TRIM_DEFAULT | VRNA_TRIM_IN_BETWEEN )

/**
 *  @brief Trim a string by removing (multiple) occurences of a particular character
 *
 *  This function removes (multiple) consecutive occurences of a set of
 *  characters (@p delimiters) within an input string. It may be used to remove
 *  leading and/or trailing whitespaces or to restrict the maximum number of
 *  consecutive occurences of the delimiting characters @p delimiters. Setting
 *  @p keep=0 removes all occurences, while other values reduce multiple
 *  consecutive occurences to at most @p keep delimiters. This might be useful
 *  if one would like to reduce multiple whitespaces to a single one, or
 *  to remove empty fields within a comma-separated value string.
 *
 *  The parameter @p delimiters may be a pointer to a 0-terminated char string
 *  containing a set of any ASCII character. If @em NULL is passed as delimiter
 *  set or an empty char string, all whitespace characters are trimmed. The
 *  @p options parameter is a bit vector that specifies which part of the string
 *  should undergo trimming. The implementation distinguishes the leading
 *  (#VRNA_TRIM_LEADING), trailing (#VRNA_TRIM_TRAILING), and in-between
 *  (#VRNA_TRIM_IN_BETWEEN) part with respect to the delimiter set. Combinations
 *  of these parts can be specified by using logical-or operator.
 *
 *  The following example code removes all leading and trailing
 *  whitespace characters from the input string:
 *  @code{.c}
 *  char          string[20]  = "  \t blablabla   ";
 *  unsigned int  r           = vrna_strtrim(&(string[0]),
 *                                           NULL,
 *                                           0,
 *                                           VRNA_TRIM_DEFAULT);
 *  @endcode
 *
 *  @note The delimiter always consists of a single character from the
 *        set of characters provided. In case of alternative delimiters and non-null
 *        @p keep parameter, the first @p keep delimiters are preserved within the
 *        string. Use #VRNA_TRIM_SUBST_BY_FIRST to substitute all remaining
 *        delimiting characters with the first from the @p delimiters list.
 *
 *  @see  VRNA_TRIM_LEADING, VRNA_TRIM_TRAILING, VRNA_TRIM_IN_BETWEEN,
 *        VRNA_TRIM_SUBST_BY_FIRST, VRNA_TRIM_DEFAULT, VRNA_TRIM_ALL
 *
 *  @param  string      The '\0'-terminated input string to trim
 *  @param  delimiters  The delimiter characters as 0-terminated char array (or @em NULL)
 *  @param  keep        The maximum number of consecutive occurences of the delimiter in the output string
 *  @param  options     The option bit vector specifying the mode of operation
 *  @return             The number of delimiters removed from the string
 */
unsigned int
vrna_strtrim(char         *string,
             const char   *delimiters,
             unsigned int keep,
             unsigned int options);


/**
 *  @brief Split a string into tokens using a delimiting character
 *
 *  This function splits a string into an array of strings using a single
 *  character that delimits the elements within the string. The default
 *  delimiter is the ampersand @c '&' and will be used when @c NULL is
 *  passed as a second argument. The returned list is NULL terminated, i.e.
 *  the last element is @c NULL. If the delimiter is not found, the returned
 *  list contains exactly one element: the input string.
 *
 *  For instance, the following code:
 *
 * @code{.c}
 * char **tok = vrna_strsplit("GGGG&CCCC&AAAAA", NULL);
 *
 * for (char **ptr = tok; *ptr; ptr++) {
 *   printf("%s\n", *ptr);
 *   free(*ptr);
 * }
 * free(tok);
 * @endcode
 *  produces this output:
 *
 * @verbatim
 * GGGG
 * CCCC
 * AAAAA
 * @endverbatim
 *  and properly free's the memory occupied by the returned element array.
 *
 *  @note This function internally uses @em strtok_r() and is therefore
 *        considered to be thread-safe. Also note, that it is the users responsibility
 *        to free the memory of the array and that of the individual element strings!<br>
 *        In case the input string consists of consecutive delimiters, starts
 *        or ends with one or multiple delimiters, empty strings are produced in the
 *        output list, indicating the empty fields of data resulting from the split.
 *        Use vrna_strtrim() prior to a call to this function to remove any leading,
 *        trailing, or in-between empty fields.
 *
 *  @see vrna_strtrim()
 *
 *  @param  string    The input string that should be split into elements
 *  @param  delimiter The delimiting character. If @c NULL, the delimiter is @c "&"
 *  @return           A @c NULL terminated list of the elements in the string
 */
char **
vrna_strsplit(const char  *string,
              const char  *delimiter);


char *
vrna_strjoin(const char **strings,
             const char *delimiter);


/**
 *  @brief Create a random string using characters from a specified symbol set
 *
 *  @param l        The length of the sequence
 *  @param symbols  The symbol set
 *  @return         A random string of length 'l' containing characters from the symbolset
 */
char *
vrna_random_string(int        l,
                   const char symbols[]);


/**
 *  @brief Calculate hamming distance between two sequences
 *
 *  @param s1   The first sequence
 *  @param s2   The second sequence
 *  @return     The hamming distance between s1 and s2
 */
int
vrna_hamming_distance(const char  *s1,
                      const char  *s2);


/**
 *  @brief Calculate hamming distance between two sequences up to a specified length
 *
 *  This function is similar to vrna_hamming_distance() but instead of comparing both sequences
 *  up to their actual length only the first 'n' characters are taken into account
 *  @param  s1  The first sequence
 *  @param  s2  The second sequence
 *  @param  n   The length of the subsequences to consider (starting from the 5' end)
 *  @return     The hamming distance between s1 and s2
 */
int
vrna_hamming_distance_bound(const char  *s1,
                            const char  *s2,
                            int         n);


/**
 *  @brief Convert an input sequence (possibly containing DNA alphabet characters) to RNA alphabet
 *
 *  This function substitudes <i>T</i> and <i>t</i> with <i>U</i> and <i>u</i>, respectively
 *
 *  @param sequence The sequence to be converted
 */
void
vrna_seq_toRNA(char *sequence);


/**
 *  @brief Convert an input sequence to uppercase
 *
 *  @param sequence The sequence to be converted
 */
void
vrna_seq_toupper(char *sequence);


/**
 *  @brief  Reverse a string in-place
 *
 *  This function reverses a character string in the form of
 *  an array of characters in-place, i.e. it changes the input
 *  parameter.
 *
 *  @post After execution, the input @p sequence consists of the
 *        reverse string prior to the execution.
 *
 *  @see vrna_DNA_complement()
 *
 *  @param  sequence  The string to reverse
 */
void
vrna_seq_reverse(char *sequence);


/**
 *  @brief  Retrieve a DNA sequence which resembles the complement of the input sequence
 *
 *  This function returns a mew DNA string which is the complement
 *  of the input, i.e. the nucleotide letters `A`,`C`,`G`, and `T`
 *  are substituted by their complements `T`,`G`,`C`, and `A`, respectively.
 *
 *  Any characters not belonging to the alphabet of the 4 canonical
 *  bases of DNA are not altered.
 *
 *  @note This function also handles lower-case input sequences and
 *        treats `U` of the RNA alphabet equally to `T`
 *
 *  @see vrna_seq_reverse()
 *
 *  @param  sequence  the input DNA sequence
 *  @return           The complement of the input DNA sequence
 */
char *
vrna_DNA_complement(const char *sequence);


/**
 *  @brief  Remove gap characters from a nucleotide sequence
 *
 *  @param  sequence  The original, null-terminated nucleotide sequence
 *  @return           A copy of the input sequence with all gap characters removed
 */
char *
vrna_seq_ungapped(const char *sequence);


/**
 *  @brief Add a separating '&' character into a string according to cut-point position
 *
 *  If the cut-point position is less or equal to zero, this function just
 *  returns a copy of the provided string. Otherwise, the cut-point character
 *  is set at the corresponding position
 *
 *  @param  string    The original string
 *  @param  cp        The cut-point position
 *  @return           A copy of the provided string including the cut-point character
 */
char *
vrna_cut_point_insert(const char  *string,
                      int         cp);


/**
 *  @brief  Remove a separating '&' character from a string
 *
 *  This function removes the cut-point indicating '&' character from a string
 *  and memorizes its position in a provided integer variable. If not '&' is
 *  found in the input, the integer variable is set to -1. The function returns
 *  a copy of the input string with the '&' being sliced out.
 *
 *  @param  string  The original string
 *  @param  cp      The cut-point position
 *  @return         A copy of the input string with the '&' being sliced out
 */
char *
vrna_cut_point_remove(const char  *string,
                      int         *cp);



/**
 *  @brief  Find (all) occurrences of a character within a string
 *
 *  @brief  string  The C string to be scanned
 *  @brief  c       The character to be searched for
 *  @brief  n       The maximum number of occurences to search for (or 0 for all occurrences)
 *  @return         An 1-based array of positions(0-based) or @b NULL on error. Position 0 specifies the number of occurrences found.
 */
size_t *
vrna_strchr(const char  *string,
            int          c,
            size_t       n);


/**
 *  @}
 */

#ifndef VRNA_DISABLE_BACKWARD_COMPATIBILITY

/**
 *  @brief Convert an input sequence to uppercase
 *  @deprecated   Use vrna_seq_toupper() instead!
 */
DEPRECATED(void
           str_uppercase(char *sequence),
           "Use vrna_seq_toupper() instead");

/**
 *  @brief Convert a DNA input sequence to RNA alphabet
 *
 *  @deprecated Use vrna_seq_toRNA() instead!
 */
DEPRECATED(void
           str_DNA2RNA(char *sequence),
           "Use vrna_seq_toRNA() instead");

/**
 *  @brief Create a random string using characters from a specified symbol set
 *
 *  @deprecated Use vrna_random_string() instead!
 */
DEPRECATED(char *random_string(int l,
                               const char symbols[]),
           "Use vrna_random_string() instead");

/**
 *  @brief Calculate hamming distance between two sequences
 *
 *  @deprecated Use vrna_hamming_distance() instead!
 */
DEPRECATED(int
           hamming(const char *s1,
                   const char *s2),
           "Use vrna_hamming_distance() instead");

/**
 *  @brief Calculate hamming distance between two sequences up to a specified length
 *
 *  @deprecated Use vrna_hamming_distance_bound() instead!
 */
DEPRECATED(int
           hamming_bound(const char *s1,
                         const char *s2,
                         int        n),
           "Use vrna_hamming_distance_bound() instead");

#endif

#endif
