#include <stddef.h>
#include <string/string.h>
#include <umalloc/umalloc.h>

size_t string_len(const char *s) {
  size_t l = 0;
  while (*s != '\0') {
    l++;
    s++;
  }
  return l;
}

void *string_memset(void *p, int c, size_t n) {
  char *cp = p;
  for (size_t i = 0; i < n; i++) cp[i] = c;
  return p;
}

void *string_memcpy(void *dst, const void *src, size_t n) {
  char *a = dst;
  const char *b = src;
  for (size_t i = 0; i < n; i++) a[i] = b[i];
  return dst;
}

// https://aticleworld.com/memcmp-in-c/
int string_memcmp(const void *s1, const void *s2, int len)
{
    unsigned char *p = (unsigned char *)s1;
    unsigned char *q = (unsigned char *)s2;
    int charCompareStatus = 0;
    //If both pointer pointing same memory block
    if (s1 == s2)
    {
        return charCompareStatus;
    }
    while (len > 0)
    {
        if (*p != *q)
        {  //compare the mismatching character
            charCompareStatus = (*p >*q)?1:-1;
            break;
        }
        len--;
        p++;
        q++;
    }
    return charCompareStatus;
}

int string_strcmp(const char *a, const char *b) {
  while (*a && (*a == *b)) {
    a++, b++;
  }
  return (unsigned char)*a - (unsigned char)*b;
}

size_t string_strcspn(const char *s, const char *reject) {
    size_t count = 0;
    for (; *s != '\0'; ++s) {
        const char *r = reject;
        while (*r != '\0') {
            if (*s == *r) {
                return count;
            }
            r++;
        }
        count++;
    }
    return count;
}

size_t string_strspn(const char *s, const char *accept) {
    size_t count = 0;
    for (; *s != '\0'; ++s) {
        const char *a = accept;
        int matched = 0;
        while (*a != '\0') {
            if (*s == *a) {
                matched = 1;
                break;
            }
            a++;
        }
        if (!matched) {
            return count;
        }
        count++;
    }
    return count;
}

char *string_strcpy(char *dest, const char *src) {
    char *d = dest;
    while ((*d++ = *src++) != '\0') {
        ;
    }
    return dest;
}

char *string_strchr(const char *s, int c) {
    char ch = (char)c;
    while (*s != '\0') {
        if (*s == ch) {
            return (char *)s;
        }
        s++;
    }
    if (ch == '\0') {
        return (char *)s;
    }
    return NULL;
}

// https://stackoverflow.com/questions/32560167/strncmp-implementation
int string_strncmp( const char * s1, const char * s2, size_t n )
{
    while ( n && *s1 && ( *s1 == *s2 ) )
    {
        ++s1;
        ++s2;
        --n;
    }
    if ( n == 0 )
    {
        return 0;
    }
    else
    {
        return ( *(unsigned char *)s1 - *(unsigned char *)s2 );
    }
}

#define STRING_TOKENIZEALLOC_TOK_SIZE 0xff

char *string_tokenizealloc(char *s, char *delim) {
  static char *saved = NULL;

  if (s) {
    saved = s;
  }

  if (!delim || !saved) {
    return NULL;
  }

  while (*saved && string_strchr(delim, *saved)) {
    saved++;
  }

  if (!*saved) {
    return NULL;
  }

  char *w = (char *)umalloc(sizeof(char) * STRING_TOKENIZEALLOC_TOK_SIZE);
  string_memset(w, 0, sizeof(char) * STRING_TOKENIZEALLOC_TOK_SIZE);

  int k = 0;
  while (*saved && !string_strchr(delim, *saved)) {
    if (k >= STRING_TOKENIZEALLOC_TOK_SIZE - 1) {
      break;
    }
    w[k++] = *saved++;
  }

  w[k] = '\0';

  if (*saved) {
    saved++;
  }

  return w;
}

char *string_tokenizealloc_linecontinue(char *s, char *delim) {
  static char *saved = NULL;

  if (s) {
    saved = s;
  }

  if (!delim || !saved) {
    return NULL;
  }

  while (*saved && string_strchr(delim, *saved)) {
    saved++;
  }

  if (!*saved) {
    return NULL;
  }

  char *w = (char *)umalloc(sizeof(char) * STRING_TOKENIZEALLOC_TOK_SIZE);
  string_memset(w, 0, sizeof(char) * STRING_TOKENIZEALLOC_TOK_SIZE);

  int k = 0;

  while (*saved) {
    if (*saved == '\\') {
      char *p = saved;
      int bs_count = 0;

      while (*p == '\\') {
        bs_count++;
        p++;
      }

      if ((*p == '\n' || (*p == '\r' && (*p + 1) == '\n')) && (bs_count % 2 == 1)) {
        int tocopy = bs_count / 2;
        while (tocopy-- > 0 && k < STRING_TOKENIZEALLOC_TOK_SIZE - 1) {
          w[k++] = '\\';
        }

        if (*p == '\r' & *(p + 1) == '\n') {
          saved = p + 2;
        } else {
          saved = p + 1;
        }

        continue;
      }
    }

    if (string_strchr(delim, *saved)) {
      break;
    }

    if (k >= STRING_TOKENIZEALLOC_TOK_SIZE - 1) {
      break;
    }

    w[k++] = *saved++;
  }

  w[k] = '\0';

  if (*saved) {
    saved++;
  }

  return w;
}

// https://stackoverflow.com/questions/2488563/strcat-implementation
char *string_combine(char *dest, const char *src) {
  size_t i, j;
  for(i = 0; dest[i] != '\0'; i++);
  for(j = 0; src[j] != '\0'; j++) {
    dest[i+j] = src[j];
  }
  dest[i+j] = '\0';
  return dest;
}

// https://aticleworld.com/memmove-function-implementation-in-c/
void * string_memmove(void* dest, const void* src, unsigned int n)
{
    char *pDest = (char *)dest;
    const char *pSrc =( const char*)src;
    //allocate memory for tmp array
    char *tmp  = (char *)umalloc(sizeof(char ) * n);
    if(NULL == tmp)
    {
        return NULL;
    }
    else
    {
        unsigned int i = 0;
        // copy src to tmp array
        for(i =0; i < n ; ++i)
        {
            *(tmp + i) = *(pSrc + i);
        }
        //copy tmp to dest
        for(i =0 ; i < n ; ++i)
        {
            *(pDest + i) = *(tmp + i);
        }
        ufree(tmp); //free allocated memory
    }
    return dest;
}
