/**
 * \file    string.c
 * \brief   String utitilies of system adaptation layer
 *
 * \par     Copyright (c) 2018 UWE Technologies Corp., Ltd. All Rights Reserved.
 * \author  James
 * \date    2018-03-05
 * \version 
 * 
 * \history 
 *  Date        Note
 *  2018-03-05  Original Creation
 * 
 */

#include "string.h"
#include "ctype.h"
#include "sync.h"


/* memcmp():
 * Compare n bytes:  s1>s2: >0  s1==s2: 0  s1<s2: <0
 */
int memcmp(const void *str1, const void *str2, register int n)
{
    u8_t *s1 = (u8_t *)str1;
    u8_t *s2 = (u8_t *)str2;

    while (n--)
    {
        if (*s1 != *s2)
        {
            return *s1 - *s2;
        }
        s1++;
        s2++;
    }
    return 0;
}

/* strcmp():
 * Compare strings:  s1>s2: >0  s1==s2: 0  s1<s2: <0
 */
int strcmp(const char *s1, const char *s2)
{
    if(s1 == s2)
        return(0);
    while(*s1 == *s2++)
        if(*s1++ == '\0')
            return(0);
    return(*s1 - *--s2);
}

/* strncmp():
 * Compare strings (at most n bytes)
 *  returns: s1>s2; >0  s1==s2; 0  s1<s2; <0
 */
int strncmp(const char *s1, const char *s2, register int n)
{
    if(s1 == s2)
        return(0);
    while(--n >= 0 && *s1 == *s2++)
        if(*s1++ == '\0')
            return(0);
    return((n < 0)? 0: (*s1 - *--s2));
}

/**
 * Compares the first n bytes of s1 and s2 in case insensitive 
 * and returns a value indicating their relationship
 *
 *
 * \param s1 string1 to be compared
 * \param s2 string2 to be compared
 * \param n
 * \return s1>s2: >0  s1==s2: 0  s1<s2: <0
 *
 * \see
 */
int strcasecmp(const char *s1, const char * s2)
{
    if(s1 == s2)
        return(0);

    while(tolower(*s1) == tolower(*s2)) {
        s2++;
        if(*s1++ == '\0')
            return(0);
    }
    return(tolower(*s1) - tolower(*(s2-1)));
}

/**
 * Compares the first n bytes of s1 and s2 in case insensitive 
 * and returns a value indicating their relationship
 *
 *
 * \param s1 string1 to be compared
 * \param s2 string2 to be compared
 * \param n
 * \return s1>s2: >0  s1==s2: 0  s1<s2: <0
 *
 * \see
 */
int strncasecmp(const char *s1, const char *s2, register int n)
{
    int c1, c2;
    do {
        c1 = tolower(*s1);
        s1++;
        c2 = tolower(*s2);
        s2++;
    } while ((--n > 0) && c1 == c2 && c1 != 0);
    return c1 - c2; 
} 

/* Nonzero if either X or Y is not aligned on a "long" boundary.  */
#define UNALIGNED(X, Y) \
  (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1)))

/* How many bytes are copied each iteration of the 4X unrolled loop.  */
#define BIGBLOCKSIZE    (sizeof (long) << 2)

/* How many bytes are copied each iteration of the word copy loop.  */
#define LITTLEBLOCKSIZE (sizeof (long))

/* Threshhold for punting to the byte copier.  */
#define TOO_SMALL(LEN)  ((LEN) < BIGBLOCKSIZE)

void *
memcpy2 (void * dst0,
    const void * src0,
    unsigned long len0)
{
#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
    char *dst = (char *) dst0;
    char *src = (char *) src0;

    void *save = dst0;

#if SYS_FREERTOS_ENABLED
    sync_enter_critical();
#endif /* SYS_FREERTOS_ENABLED */

    while (len0--)
    {
        *dst++ = *src++;
    }

#if SYS_FREERTOS_ENABLED
    sync_exit_critical();
#endif /* SYS_FREERTOS_ENABLED */

    return save;
#else
    char *dst = dst0;
    const char *src = src0;
    long *aligned_dst;
    const long *aligned_src;

#if SYS_FREERTOS_ENABLED
    sync_enter_critical();
#endif /* SYS_FREERTOS_ENABLED */

    /* If the size is small, or either SRC or DST is unaligned,
       then punt into the byte copy loop.  This should be rare.  */
    if (!TOO_SMALL(len0) && !UNALIGNED (src, dst))
    {
        aligned_dst = (long*)dst;
        aligned_src = (long*)src;

        /* Copy 4X long words at a time if possible.  */
        while (len0 >= BIGBLOCKSIZE)
        {
            *aligned_dst++ = *aligned_src++;
            *aligned_dst++ = *aligned_src++;
            *aligned_dst++ = *aligned_src++;
            *aligned_dst++ = *aligned_src++;
            len0 -= BIGBLOCKSIZE;
        }

        /* Copy one long word at a time if possible.  */
        while (len0 >= LITTLEBLOCKSIZE)
        {
            *aligned_dst++ = *aligned_src++;
            len0 -= LITTLEBLOCKSIZE;
        }

        /* Pick up any residual with a byte copier.  */
        dst = (char*)aligned_dst;
        src = (char*)aligned_src;
    }

    while (len0--)
        *dst++ = *src++;

#if SYS_FREERTOS_ENABLED
    sync_exit_critical();
#endif /* SYS_FREERTOS_ENABLED */

    return dst0;
#endif /* not PREFER_SIZE_OVER_SPEED */
}

void *
memmove2 (void *dst_void,
    const void *src_void,
    unsigned long length)
{
#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
    char *dst = dst_void;
    const char *src = src_void;

#if SYS_FREERTOS_ENABLED
    sync_enter_critical();
#endif /* SYS_FREERTOS_ENABLED */

    if (src < dst && dst < src + length)
    {
        /* Have to copy backwards */
        src += length;
        dst += length;
        while (length--)
        {
            *--dst = *--src;
        }
    }
    else
    {
        while (length--)
        {
            *dst++ = *src++;
        }
    }

#if SYS_FREERTOS_ENABLED
    sync_exit_critical();
#endif /* SYS_FREERTOS_ENABLED */

    return dst_void;
#else
    char *dst = dst_void;
    const char *src = src_void;
    long *aligned_dst;
    const long *aligned_src;

#if SYS_FREERTOS_ENABLED
    sync_enter_critical();
#endif /* SYS_FREERTOS_ENABLED */

    if (src < dst && dst < src + length)
    {
        /* Destructive overlap...have to copy backwards */
        src += length;
        dst += length;
        while (length--)
        {
            *--dst = *--src;
        }
    }
    else
    {
        /* Use optimizing algorithm for a non-destructive copy to closely 
           match memcpy. If the size is small or either SRC or DST is unaligned,
           then punt into the byte copy loop.  This should be rare.  */
        if (!TOO_SMALL(length) && !UNALIGNED (src, dst))
        {
            aligned_dst = (long*)dst;
            aligned_src = (long*)src;

            /* Copy 4X long words at a time if possible.  */
            while (length >= BIGBLOCKSIZE)
            {
                *aligned_dst++ = *aligned_src++;
                *aligned_dst++ = *aligned_src++;
                *aligned_dst++ = *aligned_src++;
                *aligned_dst++ = *aligned_src++;
                length -= BIGBLOCKSIZE;
            }

            /* Copy one long word at a time if possible.  */
            while (length >= LITTLEBLOCKSIZE)
            {
                *aligned_dst++ = *aligned_src++;
                length -= LITTLEBLOCKSIZE;
            }

            /* Pick up any residual with a byte copier.  */
            dst = (char*)aligned_dst;
            src = (char*)aligned_src;
        }

        while (length--)
        {
            *dst++ = *src++;
        }
    }

#if SYS_FREERTOS_ENABLED
    sync_exit_critical();
#endif /* SYS_FREERTOS_ENABLED */

    return dst_void;
#endif /* not PREFER_SIZE_OVER_SPEED */
}

/* memset():
 * Set an array of n chars starting at sp to the character c.
 * Return str.
 */
void *memset(register void *str, register int c, register int n)
{
    volatile char *sp0 = (volatile char *)str;

    while (--n >= 0) {
        *sp0++ = c;
    }
    return (str);
}

/* strcat():
 * Concatenate s2 on the end of s1.  S1's space must be large enough.
 * Return s1.
 */

char *strcat(register char *s1, register char *s2)
{
    register char *os1;

    os1 = s1;
    while(*s1++)
        ;
    --s1;
    while((*s1++ = *s2++))
        ;
    return(os1);
}

/* strchr():
 * Return the ptr in sp at which the character c appears;
 * NULL if not found
 */
char *strchr(register char *sp, register char c)
{
    do {
        if(*sp == c)
            return(sp);
    } while(*sp++);
    return((char *)0);
}

/* strlen():
 * Returns the number of
 * non-NULL bytes in string argument.
 */
int strlen(register char *s)
{
    register char *s0 = s + 1;

    while (*s++ != '\0')
        ;
    return (s - s0);
}

/* strnlen():
 * Returns the number of
 * non-NULL bytes in string argument.
 */
int strnlen(const char *s, unsigned int maxlen)
{
    const char *sc;

    for (sc = s; maxlen-- && *sc != '\0'; ++sc);
    return sc - s;
}


/* strstr():
 * Find the first occurrence of find in s1.
 */
char *strstr(char *s1, char *find)
{
    register char *s = s1;
    register char *p = find;

    do {
        if (!*p) {
            return (char *) s1;
        }
        if (*p == *s) {
            ++p;
            ++s;
        } else {
            p = find;
            if (!*s) {
                return 0;
            }
            s = ++s1;
        }
    } while (1);
}

/* strcpy():
 * Copy string s2 to s1.  s1 must be large enough.
 * return s1
 */

char *strcpy(register char *s1, const char *s2)
{
    register char *os1;

    os1 = s1;
    while((*s1++ = *s2++))
        ;
    return(os1);
}

/* strncat():
 * Concatenate s2 on the end of s1.  S1's space must be large enough.
 * At most n characters are moved.
 * Return s1.
 */
char *strncat(register char *s1,register char *s2,register int n)
{
    register char *os1;

    os1 = s1;
    while(*s1++)
        ;
    --s1;
    while((*s1++ = *s2++))
        if(--n < 0) {
            *--s1 = '\0';
            break;
        }
    return(os1);
}

/* strncpy():
 * Copy s2 to s1, truncating or null-padding to always copy n bytes
 * return s1
 */
char *strncpy(register char *s1, register char *s2, register int n)
{
    register char *os1 = s1;

    while (--n >= 0)
        if ((*s1++ = *s2++) == '\0')
            while (--n >= 0)
                *s1++ = '\0';
    return (os1);
}

/* strspn():
 * Return the number of characters in the maximum leading segment
 * of string which consists solely of characters from charset.
 */
int strspn(char *string, register char *charset)
{
    register char *p, *q;

    for(q=string; *q != '\0'; ++q) {
        for(p=charset; *p != '\0' && *p != *q; ++p)
            ;
        if(*p == '\0')
            break;
    }
    return(q-string);
}


/* strpbrk():
 * Return ptr to first occurance of any character from `brkset'
 * in the character string `string'; NULL if none exists.
 */

char *strpbrk(register char *string,register char *brkset)
{
    register char *p;

    do {
        for(p=brkset; *p != '\0' && *p != *string; ++p)
            ;
        if(*p != '\0')
            return(string);
    }
    while(*string++);
    return((char *)0);
}

/* strrchr():
 * Return the ptr in sp at which the character c last
 * appears; NULL if not found
 */
char *strrchr(register char *sp, register char c)
{
    register char *r;

    r = (char *)0;
    do {
        if(*sp == c)
            r = sp;
    } while(*sp++);
    return(r);
}

/* strtok():
 * uses strpbrk and strspn to break string into tokens on
 * sequentially subsequent calls.  returns NULL when no
 * non-separator characters remain.
 * `subsequent' calls are calls with first argument NULL.
 */

char *strtok(char *string, char *sepset)
{
    register char   *p, *q, *r;
    static char *savept;

    /*first or subsequent call*/
    p = (string == (char *)0)? savept: string;

    if(p == 0)      /* return if no tokens remaining */
        return((char *)0);

    q = p + strspn(p, sepset);  /* skip leading separators */

    if(*q == '\0')      /* return if no tokens remaining */
        return((char *)0);

    if((r = strpbrk(q, sepset)) == (char *)0)   /* move past token */
        savept = 0; /* indicate this is last token */
    else {
        *r = '\0';
        savept = ++r;
    }
    return(q);
}


/* strtolower():
 *  In-place modification of a string to be all lower case.
 */
char * strtolower(char *s)
{
    char *cp;

    cp = s;
    while(*cp) {
        *cp = tolower(*cp);
        cp++;
    }
    return(s);
}

/* strtoupper():
 *  In-place modification of a string to be all upper case.
 */
char *strtoupper(char *s)
{
    char *cp;

    cp = s;
    while(*cp) {
        *cp = toupper(*cp);
        cp++;
    }
    return(s);
}

/** 
 * Convert the hexadecimal format string to hexadecimal number array. Two bytes 
 * characters will be convert to one byte hexadecimal number. 
 *
 * \param dst The hexadecimal number array, unsigned char type
 * \param len The length of hexadecimal number array
 * \param src The source string with hexadecimal format
 * \return    The convertion number of hexadecimal array
 */
int strtohex(unsigned char *dst, char *src, int len)
{
    char s1,s2;
    unsigned char h1,h2;
    int i = 0;

    while ((*src) && (i < len))
    {
        s1 = *src++;
        h1 = toxdigit(s1);
        if (*src)
        {
            s2 = *src++;
            h2 = toxdigit(s2);
            dst[i++] = (h1 << 4) | h2;
        }
        else {
            dst[i++] = h1;
        }
    }
    return i;
}



#if 0  /* deleted by James Gan - 2018-04-06 */
/** \name  Variables used by getopt
 * \{ 
 */
static int  opt_ind;  ///< argv index to first command line arg that is not part of the option list.
static int  sp;          ///< Search position
#if 0  /* deleted by James Gan - 2018-04-06 */
static char *opt_arg; ///< Pointer to argument associated with an option
#endif

/** \}*/

/**
 * getopt initialization
 *
 * Since getopt() can be used by every command 
 * it's variables must be reinitialized prior to each command
 * executed through cli_cmd_parse()...
 *
 * \see
 */
void getopt_init(void)
{
    sp = 1;
    opt_ind = 1;
}

/**
 * Parse command-line options
 *
 *
 * \param argc argument count
 * \param argv argument vector
 * \param opts options for searching
 * \return If an option was successfully found, then returns the option character. 
 * If all command-line options have been parsed, then returns -1. 
 * If encounters an option character that was not in optstring, then '?' is returned. 
 * If encounters an option with a missing argument, then the return value depends on the first character in optstring: if it is ':', then ':' is returned; otherwise '?' is returned. 
 *
 * \see
 */
int getopt(int argc, char *argv[], char *opts)
{
    int c;
    char *cp;

    if(sp == 1) {
        if (opt_ind >= argc)
            return(-1);
        else if (argv[opt_ind][0] != '-')
            return(-1);
        else if (argv[opt_ind][1] == '\0')
            return(-1);
        else if(strcmp(argv[opt_ind], "--") == 0) {
            opt_ind++;
            return(-1);
        }
    }
    c = argv[opt_ind][sp];
    if(c == ':' || (cp=strchr(opts, c)) == 0) {
        //printf("Illegal option: %c\n",c);
        if(argv[opt_ind][++sp] == '\0') {
            opt_ind++;
            sp = 1;
        }
        return('?');
    }
    if(*++cp == ':') {
        if(argv[opt_ind][sp+1] != '\0')
            opt_arg = &argv[opt_ind++][sp+1];
        else if(++opt_ind >= argc) {
            //printf("Option requires argument: %c\n",c);
            sp = 1;
            return('?');
        } else
            opt_arg = argv[opt_ind++];
        sp = 1;
    } else {
        if(argv[opt_ind][++sp] == '\0') {
            sp = 1;
            opt_ind++;
        }
        opt_arg = 0;
    }
    return(c);
}
#endif /* #if 0 */

