/*
 * espconn_buf.c
 *
 *  Created on: May 25, 2016
 *      Author: liuhan
 */

#include "lwip/memp.h"
#include "lwip/def.h"
#include "ets_sys.h"
#include "os_type.h"
#include "lwip/app/espconn_buf.h"


#ifdef MEMLEAK_DEBUG
static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__;
#endif

#if (!defined(lwIP_unlikely))
#define lwIP_unlikely(Expression) !!(Expression)
#endif

#define lwIP_ASSERT(Expression)	do{if(!(Expression)) {os_printf("%s %d\n", __func__, __LINE__);return 0;}}while(0)
#define lwIP_ASSERTS(Expression)	do{if(!(Expression)) {os_printf("%s %d\n", __func__, __LINE__);return;}}while(0)

ringbuf_t ICACHE_FLASH_ATTR
ringbuf_new(size_t capacity)
{
	ringbuf_t rb = (ringbuf_t)os_zalloc(sizeof(struct ringbuf_t));
	if (rb){
		rb->size = capacity + 1;
		rb->buf = (uint8*)os_zalloc(rb->size);
		if (rb->buf){
			ringbuf_reset(rb);
		}else{
			os_free(rb);
			return NULL;
		}
	}
	return rb;
}

size_t ICACHE_FLASH_ATTR
ringbuf_buffer_size(const struct ringbuf_t *rb)
{
	return rb->size;
}

void ICACHE_FLASH_ATTR
ringbuf_reset(ringbuf_t rb)
{
	rb ->head = rb->tail = rb->buf;
}

void ICACHE_FLASH_ATTR
ringbuf_free(ringbuf_t *rb)
{
	lwIP_ASSERTS(rb && *rb);
	os_free((*rb)->buf);
	os_free(*rb);
	*rb = NULL;
}

size_t ICACHE_FLASH_ATTR
ringbuf_capacity(const struct ringbuf_t *rb)
{
	return ringbuf_buffer_size(rb) - 1;
}

static const uint8_t* ICACHE_FLASH_ATTR
ringbuf_end(const struct ringbuf_t *rb)
{
	return rb->buf + ringbuf_buffer_size(rb);
}

size_t ICACHE_FLASH_ATTR
ringbuf_bytes_free(const struct ringbuf_t *rb)
{
	if (rb->head >= rb->tail){
		return ringbuf_capacity(rb) - (rb->head - rb->tail);
	}else{
		return rb->tail - rb->head -1;
	}
}

size_t ICACHE_FLASH_ATTR
ringbuf_bytes_used(const struct ringbuf_t *rb)
{
	return ringbuf_capacity(rb) - ringbuf_bytes_free(rb);
}

int ICACHE_FLASH_ATTR
ringbuf_is_full(const struct ringbuf_t *rb)
{
	return ringbuf_bytes_free(rb) == 0;
}

int ICACHE_FLASH_ATTR
ringbuf_is_empty(const struct ringbuf_t *rb)
{
	return ringbuf_bytes_free(rb) == ringbuf_capacity(rb);
}

const void* ICACHE_FLASH_ATTR
ringbuf_tail(const struct ringbuf_t *rb)
{
	return rb->tail;
}
const void* ICACHE_FLASH_ATTR
ringbuf_head(const struct ringbuf_t *rb)
{
	return rb->head;
}

static uint8_t* ICACHE_FLASH_ATTR
ringbuf_nextp(ringbuf_t rb, const uint8_t *p)
{
	lwIP_ASSERT((p >= rb->buf) && (p < ringbuf_end(rb)));
	return rb->buf + ((++p -rb->buf) % ringbuf_buffer_size(rb));
}

size_t ICACHE_FLASH_ATTR
ringbuf_findchr(const struct ringbuf_t *rb, int c, size_t offset)
{
	const uint8_t *bufend = ringbuf_end(rb);
	size_t bytes_used = ringbuf_bytes_used(rb);
	if (offset >= bytes_used)
		return bytes_used;

	const uint8_t *start = rb ->buf + (((rb->tail - rb->buf) + offset) % ringbuf_buffer_size(rb));
	lwIP_ASSERT(bufend > start);
	size_t n = LWIP_MIN(bufend - start, bytes_used - offset);
	const uint8_t *found = (const uint8_t *)memchr(start, c, n);
	if (found)
		return offset + (found - start);
	else
		return ringbuf_findchr(rb, c, offset + n);
}

size_t ICACHE_FLASH_ATTR
ringbuf_memset(ringbuf_t dst, int c, size_t len)
{
	const uint8_t *bufend = ringbuf_end(dst);
	size_t nwritten = 0;
	size_t count = LWIP_MIN(len, ringbuf_buffer_size(dst));
	int overflow = count > ringbuf_bytes_free(dst);

	while (nwritten != count){

		lwIP_ASSERT(bufend > dst->head);
		size_t n = LWIP_MIN(bufend - dst->head, count - nwritten);
		os_memset(dst->head, c, n);
		dst->head += n;
		nwritten += n;

		if (dst->head == bufend)
			dst->head = dst->buf;
	}

	if (overflow){
		dst->tail = ringbuf_nextp(dst, dst->head);
		lwIP_ASSERT(ringbuf_is_full(dst));
	}

	return nwritten;
}

void* ICACHE_FLASH_ATTR
ringbuf_memcpy_into(ringbuf_t dst,const void *src, size_t count)
{
	const uint8_t *u8src = src;
	const uint8_t *bufend = ringbuf_end(dst);
	int overflow = count > ringbuf_bytes_free(dst);
	size_t nread = 0;

	while (nread != count){
		lwIP_ASSERT(bufend > dst->head);
		size_t n = LWIP_MIN(bufend - dst->head, count - nread);
		os_memcpy(dst->head, u8src + nread, n);
		dst->head += n;
		nread += n;

		if (dst->head == bufend)
			dst->head = dst->buf;
	}

	if (overflow) {
		dst->tail = ringbuf_nextp(dst, dst->head);
		lwIP_ASSERT(ringbuf_is_full(dst));
	}

	return dst->head;
}

void* ICACHE_FLASH_ATTR
ringbuf_memcpy_from(void *dst,ringbuf_t src, size_t count)
{
	size_t bytes_used = ringbuf_bytes_used(src);

	if (count > bytes_used)
		return NULL;

	const uint8_t *u8dst = dst;
	const uint8_t *bufend = ringbuf_end(src);
	size_t nwritten = 0;

	while (nwritten != count){
		lwIP_ASSERT(bufend > src->tail);
		size_t n = LWIP_MIN(bufend - src->tail, count - nwritten);
		os_memcpy((uint8_t*)u8dst + nwritten, src->tail, n);
		src->tail += n;
		nwritten += n;

		if (src->tail == bufend)
			src->tail = src->buf;
	}

	lwIP_ASSERT(count + ringbuf_bytes_used(src) == bytes_used);
	return src->tail;
}



