/* $Id: String,v 1.2 2008/11/28 19:57:34 kiesling Exp $ -*-c-*-*/

/*
  This file is part of ctalk.
  Copyright  2005 - 2008  Robert Kiesling, ctalk@ctalklang.org.
  Permission is granted to copy this software provided that this copyright
  notice is included in all source code modules.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
*/

/*
 *    String class.
 */

/*
 *  If you change these definitions, make sure they match the
 *  definitions in the file, "ctalklib."
 */

#ifndef MAXMSG
#define MAXMSG 8192
#endif

#ifndef MAXLABEL
#define MAXLABEL 256
#endif

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE (!(FALSE))
#endif

#ifndef NULL
#define NULL ((void *)0)
#endif

#ifndef NULLSTR
#define NULLSTR "(null)"
#endif

Character class String;

require Array;

#define TRIM_CHAR(c)    (substrcpy (c, c, 1, strlen (c) - 2))

/*
 *    = (char *s)
 *      Set the value of the receiver to the argument.
 */

String instanceMethod = set_value (char *__stringArg) {
  self addInstanceVariable ("value", __stringArg asString value);
  return __ctalkGetInstanceVariable (__ctalk_self_internal (), "value", 1);
}

/*
 *    + (char *s)
 *   Concatenate the receiver and s.
 */

String instanceMethod + concat (char *s) {
  char buf[MAXMSG];
  OBJECT *op1, *op2, *result_object;

  op1 = self value;
  op2 = s value;

  sprintf (buf, "%s%s", 
	   ((op1 -> instancevars) ? 
	   op1 -> instancevars -> __o_value : 
	    op1 -> __o_value), 
	   ((op2 -> instancevars) ? 
	    op2 -> instancevars -> __o_value : 
	    op2 -> __o_value));

  result_object = 
    __ctalkCreateObjectInit ("result", op1 -> __o_classname,
				op1 -> __o_superclassname,
				op1 -> scope, buf);
  return result_object;
}

/*
 *   == (char *s)
 *   Return true if s and the receiver are equal, false otherwise.
 *   Also handle cases where a string may evaluate to NULL or an
 *   operand is 0, using the cases in the STR_IS_NULL macro.
 *   Also used in the != method, below.
 */

#define STR_IS_NULL(__s) (!strcmp (__s, NULLSTR) || \
                          !strcmp (__s, "0") || \
                          !strcmp (__s, "0x0"))

String instanceMethod == equality (char *s) {
  OBJECT *rcvr_value, *op_value, *result_value;
  char resultvalbuf[2];

  returnObjectClass Integer;

  rcvr_value = self value;
  op_value = s value;

  if (STR_IS_NULL(rcvr_value->__o_value)) {
    if (STR_IS_NULL(op_value->__o_value)) {
      strcpy (resultvalbuf, "1");
    } else {
      strcpy (resultvalbuf, "0");
    }
  } else {
    if (STR_IS_NULL(op_value->__o_value)) {
      if (STR_IS_NULL(rcvr_value->__o_value)) {
	strcpy (resultvalbuf, "1");
      } else {
	strcpy (resultvalbuf, "0");
      }
    } else {
      if (!strcmp (rcvr_value -> __o_value, op_value -> __o_value)) {
	strcpy (resultvalbuf, "1");
      } else {
	strcpy (resultvalbuf, "0");
      }
    }
  }
  result_value = __ctalkCreateObjectInit ("result", "Integer",
	  "Magnitude", rcvr_value -> scope, resultvalbuf);
  return result_value;
}

/*
 *   != (char *s)
 *   Return true if s and the receiver are equal, false otherwise.
 */

String instanceMethod != inequality (char *s) {
  OBJECT *rcvr_value, *op_value, *result_value;
  char resultvalbuf[2];

  returnObjectClass Integer;

  rcvr_value = self value;
  op_value = s value;

  if (STR_IS_NULL(rcvr_value->__o_value)) {
    if (STR_IS_NULL(op_value->__o_value)) {
      strcpy (resultvalbuf, "0");
    } else {
      strcpy (resultvalbuf, "1");
    }
  } else {
    if (STR_IS_NULL(op_value->__o_value)) {
      if (STR_IS_NULL(rcvr_value->__o_value)) {
	strcpy (resultvalbuf, "0");
      } else {
	strcpy (resultvalbuf, "1");
      }
    } else {
      if (!strcmp (rcvr_value -> __o_value, op_value -> __o_value)) {
	strcpy (resultvalbuf, "0");
      } else {
	strcpy (resultvalbuf, "1");
      }
    }
  }
  result_value = __ctalkCreateObjectInit ("result", "Integer",
	  "Magnitude", rcvr_value -> scope, resultvalbuf);
  return result_value;
}

/*
 *   subString (int __str_index, int __str_length) 
 *   Return the substring of the receiver of length characters
 *   beginning at index.
 */

String instanceMethod subString (int __str_index, int __str_length) {
  char buf[MAXMSG];
  OBJECT *op1, *idx, *len, *result_object;

  op1 = self value;
  idx = __str_index value;
  len = __str_length value;
  
  substrcpy (buf, op1 -> __o_value, atoi (idx -> __o_value), atoi (len -> __o_value));

  result_object = 
    __ctalkCreateObjectInit ("result", op1 -> __o_classname,
				op1 -> __o_superclassname, op1 -> scope, buf);
  return result_object;
}

/*
 *   length (void)
 *   Return the length in characters of the receiver.
 */

String instanceMethod length (void) {

  char *value_end_ptr;
  OBJECT *rcvr;

  returnObjectClass Integer;

  rcvr = self value;

  /*
   *  Strlen () has an OS-dependent return type.
   *  Calculate our own string length as an int.
   */
  value_end_ptr = index (rcvr -> __o_value, '\0');
  methodReturnInteger(value_end_ptr - rcvr -> __o_value)
}

/*
 *   at (int idx)
 *   Return the character at idx.
 */

String instanceMethod at (int i) {
  OBJECT *val_obj, *idx_obj, *idx_val_obj, *result_obj;
  char charbuf[2];
  char *value_end_ptr;
  int idx, self_length;

  returnObjectClass Character;

  val_obj = self value;
  idx_obj = i;

  value_end_ptr = index (val_obj -> __o_value, '\0');
  self_length = value_end_ptr - val_obj -> __o_value - 1;

  if ((idx_val_obj = __ctalkGetInstanceVariable (idx_obj, "value", FALSE))
      != NULL) {
    idx = atoi (idx_val_obj -> __o_value);
  } else {
    idx = atoi (idx_obj -> __o_value);
  }

  if (idx > self_length)
     return NULL;

  charbuf[0] = val_obj -> __o_value[idx];
  charbuf[1] = 0;

  result_obj = __ctalkCreateObjectInit ("result", "Character",
					   "Object", val_obj -> scope,
					   charbuf);
  return result_obj;
}

/*
 *   atPut (int idx, char c)
 *   Replace the character at idx with c.  Has no effect if
 *   idx is greater than the length of the receiver and returns
 *   NULL.  Returns the receiver if successful.
 */

String instanceMethod atPut (int __idxArg, char __charArg) {
  OBJECT *val_obj, *idx_obj, *idx_val_obj, *char_obj, *char_val_obj;
  char charbuf[5];
  char *value_end_ptr;
  int idx, self_length;

  returnObjectClass Character;

  val_obj = self value;
  idx_obj = __idxArg;
  char_obj = __charArg;

  value_end_ptr = index (val_obj -> __o_value, '\0');
  self_length = value_end_ptr - val_obj -> __o_value - 1;

  if ((idx_val_obj = __ctalkGetInstanceVariable (idx_obj, "value", FALSE))
      != NULL) {
    idx = atoi (idx_val_obj -> __o_value);
  } else {
    idx = atoi (idx_obj -> __o_value);
  }

  if (idx > self_length)
     return NULL;

  if ((char_val_obj = __ctalkGetInstanceVariable (char_obj, "value", FALSE))
      != NULL) {
    strcpy (charbuf, char_val_obj -> __o_value);
  } else {
    strcpy (charbuf, char_obj -> __o_value);
  }

  while (*charbuf == '\'')
    TRIM_CHAR(charbuf);
  val_obj -> __o_value[idx] = *charbuf;

  methodReturnSelf
}

/*
 *   split (char delimiter, Array resultArray)
 *   Split the receiver into tokens delimited by delimiter,
 *   and save the tokens in resultArray.
 */

String instanceMethod split (char delimiter, char **resultArray) {

  Integer new nItems;
  Integer new idx;
  Integer new start;
  Integer new rcvrLength;
  String new token;
  String new self2;

  returnObjectClass Integer;

  rcvrLength = self length;

  /*
   *  Trim the receiver if it is the first
   *  character of the delimiter.  This
   *  simplifies the loop below.
   */

  if (self at 0 == delimiter) {
    self2 = self subString 1, rcvrLength - 1;
  } else {
    self2 = self;
  }
  rcvrLength = self2 length;

  nItems = 0;
  start = 0;

  for (idx = 0; idx < rcvrLength; idx = idx + 1) {
    if (self2 at idx == delimiter) {
      token = self2 subString start, idx - start;
      resultArray atPut nItems, token;
      start = idx + 1;
      nItems = nItems + 1;
    }
  }

  /*
   *  Save the token after the last delimiter if
   *  necessary.
   */
  if (start < rcvrLength) {
    token = self2 subString start, rcvrLength - start;
    resultArray atPut nItems, token;
    nItems = nItems + 1;
  }

  return nItems;
}

String instanceMethod writeFormat (char *fmt, ...) {
  Exception new __e_app;
  SystemErrnoException new __e_system;
  METHOD *self_method;
  OBJECT *__tmp_stream_arg, *__fmt_arg_value, *__tmp_fmt_arg, 
    *result_object, *__save_fmt_arg;
  char *__streambuf;
  char __ctalk_fmt[MAXMSG];
  int __i_arg;
  if ((self_method = 
       __ctalkGetInstanceMethodByFn (__ctalkRtReceiverObject (),
				     __ctalkRtGetMethodFn (),
				     FALSE)) == NULL) {
    __e_app raiseCriticalException UNDEFINED_METHOD_X, 
      "in fmtPrint (class String)";
    methodReturnNULL
  }
  
  __fmt_arg_value = fmt value;

  __ctalkCPrintFmtToCtalkFmt (__fmt_arg_value -> __o_value, __ctalk_fmt);

  __save_fmt_arg = self_method->args[0];

  __tmp_fmt_arg = __ctalkCreateObjectInit ("fmt", "String", "Character",
					   LOCAL_VAR, __ctalk_fmt);

  __objRefCntSet (&__tmp_fmt_arg, __save_fmt_arg -> nrefs);
  self_method -> args[0] = __tmp_fmt_arg;

  if ((__streambuf = (char *)calloc (MAXMSG, sizeof(char))) == NULL) {
    __e_system raiseCriticalException "in fmtPrint (class String)";
    methodReturnNULL
  }
  __tmp_stream_arg = __ctalkCreateObjectInit ("stream", "String", "Character",
 				     LOCAL_VAR, NULL);
  __ctalkSetObjectValueBuf (__tmp_stream_arg, __streambuf);
  __objRefCntSet (&__tmp_stream_arg, self_method -> args[0] -> nrefs);

  for (__i_arg = self_method -> n_args; __i_arg > 0; __i_arg--) {
    self_method -> args[__i_arg] = self_method -> args[__i_arg - 1];
  }
  self_method -> args[0] = __tmp_stream_arg;
  ++self_method -> n_args;

  result_object = __ctalkLibcFnWithMethodVarArgs ((int (*)())sprintf, self_method, "Integer");

  --self_method -> n_args;
  for (__i_arg = 1; __i_arg <= self_method -> n_args; __i_arg++) {
    self_method -> args[__i_arg-1] = self_method -> args[__i_arg];
  }
  self_method -> args[self_method -> n_args] = NULL;
  self_method -> args[0] = __save_fmt_arg;
  __ctalkSetObjectValueVar (self, __tmp_stream_arg 
			    -> instancevars -> __o_value);
  __objRefCntSet (&__tmp_stream_arg, 0);
  __ctalkDeleteObject(__tmp_stream_arg);
  __objRefCntSet (&__tmp_fmt_arg, 0);
  __ctalkDeleteObject(__tmp_fmt_arg);
  __ctalkDeleteObject(result_object);
  methodReturnSelf
}

String instanceMethod getEnv (char *__envVar) {

  String new __envVarValue;

  if ((__envVarValue = getenv (__envVar)) != NULL) {
    self addInstanceVariable ("value", __envVarValue);
  } else {
    self addInstanceVariable ("value", NULLSTR);
  }

  methodReturnSelf
}

String instanceMethod match (char *__patternString) {

  OBJECT *__receiver, *__pattern;
  char *__idx, *__receiver_value, *__pattern_value;

  returnObjectClass Boolean;

  __receiver = self value;
  __pattern = __patternString value;

  if (__receiver -> instancevars)
    __receiver_value = __receiver->instancevars->__o_value;
  else
    __receiver_value = __receiver->__o_value;

  if (__pattern -> instancevars)
    __pattern_value = __pattern->instancevars->__o_value;
  else
    __pattern_value = __pattern->__o_value;
  
  for (__idx = __receiver_value; *__idx; __idx++) {
    if (!strncasecmp (__idx, __pattern_value, strlen (__pattern_value)))
      methodReturnTrue
  }

  methodReturnFalse
}

String instanceMethod matchCase (char *__patternString) {

  OBJECT *__receiver, *__pattern;
  char *__idx, *__receiver_value, *__pattern_value;

  returnObjectClass Boolean;

  __receiver = self value;
  __pattern = __patternString value;

  if (__receiver -> instancevars) 
    __receiver_value = __receiver->instancevars->__o_value;
  else
    __receiver_value = __receiver->__o_value;

  if (__pattern -> instancevars)
    __pattern_value = __pattern->instancevars->__o_value;
  else
    __pattern_value = __pattern->__o_value;
  
  for (__idx = __receiver_value; *__idx; __idx++) {
    if (!strncmp (__idx, __pattern_value, strlen (__pattern_value)))
      methodReturnTrue
  }

  methodReturnFalse
}

String instanceMethod asInteger (void) {
  OBJECT *__result_object;
  char __buf[MAXMSG];
  int __i;

  returnObjectClass Integer;

  __i = atoi (self value);
  sprintf (__buf, "%d", __i);

  __result_object = 
    __ctalkCreateObjectInit ("result", "Integer",
			     "Magnitude",
			     LOCAL_VAR, __buf);
  return __result_object;
}

String instanceMethod printOn (char *__fmt, ...) {
  __ctalkSelfPrintOn (); 
  methodReturnSelf;
}
