#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "buffer.h"
#include "line.h"
#include "unfold.h"
#include "misc.h"

unfold_t *unfold_alloc(void)
{
  unfold_t *u = NULL;

  u = (unfold_t *)malloc(sizeof(unfold_t));
  if (u == NULL)
     return NULL;

  memset((unfold_t *)u, 0, sizeof(unfold_t));

  return u;
}

unfold_t *unfold_init(int fd, unsigned long blen)
{
  int ret = 0;
  unfold_t *u = NULL;

  u = unfold_alloc();
  if (u == NULL)
     return NULL;

  u->b = buffer_init(fd, blen);
  if (u->b == NULL) {
     unfold_kill(u);
     return NULL;
  }

  u->l = line_alloc();
  if (u->l == NULL) {
     unfold_kill(u);
     return NULL;
  }

  ret = line_init(u->l, NULL, blen);
  if (!ret) {
     unfold_kill(u);
     return NULL;
  }

  u->lb = line_alloc();
  if (u->lb == NULL) {
     unfold_kill(u);
     return NULL;
  }

  ret = line_init(u->lb, NULL, blen);
  if (!ret) {
     unfold_kill(u);
     return NULL;
  }

  return u;
}

void unfold_kill(unfold_t *u)
{
  if (!u)
     return;

  if (u->b)
     buffer_kill(u->b);

  if (u->l)
     line_kill(u->l);

  if (u->lb)
     line_kill(u->lb);

  free(u);
}

void unfold_restart(unfold_t *u, int fd)
{
  u->eof = 0;
  line_restart(u->l);
  line_restart(u->lb);
  buffer_restart(u->b, fd);
}

char *unfold_next_line(unfold_t *u)
{
  int ret = 0;
  char *p = NULL;

  if (u->eof)
     return NULL;

  if (u->l->bytes)
     line_restart(u->l);

  /*
     Any saved data goes into the main buffer
  */  
  if (u->lb->bytes) {
     ret = line_inject(u->l, u->lb->data, u->lb->bytes);
     if (!ret) {
        u->eof = 1;
        return NULL;
     }

     /*
        ..and the saving buffer gets restarted
     */
     line_restart(u->lb);
  }

  while(1) {
    p = buffer_next_line(u->b);
    if (p == NULL) {
       if (u->l->bytes) {
          u->eof = 1;
          return u->l->data;
       }

       return NULL;
    }

    /*
       Blank lines end the unfold
    */
    if (!(*p)) {
       if (u->l->bytes) {
          u->eof = 1;
          return u->l->data;
       }

       return NULL;
    }

    /*
       Rolled lines always get appended
    */
    if ((*p == ' ') || (*p == '\t')) {
#ifdef DEBUG
       printf("ROLLING: [%s]\n", (p + 1));
#endif

       ret = line_inject(u->l, (p + 1), (u->b->l->bytes - 1));
       if (!ret) {
          u->eof = 1;
          return NULL;
       }

       continue;
    }
  
    /*
       If we have data already in the line buffer,
       we need to back up the line we just read,
       and return the current data.
    */
    if (u->l->bytes) {
#ifdef DEBUG
       printf("SAVING: [%s]\n", p);
#endif

       ret = line_inject(u->lb, p, u->b->l->bytes);
       if (!ret) {
          u->eof = 1;
          return NULL;
       }

       return u->l->data;
    }    

    /*
       Otherwise just inject into the buffer and continue
    */
#ifdef DEBUG
    printf("INJECTING: [%s]\n", p);
#endif

    ret = line_inject(u->l, p, u->b->l->bytes);
    if (!ret) {
       u->eof = 1;
       return NULL;
    }   
  }

  return NULL;
}
