/* INSERT.C: Only one task may use these function at a time. Variables
   must be reinitialized each time a new task begins to use it. */

#include"insert.h"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>


int ci_strcmp(const char *a, const char *b) {
/* case insensitive string compare */

  int i, j;
  for(i = 0; a[i]; i++) {
    if(!b[i])
      return 1;
    j = toupper(a[i]) - toupper(b[i]);
    if(j)
      return j;
  }
  return -(b[i] != 0);
}


/* defined in insert.h 
  struct myinsert {
  long INCR;
  long strg_size, strg_len;
  char* strg, *tmp, stmp[10], *IndString;
  }; */

struct myinsert InitInsert(int i) {
  struct myinsert ret;
  memset(&ret, 0, sizeof(ret));
  if (i < 2)
    ret.INCR = BUFINC;
  else
    ret.INCR = i;
  ret.strg_size = 0;
  ret.strg_len = 0;
  ret.strg = 0;
  return ret;
}

char* GetInsertBuf(struct myinsert* s) {
  return s->strg;
}


void Empty(struct myinsert* s) {
  if(s) {
    if(s->strg) 
      if(s->strg_size) {
	s->strg_len = 0;
	s->strg[0] = 0;
      }  
    if(s->IndString)
      free(s->IndString);
    s->IndString = 0;
  }
}


void FreeIns(struct myinsert* s) { // does not free the main buffer
  long x;

  if(s) {
    x = s->INCR;
    if(s->IndString)
      free(s->IndString);
    memset(s, 0, sizeof(*s));
    s->INCR = x;
  }  
}


struct myinsert AddToIndent(struct myinsert ins, int n) {
  int k;
  char *sa;

  if(!ins.IndString && n <= 0)
    return ins;
  if(!ins.IndString) { // here n > 0
    ins.IndString = calloc(n+1, sizeof(char)); // initializes data to zero
    memset(ins.IndString, ' ', n);
    return ins;
  }
  k = strlen(ins.IndString);
  if(k + n <= 0) {
    free(ins.IndString);
    ins.IndString = 0;
    return ins;
  }

  if(n > 0) {
    sa = calloc(k+n+1, sizeof(char));
    strcpy(sa, ins.IndString);
    memset(sa+k, ' ', n);
    free(ins.IndString);
    ins.IndString = sa;
  }

  ins.IndString[k+n] = 0; // all that is needed when n < 0

  return ins;
}


inline void CInsert2(char c, struct myinsert* ins) {
  if(ins)
    *ins = CInsert(c, *ins);
  else
    fputc(c, stderr);
}

inline void SInsert2(char* s, struct myinsert* ins) {
  if(ins)
    *ins = SInsert(s, *ins);
  else
    fputs(s, stderr);
}



struct myinsert FSInsert(struct myinsert ins, const char *str, ...) {
  char* p;
  va_list param;

  if(str) 
    if(strlen(str)) {
      va_start(param, str);
      vasprintf(&p, str, param);
      va_end(param);

      ins = SInsert(p, ins);

      free(p);
    }
  return ins;
}



struct myinsert SInsert_sub(char* s, size_t k, struct myinsert ins) {
  // k is the length of s (without the final 0 element)

  long i, j;

  if(!s)
    return ins;
  i = ins.strg_len;
  if(k <= 0)
    return ins;
  ins.strg_len += k;
  if(ins.strg_size <= ins.strg_len + 1) {
    for(; ins.strg_size <= ins.strg_len + 1; ins.strg_size += ins.INCR);
    if(!ins.strg) {
      ins.strg = calloc(ins.strg_size, sizeof(*ins.strg));
      if(!ins.strg) {
	printf("Sinsert: unable to allocate %ld bytes\n", ins.strg_size);
	exit(EXIT_FAILURE);
      }
      strcpy(ins.strg, s);
      return ins;
    }
    else {  /* if more memory is needed */
      ins.tmp = calloc(ins.strg_size, sizeof(*ins.strg));
      if(!ins.tmp) {
	printf("Sinsert: unable to allocate %ld bytes\n", ins.strg_size);
	exit(EXIT_FAILURE); 
      }
      strcpy(ins.tmp, ins.strg);
      free(ins.strg);
      ins.strg = ins.tmp;
    }
  }
  for(j = 0; i + j < ins.strg_len; j++) /* concatenate the strings */
    ins.strg[i+j] = s[j];
  ins.strg[i+j] = 0;

  return ins;
}



struct myinsert CInsert(char c, struct myinsert ins) {
  if(c == 0)
    return ins;
  if(ins.strg_size <= ins.strg_len + 1) {
    ins.strg_size += ins.INCR;
    if(!ins.strg) {
      ins.strg = calloc(ins.strg_size, sizeof(*ins.strg));
      if(!ins.strg) {
	printf("Cinsert: unable to allocate %ld bytes\n", ins.strg_size);
	exit(EXIT_FAILURE);
      }
    }
    else {
      ins.tmp = calloc(ins.strg_size, sizeof(*ins.tmp));
      if(!ins.tmp) {
	printf("Cinsert: unable to allocate %ld bytes\n", ins.strg_size);
	exit(EXIT_FAILURE);
      }
      strcpy(ins.tmp, ins.strg);
      free(ins.strg);
      ins.strg = ins.tmp;
    }
  }
  ins.strg[ins.strg_len++] = c; /* update ins.strg and ins.strg_len */
  ins.strg[ins.strg_len] = 0;

  if(c == '\n' && ins.IndString)
    ins = SInsert_sub(ins.IndString, strlen(ins.IndString), ins);

  return ins;
}



struct myinsert SInsert(char* s, struct myinsert ins) {
  long i, j;
  size_t k;
  char c;

  if(!s)
    return ins;
  k = strlen(s);
  if(!k)
    return ins;
  if(!ins.IndString) {
    ins = SInsert_sub(s, k, ins);
  }
  else {
    for(i = 0, j = 0; i < k; i++) {
      if(s[i] == '\n') {
	ins = SInsert_sub(s+j, i-j+1, ins); // insert s[j] ... s[i]
	j = i+1;
	ins = SInsert_sub(ins.IndString, strlen(ins.IndString), ins); 
      }
    }
    if(j < i)  // insert remaining part of string, if any is left
      ins = SInsert_sub(s+j, k-j, ins); // insert s[j] ... s[k-1]
  }
  return ins;
}


  long INCR = BUFINC;
  long strg_size = 0, strg_len = 0;
  char* strg = 0, *tmp, stmp[10];


void empty_buf() {
  if(strg) 
    if(strg_size) {
      strg_len = 0;
      strg[0] = 0;
    }  
}

void init_insert() {
  strg_size = 0; strg_len = 0; strg = 0;
}

char* get_insert_buf() {
  return strg;
}

void cinsert(char c) {
  if(c == 0)
    return;
  if(strg_size <= strg_len + 1) {
    strg_size += INCR;
    if(!strg) {
      strg = calloc(strg_size, sizeof(*strg));
      if(!strg) {
	printf("Cinsert: unable to allocate %ld bytes\n", strg_size);
	exit(EXIT_FAILURE);
      }
    }
    else {
      tmp = calloc(strg_size, sizeof(*tmp));
      if(!tmp) {
	printf("Cinsert: unable to allocate %ld bytes\n", strg_size);
	exit(EXIT_FAILURE);
      }
      strcpy(tmp, strg);
      free(strg);
      strg = tmp;
    }
  }
  strg[strg_len++] = c; /* update strg and strg_len */
  strg[strg_len] = 0;
}


void sinsert(char* s) {
  long i, j;
  size_t k;
  if(!s)
    return;
  i = strg_len;
  k = strlen(s);
  if(!k)
    return;
  strg_len += k;
  if(strg_size <= strg_len + 1) {
    for(; strg_size <= strg_len + 1; strg_size += INCR);
    if(!strg) {
      strg = calloc(strg_size, sizeof(*strg));
      if(!strg) {
	printf("Sinsert: unable to allocate %ld bytes\n", strg_size);
	exit(EXIT_FAILURE);
      }
      strcpy(strg, s);
      return;
    }
    else {  /* if more memory is needed */
      tmp = calloc(strg_size, sizeof(*strg));
      if(!tmp) {
	printf("Sinsert: unable to allocate %ld bytes\n", strg_size);
	exit(EXIT_FAILURE); 
      }
      strcpy(tmp, strg);
      free(strg);
      strg = tmp;
    }
  }
  for(j = 0; i + j < strg_len; j++) /* concatenate the strings */
    strg[i+j] = s[j];
  strg[i+j] = 0;
}
