#include "utils.h"



ptr_t
malloc0 (size_t sz) {
  ptr_t retval = malloc (sz);
  if (retval != NULL) {
    memset (retval, 0, sz);
  }
  return retval;
} 

str_t
string_vacuum (str_t s) {
  str_t out_buff;
  int index, i;

  index = -1;
  i = 0;
  for (auto c = s[i]; c != '\0'; c = s[i++]) {
    if (c != ' ' && c != '\n' && c != '\t' && c != '\r') {
        index = i;
    }
  }
  s[index + 1] = '\0';
  out_buff = realloc (s, strlen (s) + 1);
  return out_buff;
}

str_t
string_concat_list_nl (str_t* list) {
  ptr_t tmp;
  str_t buff = malloc0 (strlen (*list) * sizeof (list));
  if (buff == NULL) {
    err_print ("buffer is for some reason Null, this is an error.\n");
    goto error1;
  }
  for (str_t s = *list; s != NULL; s = *++list) {
    if (sizeof (buff) <= strlen (buff) + strlen (s) + 1 ) {
      size_t next_size = strlen (buff) + strlen (s) + 1;
      fprintf (stdout, "nz: %zu\n", next_size);
      tmp = realloc (buff, next_size);
      if (tmp == NULL || errno == ENOMEM) {
        goto error2;
      }
    }
    strcat (buff, "\n");
    strcat (buff, s);
  }
  buff = string_vacuum (buff);
  return buff;

error2:
  free (tmp);
error1:
  free (buff);
  return NULL;
}

void
err_print (str_t fmt, ...) {
  va_list vlst;
  va_start (vlst, fmt);
  vfprintf (stderr, fmt, vlst);
  va_end (vlst);
}


/* ************************************************************************** */

struct ByteStream {
  byte * bytes;
  size_t length;
  long int current;
};

ByteStream *
byte_stream_new (size_t length) {
  ByteStream * retval = malloc0 (sizeof (ByteStream));
  
  if (retval == NULL) {
    err_print ("Something went wrong with allocating the ByteStream. Have you run out of memory?\n");
    goto error1;
  }

  retval->bytes = malloc0 (length);
  if (retval->bytes == NULL) {
    err_print ("Cound not allocate ByteStream buffer. Have you run out of memory?\n");
    goto error2;
  }

  retval->length = length;
  retval->current = 0;

  return retval;

error2:
  free (retval);
error1:
  return NULL;
}

ByteStream *
byte_stream_new_from_file (UNUSED FILE * f) {
  NOT_IMPLEMENTED;
  return NULL;
}


void
byte_stream_seek (ByteStream * bstream, long int offset, SEEK_OFFSET origin) {
  switch (origin) {
    case (SEEK_OFFSET_SET):
      assert (offset > 0 && "Offset must be positive when using SEEK_OFFSET_SET.");
      bstream->current = offset;
      return;
    case (SEEK_OFFSET_CUR): 
      assert (((long) bstream->length >= (bstream->current + offset)) &&
              ((bstream->current + offset) >= 0) &&
              "Trying to seek outside range valid range.");
        bstream->current += offset;
      return;
    case (SEEK_OFFSET_END):
      assert (offset < 0 && "Offset must be negative when using SEEK_OFFSET_END.");
      bstream->current = bstream->length - offset;
      return;
    default:
      assert (false && "How did you end up here?");
  }
}

size_t
byte_stream_read (ByteStream * bstream, OUT ptr_t ptr, size_t size, size_t count) {
  /* Based on the Minix implementation: https://stackoverflow.com/a/8590471 */
  byte * cp = ptr;
  byte * bytes = bstream->bytes;
  size_t length = bstream->length;
  int c;
  size_t node = 0;
  size_t s;

  // Set the pointer correctly.
  bytes += bstream->current;

  if (size) {
    while (node < count) {
      s = 0;
      do {
        if ((s + (node * size)) <= length) {
          c = *bytes++;
          *cp++ = c;
          // update read byte.
          bstream->current++;
        }
        s++;
      } while (s <= size);
      node++;
    }
  }

  return node;
}

size_t
byte_stream_write (ByteStream * bstream, IN ptr_t ptr, size_t size, size_t count) {
  byte * cp = ptr;
  byte * bytes = bstream->bytes;
  int c;
  size_t node = 0;
  size_t s;
  size_t length = bstream->length;
  size_t read_length = size * count; 
  size_t current = bstream->current;


  if ((read_length + current) >= length) {
    ptr_t tmp = realloc (bstream->bytes, read_length + current);
    if (tmp == NULL) {
      err_print ("Could not reallocate ByteStream buffer. Have you run out of memory?\n");
      goto error;
    }
    
    else bstream->bytes = tmp;
  }

  if (size) {
    while (node < count) {
      s = 0;
      do {
        if ((s + (node * size)) <= read_length) {
          c = *cp++;
          *bytes++ = c;
        }
        s++;
      } while (s <= size);
      node++;
      current += s;
    }
  }
   
error:
  return node;
}

size_t
byte_stream_length (ByteStream * bstream) {
  return bstream->length;
}
