/*
 * frostbasicstring.c
 *
 *  Created on: Jun 29, 2012
 *      Author: ergino
 */

#include "frost_basicstring.h"
#include "frost_mem.h"

#define GROW_IF_NECESSARY(s,l) { \
    if(s->len + l >= s->allocated_len) { \
        s->allocated_len = (s->allocated_len + l + 16) * 2; \
        s->str = frost_realloc(s->str, s->allocated_len); \
    } \
}

frost_basic_string *
frost_string_new_len (const char *init, signed   long len)
{
    frost_basic_string *ret = frost_new (frost_basic_string, 1);

    if (init == 0)
        ret->len = 0;
    else
        ret->len = len < 0 ? strlen(init) : len;
    ret->allocated_len = MAX(ret->len + 1, 16);
    ret->str = frost_malloc(ret->allocated_len);
    if (init)
        memcpy(ret->str, init, ret->len);
    ret->str[ret->len] = 0;

    return ret;
}

frost_basic_string *
frost_string_new (const char *init)
{
    return frost_string_new_len(init, -1);
}

frost_basic_string *
frost_string_sized_new (unsigned long default_size)
{
    frost_basic_string *ret = frost_new (frost_basic_string, 1);

    ret->str = frost_malloc (default_size);
    ret->str [0] = 0;
    ret->len = 0;
    ret->allocated_len = default_size;

    return ret;
}

char *
frost_string_free (frost_basic_string *string, frost_bool free_segment)
{
    char *data;

    frost_return_val_if_fail (string != NULL, NULL);

    data = string->str;
    frost_free(string);

    if(!free_segment) {
        return data;
    }

    frost_free(data);
    return 0;
}

frost_basic_string *
frost_string_append_len (frost_basic_string *string, const char *val, signed   long len)
{
    frost_return_val_if_fail(string != 0, 0);
    frost_return_val_if_fail(val != 0, string);

    if(len < 0) {
        len = strlen(val);
    }

    GROW_IF_NECESSARY(string, len);
    memcpy(string->str + string->len, val, len);
    string->len += len;
    string->str[string->len] = 0;

    return string;
}

frost_basic_string *
frost_string_append (frost_basic_string *string, const char *val)
{
    frost_return_val_if_fail(string != 0, 0);
    frost_return_val_if_fail(val != 0, string);

    return frost_string_append_len(string, val, -1);
}

frost_basic_string *
frost_string_append_c (frost_basic_string *string, char c)
{
    frost_return_val_if_fail(string != 0, 0);

    GROW_IF_NECESSARY(string, 1);

    string->str[string->len] = c;
    string->str[string->len + 1] = 0;
    string->len++;

    return string;
}
int
frost_unichar_to_utf8 (char c, char   *outbuf)
{
  /* If this gets modified, also update the copy in g_string_insert_unichar() */
  unsigned char len = 0;
  int first;
  int i;

  if (c < 0x80)
    {
      first = 0;
      len = 1;
    }
  else if (c < 0x800)
    {
      first = 0xc0;
      len = 2;
    }
  else if (c < 0x10000)
    {
      first = 0xe0;
      len = 3;
    }
   else if (c < 0x200000)
    {
      first = 0xf0;
      len = 4;
    }
  else if (c < 0x4000000)
    {
      first = 0xf8;
      len = 5;
    }
  else
    {
      first = 0xfc;
      len = 6;
    }

  if (outbuf)
    {
      for (i = len - 1; i > 0; --i)
    {
      outbuf[i] = (c & 0x3f) | 0x80;
      c >>= 6;
    }
      outbuf[0] = c | first;
    }

  return len;
}

frost_basic_string *
frost_string_append_unichar (frost_basic_string *string, char c)
{
    char utf8[6];
    int len;

    frost_return_val_if_fail (string != 0, 0);

    if ((len = frost_unichar_to_utf8 (c, utf8)) <= 0)
        return string;

    return frost_string_append_len (string, utf8, len);
}

frost_basic_string *
frost_string_prepend (frost_basic_string *string, const char *val)
{
    signed long len;

    frost_return_val_if_fail (string != 0, string);
    frost_return_val_if_fail (val != 0, string);

    len = strlen (val);

    GROW_IF_NECESSARY(string, len);
    memmove(string->str + len, string->str, string->len + 1);
    memcpy(string->str, val, len);

    return string;
}

frost_basic_string *
frost_string_insert (frost_basic_string *string, signed   long pos, const char *val)
{
    signed long len;

    frost_return_val_if_fail (string != 0, string);
    frost_return_val_if_fail (val != 0, string);
    frost_return_val_if_fail (pos <= string->len, string);

    len = strlen (val);

    GROW_IF_NECESSARY(string, len);
    memmove(string->str + pos + len, string->str + pos, string->len - pos - len + 1);
    memcpy(string->str + pos, val, len);

    return string;
}

void
frost_string_append_printf (frost_basic_string *string, const char *format, ...)
{
    char *ret;
    va_list args;

    frost_return_if_fail (string != 0);
    frost_return_if_fail (format != 0);

    va_start (args, format);
    ret = frost_strdup_vprintf (format, args);
    va_end (args);
    frost_string_append (string, ret);

    frost_free (ret);
}

void
frost_string_append_vprintf (frost_basic_string *string, const char *format, va_list args)
{
    char *ret;

    frost_return_if_fail (string != NULL);
    frost_return_if_fail (format != NULL);

    ret = frost_strdup_vprintf (format, args);
    frost_string_append (string, ret);
    frost_free (ret);
}

void
frost_string_printf (frost_basic_string *string, const char *format, ...)
{
    va_list args;

    frost_return_if_fail (string != NULL);
    frost_return_if_fail (format != NULL);

    frost_free (string->str);

    va_start (args, format);
    string->str = frost_strdup_vprintf (format, args);
    va_end (args);

    string->len = strlen (string->str);
    string->allocated_len = string->len+1;
}

frost_basic_string *
frost_string_truncate (frost_basic_string *string, unsigned long len)
{
    frost_return_val_if_fail (string != NULL, string);

    /* Silent return */
    if (len < 0 || len >= string->len) {
        return string;
    }

    string->len = len;
    string->str[len] = 0;
    return string;
}

frost_basic_string *
frost_string_set_size (frost_basic_string *string, unsigned long len)
{
    frost_return_val_if_fail (string != NULL, string);

    GROW_IF_NECESSARY(string, len);

    string->len = len;
    string->str[len] = 0;
    return string;
}

frost_basic_string *
frost_string_erase (frost_basic_string *string, signed   long pos, signed   long len)
{
    frost_return_val_if_fail (string != NULL, string);

    /* Silent return */
    if (pos >= string->len)
        return string;

    if (len == -1 || (pos + len) >= string->len) {
        string->str[pos] = 0;
        string->len = pos ;
    }
    else {
        memmove (string->str + pos, string->str + pos + len, string->len - (pos + len) + 1);
        string->len -= len;
    }

    return string;
}
