/* putil.c */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "putil.h"

extern int
pu_escape_single_quote(char* s, char** result)
{
  int n;
  int s_len;
  int in_sq;
  int in_atom;
  char nc;
  char* p;
  char* q;
  char* end;
  char* r;
  char* rp;

  s_len = strlen(s);
  end = s + s_len;

  in_sq   = 0;
  in_atom = 0;
  n = 0;
  p = s;
  while (p < end) {
    switch (*p) {
    case '\'':
      if (in_atom == 0) {
        in_atom = 1;
        in_sq   = 1;
      }
      else {
        if (in_sq == 0) {
          nc = *(p+1);
          if (nc == ' ' || nc == '\t' || nc == ',' || nc == '.' || nc == ']')
            in_atom = 0;
          else {
            n++; // found
          }
        }
        else {
          q = p + 1;
          while (q < end && (*q == ' ' || *q == '\t')) q++;
          if (q >= end || *q == ',' || *q == ']') {
            in_atom = in_sq = 0;
          }
          else {
            n++; // found
          }
        }
      }
      break;

    default:
      if (in_sq == 0 && in_atom != 0) {
        if (*p == ' ' || *p == '\t' || *p == ',' || *p == '.' || *p == ']') {
          in_atom = 0;
        }
      }
      break;
    }

    p++;
  }

  if (n == 0) {
    *result = s;
    return 0;
  }

  //fprintf(stderr, "s_len: %d, n: %d\r\n", s_len, n);
  r = (char* )malloc(s_len + 1 + n);
  if (r == 0)
    return -1;

  in_sq   = 0;
  in_atom = 0;
  p = s;
  rp = r;
  while (p < end) {
    switch (*p) {
    case '\'':
      if (in_atom == 0) {
        in_atom = 1;
        in_sq   = 1;
      }
      else {
        if (in_sq == 0) {
          nc = *(p+1);
          if (nc == ' ' || nc == '\t' || nc == ',' || nc == '.' || nc == ']')
            in_atom = 0;
          else {
            *rp++ = '\'';
          }
        }
        else {
          q = p + 1;
          while (q < end && (*q == ' ' || *q == '\t')) q++;
          if (q >= end || *q == ',' || *q == ']') {
            in_atom = in_sq = 0;
          }
          else {
            *rp++ = '\'';
          }
        }
      }
      break;

    default:
      if (in_sq == 0 && in_atom != 0) {
        if (*p == ' ' || *p == '\t' || *p == ',' || *p == '.' || *p == ']') {
          in_atom = 0;
        }
      }
      break;
    }

    *rp = *p;
    rp++;
    p++;
  }

  *result = r;
  return n;
}

extern int
pu_string_replace(char* s, char* from, char* to, char** result)
{
  int n;
  int len;
  int from_len;
  int to_len;
  int s_len;
  int new_size;
  char* p;
  char* q;
  char* end;
  char* r;

  from_len = strlen(from);
  to_len   = strlen(to);
  s_len    = strlen(s);
  end = s + s_len;;

  p = s;
  n = 0;
  while (p < end && (q = strstr(p, from)) != NULL) {
    p = q + from_len;
    n++;
  }

  if (n == 0) {
    *result = s;
    return 0;
  }

  if (from_len < to_len) {
    new_size = s_len + 1 + n * (to_len - from_len);
  }
  else {
    new_size = s_len + 1;
  }

  r = (char* )malloc(new_size);
  if (r == 0)
    return -1;

  *r = '\0';

  p = s;
  n = 0;
  while (p < end && (q = strstr(p, from)) != NULL) {
    len = (int )(q - p);
    strncat(r, p, len);
    strcat(r, to);
    p = q + from_len;
    n++;
  }

  if (p < end)
    strcat(r, p);

  *result = r;
  return n;
}

static int
pu_redis_prolog_atom_buf_size(TERM* t)
{
  char buf[AZ_MAX_ATOM_LENGTH + 1];

  if (IsAtom(t)) {
    Atom2Asciz(GetAtom(t), buf);
    return strlen(buf);
  }
  else
    return -1;
}

static int
pu_redis_prolog_list_buf_size(TERM* t)
{
  int len;

  if (IsNil(t))
    return 0;

  len = 0;
  while (IsCons(t)) {
    REALVALUE(t);
    t = BODY(t);
    if (IsInt(t)) {
      len++;
    }
    else if (IsAtom(t)) {
      if (GetAtom(t) != ATOM_NIL) { /* ignore ATOM_NIL => "[]" */
        len += pu_redis_prolog_atom_buf_size(t);
      }
    }
    else if (IsCons(t)) {
      len += pu_redis_prolog_list_buf_size(t);
    }
    else {
      return -1; /* error */
    }

    t++;
  }

  return len;
}

extern char*
pu_redis_prolog_list_to_string(TERM* t, char **pp)
{
  int len;
  char *buf;
  char *p;
  TERM *tb;

  if (pp == NULL) {
    len = pu_redis_prolog_list_buf_size(t);
    if (len <= 0)
      return NULL;

#if 0
    fprintf(stderr, "prolog_list_to_buf: %d\r\n", len);
    fflush(stderr);
#endif

    p = buf = (char* )malloc(len + 1);
    if (buf == NULL) {
      return NULL;
    }
    buf[0] = '\0';
  }
  else {
    buf = NULL;
    p = *pp;
  }

  while (IsCons(t)) {
    REALVALUE(t);
    t = BODY(t);
    tb = t;
    REALVALUE(tb);

    if (IsInt(tb)) {
      int v = GetInt(tb);
      if (v > 255 || v < 0) {
      err:
        if (buf != NULL) free(buf);
        return NULL;
      }

      *p = (char )v;
      p++;
      if (pp != NULL) (*pp)++;
    }
    else if (IsAtom(tb)) {
      if (GetAtom(tb) != ATOM_NIL) { /* ignore ATOM_NIL => "[]" */
        Atom2Asciz(GetAtom(tb), p);
        p += pu_redis_prolog_atom_buf_size(tb);
      }
    }
    else if (IsCons(tb)) {
      pu_redis_prolog_list_to_string(tb, &p);
    }
    else {
      goto err;
    }

    t++;
  }
  *p = '\0';

  return buf;
}

extern int pu_redis_is_prolog_string(TERM* t)
{
  if (IsCons(t)) {
    TERM *tb;

    REALVALUE(t);
    t = BODY(t);
    tb = t;
    REALVALUE(tb);
    if (IsInt(tb))
      return 1;
  }

  return 0;
}
