/**********************************************************************************************/
/* The MIT License                                                                            */
/*                                                                                            */
/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */
/*                                                                                            */
/* 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.                                                                              */
/**********************************************************************************************/

#include "utf8.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const utf8_char_t *
utf8_char_next (const utf8_char_t * c)
{
  const utf8_char_t *n = c + utf8_char_length (c);
  return n == c ? 0 : n;
}

// returnes the length of the char in bytes
size_t
utf8_char_length (const utf8_char_t * c)
{
  // count null term as zero size
  if (!c || 0x00 == c[0]) {
    return 0;
  }

  static const size_t _utf8_char_length[] = {
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2,
    2, 2, 2, 3, 3, 4, 0
  };

  return _utf8_char_length[(c[0] >> 3) & 0x1F];
}

int
utf8_char_whitespace (const utf8_char_t * c)
{
  // 0x7F is DEL
  if (!c || (c[0] >= 0 && c[0] <= ' ') || c[0] == 0x7F) {
    return 1;
  }
  // EIA608_CHAR_NO_BREAK_SPACE TODO other utf8 spaces
  if (0xC2 == (unsigned char) c[0] && 0xA0 == (unsigned char) c[1]) {
    return 1;
  }

  return 0;
}

// returns length of the string in bytes
// size is number of charcter to count (0 to count until NULL term)
size_t
utf8_string_length (const utf8_char_t * data, utf8_size_t size)
{
  size_t char_length, byts = 0;

  if (0 == size) {
    size = utf8_char_count (data, 0);
  }

  for (; 0 < size; --size) {
    if (0 == (char_length = utf8_char_length (data))) {
      break;
    }

    data += char_length;
    byts += char_length;
  }

  return byts;
}

size_t
utf8_char_copy (utf8_char_t * dst, const utf8_char_t * src)
{
  size_t bytes = utf8_char_length (src);

  if (bytes && dst) {
    memcpy (dst, src, bytes);
    dst[bytes] = '\0';
  }

  return bytes;
}

// returnes the number of utf8 charcters in a string given the number of bytes
// to count until the a null terminator, pass 0 for size
utf8_size_t
utf8_char_count (const char *data, size_t size)
{
  size_t i, bytes = 0;
  utf8_size_t count = 0;

  if (0 == size) {
    size = strlen (data);
  }

  for (i = 0; i < size; ++count, i += bytes) {
    if (0 == (bytes = utf8_char_length (&data[i]))) {
      break;
    }
  }

  return count;
}

// returns the length of the line in bytes triming not printable charcters at the end
size_t
utf8_trimmed_length (const utf8_char_t * data, utf8_size_t charcters)
{
  size_t l, t = 0, split_at = 0;
  for (size_t c = 0; (*data) && c < charcters; ++c) {
    l = utf8_char_length (data);
    if (!utf8_char_whitespace (data)) {
      split_at = t + l;
    }
    t += l, data += l;
  }

  return split_at;
}

size_t
_utf8_newline (const utf8_char_t * data)
{
  if ('\r' == data[0]) {
    return '\n' == data[1] ? 2 : 1;     // windows/unix
  } else if ('\n' == data[0]) {
    return '\r' == data[1] ? 2 : 1;     // riscos/macos
  } else {
    return 0;
  }
}

// returns the length in bytes of the line including the new line charcter(s)
// auto detects between windows(CRLF), unix(LF), mac(CR) and riscos (LFCR) line endings
size_t
utf8_line_length (const utf8_char_t * data)
{
  size_t n, len = 0;

  for (len = 0; 0 != data[len]; ++len) {
    if (0 < (n = _utf8_newline (data))) {
      return len + n;
    }

    data += utf8_char_length (data);
  }

  return len;
}

// returns number of chars to include before split
utf8_size_t
utf8_wrap_length (const utf8_char_t * data, utf8_size_t size)
{
  // Set split_at to size, so if a split point cna not be found, retuns the size passed in
  size_t char_length, char_count, split_at = size;

  for (char_count = 0; char_count <= size; ++char_count) {
    if (_utf8_newline (data)) {
      return char_count;
    } else if (utf8_char_whitespace (data)) {
      split_at = char_count;
    }

    char_length = utf8_char_length (data);
    data += char_length;
  }

  return split_at;
}

int
utf8_line_count (const utf8_char_t * data)
{
  size_t len = 0;
  int count = 0;

  do {
    len = utf8_line_length (data);
    data += len;
    ++count;
  } while (0 < len);

  return count - 1;
}

utf8_char_t *
utf8_load_text_file (const char *path, size_t * size)
{
  utf8_char_t *data = NULL;
  FILE *file = fopen (path, "r");

  if (file) {
    fseek (file, 0, SEEK_END);
    size_t file_size = ftell (file);
    fseek (file, 0, SEEK_SET);

    if (0 == (*size) || file_size <= (*size)) {
      (*size) = 0;
      data = (utf8_char_t *) malloc (1 + file_size);
      memset (data, '\0', file_size);

      if (data) {
        utf8_char_t *pos = data;
        size_t bytes_read = 0;

        while (0 < (bytes_read = fread (pos, 1, file_size - (*size), file))) {
          pos += bytes_read;
          (*size) += bytes_read;
        }
      }

      fclose (file);
    }
  }

  data[*size] = 0;
  return data;
}

#ifndef strnstr
char *
strnstr (const char *string1, const char *string2, size_t len)
{
  size_t length2;

  length2 = strlen (string2);
  if (!length2) {
    return (char *) string1;
  }

  while (len >= length2) {
    len--;
    if (!memcmp (string1, string2, length2))
      return (char *) string1;
    string1++;
  }
  return NULL;
}
#endif
