#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include "ring.h"

/*
  let tail be the offset of the next byte to read
  let bytes be the number of bytes available to read
  let len be the total length of the buffer
 */
/*
  when buffer is emptied, as an optimization we 
  COULD reset the head and tail back to the beginning
 */
ring_buffer *new_ring_buffer(int len) {
  ring_buffer *ring = malloc(sizeof(ring_buffer));
  ring->buf = len ? malloc(len) : malloc(1);
  ring->len = len;
  ring->tail = 0;
  ring->bytes = 0;
  return ring;
}

void delete_ring_buffer(ring_buffer *ring) {
  free(ring->buf);
  free(ring);
}

// return the maximum number of bytes that can be read
// before hitting the wrap or the tail
int ring_bytes_can_read(ring_buffer *ring) {
  if (ring->bytes > ring->len - ring->tail) {
    return ring->len - ring->tail;
  } else {
    return ring->bytes;
  }
}

static inline int ring_head(ring_buffer *ring) {
  return (ring->tail + ring->bytes) % ring->len;
}

int ring_bytes_can_write(ring_buffer *ring) {
  int head = ring_head(ring);

  if (head >= ring->tail) {
    // [    tail------head     ]
    return ring->len - head;
  } else {
    // [----head     tail     ]
    return ring->len - ring->bytes;
  }
}

static inline int ring_add(ring_buffer *ring,int n) {  
  ring->bytes += n;
  assert(ring->bytes <= ring->len);
}

static inline int ring_sub(ring_buffer *ring,int n) {
  ring->tail += n;
  ring->bytes -= n;
  assert(ring->bytes >= 0);
  ring->tail %= ring->len;
}
// return how many bytes are present in the ring buffer
int ring_bytes_ready(ring_buffer *ring) {
  return ring->bytes;
}

// return how many additional bytes can fit into the ring buffer
int ring_bytes_free(ring_buffer *ring) {
  return ring->len - ring->bytes;
}

// call read(h,...) to try to fill up the ring buffer
int ring_rx(int h,ring_buffer *ring,int *err) {
  int len,n=0,got=1;

  *err = 0;
  while ((len = ring_bytes_can_write(ring)) && got > 0) {
    got = read(h,ring->buf+ring_head(ring),len);
    if (got > 0) {
      n += got;
      ring_add(ring,got);
    } else if (got < 0) {
      *err = errno;
    }
  }
  return n;
}

// call write(h,...) to try to empty the ring buffer
int ring_tx(int h,ring_buffer *ring) {
  int len,n,wrote=1;

  while ((len = ring_bytes_can_read(ring)) && write > 0) {
    wrote = write(h,ring->buf+ring->tail,len);
    if (wrote > 0) {
      n += wrote;
      ring_sub(ring,wrote);
    }
  }
  return n;
}

int ring_read_byte(ring_buffer *ring,unsigned char *byte) {
  if (ring_bytes_ready(ring) < 1) {
    return 0;
  }
  *byte = ring->buf[ring->tail];
  ring_sub(ring,1);
  return 1;
}

int ring_write_byte(ring_buffer *ring,unsigned char byte) {
  if (ring_bytes_free(ring) < 1) {
    return 0;
  }
  ring->buf[ring_head(ring)] = byte;
  ring_add(ring,1);
  return 1;
}

int ring_read_word_le(ring_buffer *ring,unsigned short *word);
int ring_read_word_be(ring_buffer *ring,unsigned short *word);
int ring_write_word_le(ring_buffer *ring,unsigned short word);
int ring_write_word_be(ring_buffer *ring,unsigned short word);
/*
  Author: Michael Schmidt (michael@embeddedARM.com)
 */
