/*
 *
 * s t r . c				-- Strings management
 *
 * Copyright (C) 1993,1994,1995 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
 * 
 *
 * Permission to use, copy, and/or distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that both the above copyright notice and this permission notice appear in
 * all copies and derived works.  Fees for distribution or use of this
 * software or derived works may only be charged with express written
 * permission of the copyright holder.  
 * This software is provided ``as is'' without express or implied warranty.
 *
 * This software is a derivative work of other copyrighted softwares; the
 * copyright notices of these softwares are placed in the file COPYRIGHTS
 *
 *
 *           Author: Erick Gallesio [eg@kaolin.unice.fr]
 *    Creation date: ??????
 * Last file update:  1-Feb-1995 14:39
 */

#include <ctype.h>
#include "stk.h"

static int stringcomp(SCM s1, SCM s2)
{
  if (NSTRINGP(s1)) Err("comparing string: bad string", s1); 
  if (NSTRINGP(s2)) Err("comparing string: bad string", s2);

  return strcmp(CHARS(s1), CHARS(s2));
}

static int stringcompi(SCM s1, SCM s2)
{
  if (NSTRINGP(s1)) Err("comparing string: bad string", s1);
  if (NSTRINGP(s2)) Err("comparing string: bad string", s2);

  return STk_strcmpi(CHARS(s1), CHARS(s2));
}

SCM STk_makestrg(int len, char *init)
{
  long flag;
  SCM  z;

  flag = No_interrupt(1);
  NEWCELL(z, tc_string);

  z->storage_as.string.dim  = len;
  z->storage_as.string.data = (char *) must_malloc(len+1); 
  z->storage_as.string.data[len] = 0;

  if (init) strncpy(z->storage_as.string.data, init, len);
  No_interrupt(flag);
  return z;
}


/**** Section 6.7 ****/

PRIMITIVE STk_stringp(SCM obj)
{
  return STRINGP(obj) ? Truth: Ntruth;
}

PRIMITIVE STk_make_string(SCM len, SCM init_char)
{
  long k;
  SCM z;

  if ((k=STk_integer_value(len)) < 0) Err("make-string: bad string length", len);

  z = STk_makestrg(k, NULL);
      
  if (init_char != UNBOUND) {
    if (CHARP(init_char)) {
      char c = CHAR(init_char);
      int j;

      for(j=0 ;j<k; j++) z->storage_as.string.data[j] = c;
    }
    else
      Err("make-string: initializing char not valid", init_char); }
  return z;
}

PRIMITIVE STk_lstring(SCM l, int len)
{
  int j;
  SCM tmp, z;

  if (len < 0) Err("string: bad list", l);
  z = STk_makestrg(len, NULL);

  /* copy element in newly allocated string */
  for (j=0; j < len; j++, l=CDR(l)) {
    tmp = CAR(l);
    if (NCHARP(tmp)) Err("string: bad element", tmp);
    CHARS(z)[j] = CHAR(tmp);
  }
  return z;
}

PRIMITIVE STk_string_length(SCM str)
{
  if (NSTRINGP(str)) Err("string-length: not a string", str);
  return STk_makeinteger(str->storage_as.string.dim);
}

PRIMITIVE STk_string_ref(SCM str, SCM index)
{
  long k;

  if (NSTRINGP(str))    	        Err("string-ref: not a string", str);
  if ((k=STk_integer_value(index)) < 0) Err("string-ref: bad index", index);

  if (k >= str->storage_as.string.dim) 
    Err("string-ref: index out of bounds", index);
  return STk_makechar(CHARS(str)[k]);
}

PRIMITIVE STk_string_set(SCM str, SCM index, SCM value)
{
  long k;

  if (NSTRINGP(str))		        Err("string-set!: not a string", str); 
  if ((k=STk_integer_value(index)) < 0) Err("string-set!: bad index", index); 
  
  if (k >= str->storage_as.string.dim) 
    Err("string-set!: index out of bounds", index);
    
  if (NCHARP(value)) Err("string-set!: value is not a char", value);
    
  CHARS(str)[k] = CHAR(value);
  return UNDEFINED;
}

PRIMITIVE STk_streq   (SCM s1,SCM s2){return (stringcomp(s1,s2)==0)? Truth: Ntruth;}
PRIMITIVE STk_strless (SCM s1,SCM s2){return (stringcomp(s1,s2)<0) ? Truth: Ntruth;}
PRIMITIVE STk_strgt   (SCM s1,SCM s2){return (stringcomp(s1,s2)>0) ? Truth: Ntruth;}
PRIMITIVE STk_strlesse(SCM s1,SCM s2){return (stringcomp(s1,s2)<=0)? Truth: Ntruth;}
PRIMITIVE STk_strgte  (SCM s1,SCM s2){return (stringcomp(s1,s2)>=0)? Truth: Ntruth;}

PRIMITIVE STk_streqi   (SCM s1,SCM s2){return (stringcompi(s1,s2)==0)?Truth:Ntruth;}
PRIMITIVE STk_strlessi (SCM s1,SCM s2){return (stringcompi(s1,s2)<0) ?Truth:Ntruth;}
PRIMITIVE STk_strgti   (SCM s1,SCM s2){return (stringcompi(s1,s2)>0) ?Truth:Ntruth;}
PRIMITIVE STk_strlessei(SCM s1,SCM s2){return (stringcompi(s1,s2)<=0)?Truth:Ntruth;}
PRIMITIVE STk_strgtei  (SCM s1,SCM s2){return (stringcompi(s1,s2)>=0)?Truth:Ntruth;}

PRIMITIVE STk_substring(SCM string, SCM start, SCM end)
{
  char msg[] = "substring: not an integer";
  int from, to;

  if (NSTRINGP(string)) Err("substring: not a string", string);

  if ((from=STk_integer_value(start))==LONG_MIN) Err(msg ,start);
  if ((to=STk_integer_value(end)) == LONG_MIN)   Err(msg ,start);

  if (0 <= from && from <= to && to <= string->storage_as.string.dim)
    return STk_makestrg(to - from, CHARS(string)+from);

  Err("substring: bad bounds", Cons(start, end));
}

PRIMITIVE STk_string_append(SCM l, int len)
{
  int i, total=0;
  SCM z, tmp = l;
  char *p;
  
  /* Compute total length of resulting string */
  for (i = 0; i < len; i++) {
    if (NSTRINGP(CAR(tmp)))
      Err("string-append: bad string", CAR(tmp));
    total += CAR(tmp)->storage_as.string.dim;
    tmp = CDR(tmp);
  }

  /* Allocate result */
  z = STk_makestrg(total, NULL);
  p = CHARS(z);
  
  /* copy strings */
  for (i=0; i < len; i++) {
    strcpy(p, CHARS(CAR(l)));
    p += CAR(l)->storage_as.string.dim;
    l = CDR(l);
  }
  return z;
}

PRIMITIVE STk_string2list(SCM str)
{
  int j, len;
  SCM tmp, z = NIL;

  if (NSTRINGP(str)) Err("string->list: not a string", str);
  len = str->storage_as.string.dim;

  for (j=0; j<len; j++) {
    if (j == 0)
      tmp = z = Cons(STk_makechar(CHARS(str)[j]), NIL);
    else 
      tmp = CDR(tmp) = Cons(STk_makechar(CHARS(str)[j]), NIL);
  }
  return z;
}

PRIMITIVE STk_list2string(SCM l)
{
  int j=0, len = STk_llength(l);
  SCM z;

  if (len < 0) Err("list->string: bad list", l);
  z = STk_makestrg(len, NULL);
  for ( ; NNULLP(l); l=CDR(l)) {
    if (NCHARP(CAR(l))) Err("list->string: not a character", CAR(l));
    CHARS(z)[j++] = CHAR(CAR(l));
  }
  return z;
}

PRIMITIVE STk_string_copy(SCM str)
{
  if (NSTRINGP(str)) Err("string-copy: not a string", str);
  return STk_makestring(CHARS(str));
}

PRIMITIVE STk_string_fill(SCM str, SCM c)
{
  int len, i;
  char c_char;

  if (NSTRINGP(str)) Err("string-fill: not a string", str);
  if (NCHARP(c))     Err("string-fill: not a char", c);

  len = str->storage_as.string.dim;
  c_char = CHAR(c);

  for (i = 0; i < len; i++)
    CHARS(str)[i] = c_char;
  return UNDEFINED;
}


/*
 * 
 * STk bonus
 *
 */

PRIMITIVE STk_string_findp(SCM s1, SCM s2)
{
  char msg[] = "string-find?: bad string";

  if (NSTRINGP(s1)) Err(msg,s1);
  if (NSTRINGP(s2)) Err(msg,s2);
  
  return strstr(CHARS(s2), CHARS(s1)) ? Truth: Ntruth;
}

PRIMITIVE STk_string_index(SCM s1, SCM s2)
{
  char *p, msg[] = "string-index: bad string";

  if (NSTRINGP(s1)) Err(msg,s1);
  if (NSTRINGP(s2)) Err(msg,s2);

  p = strstr(CHARS(s2), CHARS(s1));
  
  return p? STk_makeinteger(p - CHARS(s2)) : Ntruth;
}



PRIMITIVE STk_string_lower(SCM s)
{
  SCM z;
  register char *p, *q;

  if (NSTRINGP(s)) Err("string-lower: not a string", s);
  z = STk_makestrg(strlen(CHARS(s)), NULL);

  for (p=CHARS(s), q=CHARS(z); *p; p++, q++) *q = tolower(*p);
  return z;
}

PRIMITIVE STk_string_upper(SCM s)
{
  SCM z;
  register char *p, *q;

  if (NSTRINGP(s)) Err("string-upper: not a string", s);
  z = STk_makestrg(strlen(CHARS(s)), NULL);

  for (p=CHARS(s), q=CHARS(z); *p; p++, q++) *q = toupper(*p);
  return z;
}
