/*
 * ard_utils.c
 *
 *  Created on: Jul 4, 2010
 *      Author: mlf by Metodo2 srl
 */
#undef _APP_DEBUG_

#include "lwip/pbuf.h"
#include "wifi_spi.h"
#include "ard_utils.h"
#include "debug.h"

#define MAX_PBUF_STORED	30

tData pBufStore[MAX_PBUF_STORED][MAX_SOCK_NUM];

unsigned char headBuf = 0;
unsigned char tailBuf = 0;

#define IS_BUF_AVAIL() (tailBuf!=headBuf)
#define IS_BUF_EMPTY() ((tailBuf == 0) && (headBuf == 0))

void init_pBuf()
{
	memset(pBufStore, 0, sizeof(pBufStore));
}

void insert_pBuf(struct pbuf* q, uint8_t sock, void* _pcb)
{
	if (q == NULL)
		return;

	if (pBufStore[headBuf][sock].data != NULL)
	{
		WARN("Overwriting buffer %p idx:%d!\n", pBufStore[headBuf][sock].data, headBuf);
		// to avoid memory leak free the oldest buffer
		freetDataIdx(headBuf, sock);
	}

	u8_t* p = (u8_t*)calloc(q->tot_len,sizeof(u8_t));
    if(p != NULL) {
      if (pbuf_copy_partial(q, p, q->tot_len,0) != q->tot_len) {
    	  WARN("pbuf_copy_partial failed: src:%p, dst:%p, len:%d\n", q, p, q->tot_len);
    	  free(p);
    	  p = NULL;
    	  return;
      }

      pBufStore[headBuf][sock].data = p;
      pBufStore[headBuf][sock].len = q->tot_len;
      pBufStore[headBuf][sock].idx = 0;
      pBufStore[headBuf][sock].pcb = _pcb;
      headBuf++;

  	  if (headBuf == MAX_PBUF_STORED)
  		headBuf = 0;
  	  if (headBuf == tailBuf)
  		  WARN("Overwriting data [%d-%d]!\n", headBuf, tailBuf);
  	  INFO_UTIL("Insert: %p:%d-%d [%d,%d]\n", p, q->tot_len, p[0], headBuf, tailBuf);
    }
}

tData* get_pBuf(uint8_t sock)
{
	if (IS_BUF_EMPTY())
		return NULL;

	if (IS_BUF_AVAIL())
	{
		tData* p = &(pBufStore[tailBuf][sock]);
		INFO_UTIL_VER("%p [%d,%d]\n", p, headBuf, tailBuf);
		return p;
	}
	return NULL;
}

void freetData(void * buf, uint8_t sock)
{
	if (buf==NULL)
	{
		WARN("Buf == NULL!");
		return;
	}

    pBufStore[tailBuf][sock].data = NULL;
    pBufStore[tailBuf][sock].len = 0;
    pBufStore[tailBuf][sock].idx = 0;
    pBufStore[tailBuf][sock].pcb = 0;

	if (++tailBuf == MAX_PBUF_STORED)
		tailBuf = 0;
	INFO_UTIL("%p [%d,%d]\n", buf, headBuf, tailBuf);
	free(buf);
}

void freetDataIdx(uint8_t idxBuf, uint8_t sock)
{
	if (idxBuf >=MAX_PBUF_STORED)
	{
		WARN("idxBuf out of range: %d\n", idxBuf);
		return;
	}

	void * buf = pBufStore[idxBuf][sock].data;

	INFO_UTIL("%p idx:%d\n", buf, idxBuf);

	free(buf);

    pBufStore[idxBuf][sock].data = 0;
    pBufStore[idxBuf][sock].len = 0;
    pBufStore[idxBuf][sock].idx = 0;
    pBufStore[idxBuf][sock].pcb = 0;
}


void ack_recved(void* pcb, int len);

bool isAvailTcpDataByte(uint8_t sock)
{
	tData* p = get_pBuf(sock);

	if (p != NULL)
	{
		INFO_UTIL_VER("check:%d %d %p\n",p->idx, p->len, p->data);
		if (p->idx == p->len)
		{
			freetData(p->data, sock);
			ack_recved(p->pcb, p->len);
			INFO_UTIL("Free %p other buf %d tail:%d head:%d\n",
					p->data, IS_BUF_AVAIL(), tailBuf, headBuf);
			return (IS_BUF_AVAIL());
		}else{
			return true;
		}
	}
	return false;
}



bool getTcpDataByte(uint8_t sock, uint8_t* payload, uint8_t peek)
{
	// ref field in struct pbuf has been used as index pointer for byte data
	tData* p = get_pBuf(sock);

	if (p != NULL)
	{
		if (p->idx < p->len)
		{
		uint8_t* buf = (uint8_t*)p->data;
		if (peek)
			*payload = buf[p->idx];
		else
			*payload = buf[p->idx++];
		INFO_UTIL_VER("get:%d %p %d\n",p->idx, p->data, *payload);
		return true;
		}else{
			//dealloc current buffer
			INFO_UTIL("Free %p\n", p->data);
			freetData(p->data, sock);
			ack_recved(p->pcb, p->len);
		}
	}
	return false;
}

bool getTcpData(uint8_t sock, void** payload, uint16_t* len)
{
	tData* p = NULL;
	p = get_pBuf(sock);
	if (p != NULL)
	{
		*payload = p->data;
		*len = p->len;
		return true;
	}
	return false;
}

bool freeTcpData(uint8_t sock)
{
	tData* p = NULL;
	p = get_pBuf(sock);
	if (p != NULL)
	{
		freetData(p->data, sock);
		ack_recved(p->pcb, p->len);
		return true;
	}
	return false;
}