#ifndef _XH_STRING_H_
#define _XH_STRING_H_

#include "xh_config.h"
#include "xh_core.h"

#define xh_str_equal2(p, c0, c1)                                        \
    ((((uint32_t *) (p))[0] & 0xffff) == ((c1 << 8) | c0))

#define xh_str_equal3(p, c0, c1, c2)                                    \
    ((((uint32_t *) (p))[0]  & 0xffffff) == ((c2 << 16) | (c1 << 8) | c0))

#define xh_str_equal4(p, c0, c1, c2, c3)                                \
    (*(uint32_t *) (p) == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0))

#define xh_str_equal5(p, c0, c1, c2, c3, c4)                            \
    (xh_str_equal4(p, c0, c1, c2, c3) && (p)[4] == c4)

#define xh_str_equal6(p, c0, c1, c2, c3, c4, c5)                        \
    (xh_str_equal4(p, c0, c1, c2, c3) && xh_str_equal2(&p[4], c4, c5))

#define xh_str_equal7(p, c0, c1, c2, c3, c4, c5, c6)                    \
    (xh_str_equal4(p, c0, c1, c2, c3) && xh_str_equal3(&p[4], c4, c5, c6))

#define xh_str_equal8(p, c0, c1, c2, c3, c4, c5, c6, c7)                \
    (xh_str_equal4(p, c0, c1, c2, c3) && xh_str_equal4(&p[4], c4, c5, c6, c7))

#define xh_str_equal9(p, c0, c1, c2, c3, c4, c5, c6, c7, c8)            \
    (xh_str_equal8(p, c0, c1, c2, c3, c4, c5, c6, c7) && (p)[8] == c8)

#define xh_str_equal10(p, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9)       \
    (xh_str_equal8(p, c0, c1, c2, c3, c4, c5, c6, c7) && xh_str_equal2(&p[8], c8, c9))

#define xh_str_equal11(p, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10)  \
    (xh_str_equal8(p, c0, c1, c2, c3, c4, c5, c6, c7) && xh_str_equal3(&p[8], c8, c9, c10))

#define xh_str_equal12(p, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11)\
    (xh_str_equal8(p, c0, c1, c2, c3, c4, c5, c6, c7) && xh_str_equal4(&p[8], c8, c9, c10, c11))

#define xh_str_equal13(p, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12)\
    (xh_str_equal8(p, c0, c1, c2, c3, c4, c5, c6, c7) && xh_str_equal5(&p[8], c8, c9, c10, c11, c12))


#define xh_strcmp(s1, s2)       strcmp((const char *) (s1), (const char *) (s2))
#define xh_strcasecmp(s1, s2)   strcasecmp((const char *) (s1), (const char *) (s2))
#define xh_strncmp(s1, s2, n)   strncmp((const char *) (s1), (const char *) (s2), (n))
#define xh_strlen(s)            strlen((const char *) (s))
#define xh_strcpy(d, s)         strcpy((char *) (d), (const char *) (s))
#define xh_strncpy(d, s, n)     strncpy((char *) (d), (const char *) (s), (n))

XH_INLINE xh_char_t *
xh_str_trim(xh_char_t *s, size_t *len)
{
    xh_char_t *end, ch;

    end = s + *len;

    while ((ch = *s++) == ' ' || ch =='\t' || ch == '\n' || ch == '\r');
    if (ch == '\0') {
        *len = 0;
        return s - 1;
    }

    s--;

    while (--end != s && ((ch = *end) == ' ' || ch =='\t' || ch == '\n' || ch == '\r'));

    *len = end - s + 1;

    return s;
}

XH_INLINE xh_char_t *
xh_str_copy(xh_char_t *dest, const xh_char_t *src, size_t n)
{
    dest[--n] = '\0';
    return XH_CHAR_CAST strncpy((char *) dest, (const char *) src, n);
}

XH_INLINE xh_char_t *
xh_str_range_copy(xh_char_t *dest, const xh_char_t *src, size_t l, size_t n)
{
    if (l < n) n = l + 1;
    dest[--n] = '\0';
    return XH_CHAR_CAST strncpy((char *) dest, (const char *) src, n);
}

XH_INLINE void
xh_memmove(xh_char_t *dest, const xh_char_t *src, size_t n)
{
    while (n--) *dest++ = *src++;
}

XH_INLINE xh_bool_t
xh_str_is_xml(xh_char_t *s)
{
    xh_char_t ch;

    while ((ch = *s++) == ' ' || ch =='\t' || ch == '\n' || ch == '\r');
    if (ch == '<') return TRUE;

    return FALSE;
}

#endif /* _XH_STRING_H_ */
