#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "gc.h"

#define BUFSIZE 1024
static char buf[BUFSIZE];

#define LENGTHOF(x) (*( ((int *) (x)) - 1 ))
#define ASSERT(x,msg) if (!(x)) { printf("*** libic *** "); printf msg; exit(1); }

#if defined LINUX
#define LIB(f) _ ## f
#else
#define LIB(f) f
#endif

/**************************************************************
 * Internal routines
 */

char *LIB(_allocateArray)(int len)
{
  ASSERT((len >= 0), ("__allocate: negative array length: %d\n", len));

  int size = (len + 1) * sizeof(int);
  char *p = (char*) GC_malloc (size);
  ASSERT((p != NULL), ("GC_malloc: memory allocation failed\n"));
  memset (p, 0, size); 

  *((int*)p) = len;
  return (p + sizeof(int));
}

char *LIB(_allocateString)(int len)
{
  ASSERT((len >= 0), ("__allocate: negative string length: %d\n", len));

  int size = len + sizeof(int);
  char *p = (char*) GC_malloc (size);
  ASSERT((p != NULL), ("GC_malloc: memory allocation failed\n"));
  memset (p, 0, size); 

  *((int*)p) = len;
  return (p + sizeof(int));
}

char *LIB(_allocateObject)(int size)
{
  ASSERT((size >= 0), ("__allocate: negative size: %d\n", size));

  char *p = (char*) GC_malloc (size);
  ASSERT((p != NULL), ("GC_malloc: memory allocation failed\n"));
  memset (p, 0, size);

  return p;
}

char *LIB(_stringCat)(char *s, char *d)
{
  int lens, lend, len;
  char *r;

  ASSERT((s != NULL), ("__stringcat: first source string is null\n"));
  ASSERT((d != NULL), ("__stringcat: second source string is null\n"));

  lens = LENGTHOF(s);
  lend = LENGTHOF(d);
  len = lens + lend;
  r = LIB(_allocateString)(len);
  memcpy(r, s, lens);
  memcpy(r + lens, d, lend);
  return r;
}

char *LIB(_makeString)(char *s)
{
  int len;
  char *d;

  ASSERT(s != NULL, ("__makestring: source string is null\n"));

  len = strlen(s);
  d = LIB(_allocateString)(len);
  memcpy(d, s, len);
  return d;
}

/**************************************************************
 * io module
 */

void LIB(_print)(char *s) 
{ 
  ASSERT(s != NULL, ("print: source string is null\n"));
  fwrite (s, sizeof(char), LENGTHOF(s), stdout); 
  fflush(stdout); 
}

void LIB(_println)(char *s) 
{ 
  ASSERT(s != NULL, ("print: source string is null\n"));
  fwrite (s, sizeof(char), LENGTHOF(s), stdout);
  fwrite ("\n", sizeof(char), 1, stdout);
  fflush(stdout); 
}

void LIB(_printi)(int i)   { fprintf(stdout,"%d", i); }
void LIB(_printb)(char b)  { fprintf(stdout,"%s", b?"true":"false"); }

int LIB(_eof)()   { return feof(stdin) ? 1 : 0; } 
int LIB(_readi)() { return getc(stdin); }

char *LIB(_readln)() 
{ 
  fgets(buf,BUFSIZE-1,stdin); 
  buf[strlen(buf)-1] = 0;
  return LIB(_makeString)(buf);  
}

/***************************************************************
 * conv module
 */

int LIB(_stoi)(char *string, int error)
{
  int i = atoi (string);
  return (i == 0 && *string != '0') ? error : i;
}

char *LIB(_itos)(int i)
{
  sprintf (buf, "%d", i);
  return LIB(_makeString)(buf);
}

char *LIB(_atos)(int *a)
{
  int len, i;
  char *p;
  ASSERT(a != NULL, ("atos: null source array\n"));
  len = LENGTHOF(a);
  p = LIB(_allocateString)(len);
  for(i=0; i<len; i++) p[i] = (char) a[i];
  return p;
}

int *LIB(_stoa)(char *s)
{
  int len, i, *p;
  ASSERT(s != NULL, ("stoa: null source string\n"));
  len = LENGTHOF(s);
  p = (int*)LIB(_allocateArray)(len);
  for(i=0; i<len; i++) p[i] = (int) s[i];
  return p;
}

/*************************************************************
 * random, exit, timing
 */

void LIB(_exit)(int n) 
{ 
  exit(n); 
}

int LIB(_random)(int n) 
{ 
  ASSERT((n > 0), ("random: argument is not positive: %d\n", n));

  return (int) 
    ((double)n *
     ((double)rand()/(double)RAND_MAX)); 
}

int LIB(_time)() 
{ 
  return (int) 
    (1000.0 * 
     ((double)(clock())/(double)CLOCKS_PER_SEC));
}

/*************************************************************
 * main
 */

int main (int argc, char **argv)
{
  char **icarg;
  int i;
  extern int LIB(_ic_main) (char**); 

  argc--;
  argv++;
  
  icarg = (char **) LIB(_allocateArray)(argc);
  for (i = 0; i < argc; i++) 
    icarg[i] = LIB(_makeString)(argv[i]);
  
  LIB(_ic_main)(icarg); 
}
