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

static buffer_t *buffer_alloc(void);

buffer_t *buffer_alloc(void)
{
  int ret = 0;
  buffer_t *b = NULL;
  
  b = (buffer_t *)malloc(sizeof(buffer_t));
  if (b == NULL)
     return NULL;

  memset((buffer_t *)b, 0, sizeof(buffer_t));

  b->l = line_alloc();
  if (b->l == NULL) {
     free(b);
     return NULL;
  }

  ret = line_init(b->l, NULL, 0);
  if (!ret) {
     line_kill(b->l);
     free(b);
     return NULL;
  }

  return b;
}

buffer_t *buffer_init(int fd, unsigned long blen)
{
  buffer_t *b = NULL;

  /*
     Need at least 2 bytes
  */
  if (blen < 2)
     return NULL;

  b = buffer_alloc();
  if (b == NULL)
     return NULL;

  b->b = (char *)malloc(blen + 1);
  if (b->b == NULL) {
     buffer_kill(b);
     return NULL;
  }

  b->fd = fd;
  b->blen = blen;
  memset((char *)b->b, 0, blen + 1);
  
  return b;
}

void buffer_restart(buffer_t *b, int fd)
{
  b->fd       = fd;
  b->eof      = 0;
  b->cin      = 0;
  b->restart  = 0;
  b->ulen     = 0;
  b->clen     = 0;
  b->fbrk     = 0;

  line_restart(b->l);

  if (b->fd == -1)
     b->bp = b->b;

  else {
     b->bp = NULL;
     memset((char *)b->b, 0, b->blen);
  }
}

void buffer_kill(buffer_t *b)
{
  if (b->l)
     line_kill(b->l);

  if ((b->b) && (b->fd != -1))
     free(b->b);

  free(b);
}

char *buffer_next_line(buffer_t *bb)
{
  int ret = 0;
  char *p = NULL, *t = NULL, *bp = NULL, fn = 0;

  if (bb->eof)
     return NULL;

  if (bb->lbp != bb->bp)
     bb->lbp = bb->bp;

  /*
     Call line_restart?
  */
  if (bb->restart) {
     line_restart(bb->l);
     bb->restart = 0;
  }

  /*
     Buffer is empty
  */
  if (bb->cin == 0) {
     if (bb->fd != -1) {
        ret = read(bb->fd, bb->b, (bb->blen - 1));
        if (!ret) {
           bb->eof = 1;
           return NULL;
        }
     
        else
           bb->cin = ret;
     }

     else {
        bb->eof = 1;
        return NULL;
     }
  }

  while(1) {
    fn = 0;

    if (bb->fd == -1)
       bp = p = t = bb->bp;

    else
       bp = p = t = bb->b;

    for (bb->clen = bb->ulen = 0;; p++) {
        if (*p == '\0') {
           p = NULL;
           break;
        }

        else if (*p == '\n') {
// XXX Patch Submitted
	   if (bb->ulen) {
 	      if (*(p - 1) == '\r')
                 bb->ulen--;
	   }

           fn = 1;
           bb->clen++;
           break;
        }

        else {
           bb->ulen++;
           bb->clen++;
        }
    }

    /*
       No newline found.
    */
    if (!fn) { 
       ret = line_inject(bb->l, bp, bb->ulen);
       if (!ret)
          return NULL;

       if (bb->fd == -1)
          bb->bp += bb->clen;
      
       bb->fn = 1;
       bb->cin = 0;
       bb->restart = 0;
       bb->ulen = 0;       
       bb->clen = 0;

       if (bb->fd != -1)
          memset((char *)bb->b, 0, bb->blen);

       return buffer_next_line(bb);
    }

    if (bb->ulen) {
       ret = line_inject(bb->l, bp, bb->ulen);
       if (!ret)
          return NULL;
    }

    bb->restart = 1;

    if (bb->fd != -1)
#if 0
       memcpy((char *)bb->b, (char *)(bb->b + bb->clen), (bb->blen - bb->clen));
#endif
       memmove((char *)bb->b, (char *)(bb->b + bb->clen), (bb->blen - bb->clen));

    else
       bb->bp += bb->clen;

    bb->cin -= bb->clen;

    if (bb->fd != -1)
       memset((char *)(bb->b + bb->cin + 1), 0, (bb->blen - bb->cin));

    bb->ulen = 0;
    bb->clen = 0;

    return bb->l->data;
  }

  return NULL;
}
