/******************************************************************************
 * Copyright 2013-2014 Espressif Systems (Wuxi)
 *
 * FileName: espconn_tcp.c
 *
 * Description: tcp proto interface
 *
 * Modification history:
 *     2014/3/31, v1.0 create this file.
*******************************************************************************/

#include "lwip/netif.h"
#include "lwip/inet.h"
#include "netif/etharp.h"
#include "lwip/tcp.h"
#include "lwip/ip.h"
#include "lwip/init.h"
#include "lwip/tcp_impl.h"
#include "lwip/memp.h"

#include "ets_sys.h"
#include "os_type.h"
//#include "os.h"
#include "lwip/mem.h"
#include "lwip/app/espconn_tcp.h"

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

extern espconn_msg *plink_active;
extern espconn_msg *pserver_list;
extern struct tcp_pcb ** const tcp_pcb_lists[];

os_event_t espconn_TaskQueue[espconn_TaskQueueLen];

static err_t
espconn_client_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
static void
espconn_client_close(void *arg, struct tcp_pcb *pcb,u8 type);

static err_t
espconn_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
static void
espconn_server_close(void *arg, struct tcp_pcb *pcb,u8 type);

///////////////////////////////common function/////////////////////////////////
/******************************************************************************
 * FunctionName : espconn_kill_oldest
 * Description  : kill the oldest TCP block
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
static void ICACHE_FLASH_ATTR
espconn_kill_oldest(void)
{
	struct tcp_pcb *pcb, *inactive;
	u32_t inactivity;

	inactivity = 0;
	inactive = NULL;
	/* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */
	for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
		if ((u32_t) (tcp_ticks - pcb->tmr) >= inactivity) {
			inactivity = tcp_ticks - pcb->tmr;
			inactive = pcb;
		}
	}
	if (inactive != NULL) {
		tcp_abort(inactive);
	}

	/* Go through the list of FIN_WAIT_2 pcbs and get the oldest pcb. */
	inactivity = 0;
	inactive = NULL;
	for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
		if (pcb->state == FIN_WAIT_1 || pcb->state == FIN_WAIT_2){
			if ((u32_t) (tcp_ticks - pcb->tmr) >= inactivity) {
				inactivity = tcp_ticks - pcb->tmr;
				inactive = pcb;
			}
		}
	}
	/*Purges the PCB, removes it from a PCB list and frees the memory*/
	if (inactive != NULL) {
		tcp_pcb_remove(&tcp_active_pcbs, inactive);
		memp_free(MEMP_TCP_PCB, inactive);
	}

	/* Go through the list of LAST_ACK pcbs and get the oldest pcb. */
	inactivity = 0;
	inactive = NULL;
	for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
		if (pcb->state == LAST_ACK) {
			if ((u32_t) (tcp_ticks - pcb->tmr) >= inactivity) {
				inactivity = tcp_ticks - pcb->tmr;
				inactive = pcb;
			}
		}
	}
	/*Purges the PCB, removes it from a PCB list and frees the memory*/
	if (inactive != NULL) {
		tcp_pcb_remove(&tcp_active_pcbs, inactive);
		memp_free(MEMP_TCP_PCB, inactive);
	}
}

/******************************************************************************
 * FunctionName : espconn_kill_oldest_pcb
 * Description  : find the oldest TCP block by state
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void ICACHE_FLASH_ATTR espconn_kill_oldest_pcb(void)
{
	struct tcp_pcb *cpcb = NULL;
	uint8 i = 0;
	uint8 num_tcp_fin = 0;
	for(i = 2; i < 4; i ++){
		for (cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
			if (cpcb->state == TIME_WAIT){
				num_tcp_fin ++;
				if (num_tcp_fin == MEMP_NUM_TCP_PCB)
					break;
			}

			if (cpcb->state == FIN_WAIT_1 || cpcb->state == FIN_WAIT_2 || cpcb->state == LAST_ACK){
				num_tcp_fin++;
				if (num_tcp_fin == MEMP_NUM_TCP_PCB)
					break;
			}
		}

		if (num_tcp_fin == MEMP_NUM_TCP_PCB){
			num_tcp_fin = 0;
			espconn_kill_oldest();
		} else if (cpcb == NULL){
			num_tcp_fin = 0;
		}
	}
}

/******************************************************************************
 * FunctionName : espconn_kill_pcb
 * Description  : kill all the TCP block by port
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void ICACHE_FLASH_ATTR espconn_kill_pcb(u16_t port)
{
	struct tcp_pcb *cpcb = NULL;
	uint8 i = 0;
	struct tcp_pcb *inactive = NULL;
	struct tcp_pcb *prev = NULL;
	u8_t pcb_remove;
	/* Check if the address already is in use (on all lists) */
	for (i = 1; i < 4; i++) {
		cpcb = *tcp_pcb_lists[i];
		while(cpcb != NULL){
			pcb_remove = 0;
			if (cpcb->local_port == port) {
				++pcb_remove;
			}
			/* If the PCB should be removed, do it. */
			if (pcb_remove) {
				/* Remove PCB from tcp_pcb_lists list. */
				inactive = cpcb;
				cpcb = inactive->next;
				tcp_pcb_remove(tcp_pcb_lists[i], inactive);
				memp_free(MEMP_TCP_PCB, inactive);
			} else {
				cpcb = cpcb->next;
			}
		}
	}
}

/******************************************************************************
 * FunctionName : espconn_find_current_pcb
 * Description  : find the TCP block which option
 * Parameters   : pcurrent_msg -- the node in the list which active
 * Returns      : TCP block point
*******************************************************************************/
struct tcp_pcb *ICACHE_FLASH_ATTR espconn_find_current_pcb(espconn_msg *pcurrent_msg)
{
	uint16 local_port = pcurrent_msg->pcommon.local_port;
	uint32 local_ip = pcurrent_msg->pcommon.local_ip;
	uint16 remote_port = pcurrent_msg->pcommon.remote_port;
	uint32 remote_ip = *((uint32*)&pcurrent_msg->pcommon.remote_ip);
	struct tcp_pcb *find_pcb = NULL;
	if (pcurrent_msg ->preverse == NULL){/*Find the server's TCP block*/
		if (local_ip == 0|| local_port == 0) return pcurrent_msg->pcommon.pcb;

		for (find_pcb = tcp_active_pcbs; find_pcb != NULL; find_pcb = find_pcb->next){
			if ((find_pcb->remote_port == remote_port) && (find_pcb->remote_ip.addr == remote_ip) &&
				(find_pcb->local_port == local_port) && (find_pcb->local_ip.addr == local_ip))
				return find_pcb;
		}

		for (find_pcb = tcp_tw_pcbs; find_pcb != NULL; find_pcb = find_pcb->next){
			if ((find_pcb->remote_port == remote_port) && (find_pcb->remote_ip.addr == remote_ip) &&
				(find_pcb->local_port == local_port) && (find_pcb->local_ip.addr == local_ip))
				return find_pcb;
		}
	} else {/*Find the client's TCP block*/
		if (remote_ip == 0|| remote_port == 0) return pcurrent_msg->pcommon.pcb;

		for (find_pcb = tcp_active_pcbs; find_pcb != NULL; find_pcb = find_pcb->next){
			if ((find_pcb->remote_port == remote_port) && (find_pcb->remote_ip.addr == remote_ip))
				return find_pcb;
		}

		for (find_pcb = tcp_tw_pcbs; find_pcb != NULL; find_pcb = find_pcb->next){
			if ((find_pcb->remote_port == remote_port) && (find_pcb->remote_ip.addr == remote_ip))
				return find_pcb;
		}
	}
	return NULL;
}

/******************************************************************************
 * FunctionName : espconn_tcp_memp_free
 * Description  : frees the connection memory in the server mode
 * Parameters   : arg -- Additional argument to pass to the function
 * Returns      : none
*******************************************************************************/
void ICACHE_FLASH_ATTR espconn_tcp_memp_free(espconn_msg *pmemp)
{
	struct espconn *espconn = NULL;
	if (pmemp == NULL)
		return;

	/*Enable block option for fetches the data proactive*/
    if (espconn_manual_recv_disabled(pmemp))
        espconn_list_delete(&plink_active, pmemp);

	if (pmemp->espconn_mode == ESPCONN_TCPSERVER_MODE){
		if (pmemp->pespconn != NULL && pmemp->pespconn->proto.tcp != NULL)
			os_free(pmemp->pespconn->proto.tcp);
		pmemp->pespconn->proto.tcp = NULL;

		os_free(pmemp->pespconn);
		pmemp->pespconn = NULL;
	}

	if (pmemp->readbuf != NULL){
		ringbuf_free(&pmemp->readbuf);
	}
	os_free(pmemp);
	pmemp = NULL;
}

/******************************************************************************
 * FunctionName : espconn_tcp_reconnect
 * Description  : reconnect with host
 * Parameters   : arg -- Additional argument to pass to the callback function
 * Returns      : none
*******************************************************************************/
static void ICACHE_FLASH_ATTR
espconn_tcp_reconnect(void *arg)
{
	espconn_msg *precon_cb = arg;
	sint8 re_err = 0;
	espconn_buf *perr_buf = NULL;
	espconn_buf *perr_back = NULL;
	espconn_kill_oldest_pcb();
	if (precon_cb != NULL) {
		struct espconn *espconn = precon_cb->preverse;

		re_err = precon_cb->pcommon.err;
		if (precon_cb->pespconn != NULL){
			if (espconn != NULL){/*Process the server's message block*/
				if (precon_cb->pespconn->proto.tcp != NULL){
					espconn_copy_partial(espconn, precon_cb->pespconn);
				}
			} else {/*Process the client's message block*/
				espconn = precon_cb->pespconn;
			}
		}

		/*to prevent memory leaks, ensure that each allocated is deleted*/
		perr_buf = precon_cb->pcommon.pbuf;
		while (perr_buf != NULL){
			perr_back = perr_buf;
			perr_buf = perr_back->pnext;
			espconn_pbuf_delete(&precon_cb->pcommon.pbuf,perr_back);
			os_free(perr_back);
			perr_back = NULL;
		}

		if (espconn && espconn->proto.tcp && espconn->proto.tcp->reconnect_callback != NULL) {
			espconn->proto.tcp->reconnect_callback(espconn, re_err);
		}

		/*frees the connection memory*/
		espconn_tcp_memp_free(precon_cb);
	} else {
		espconn_printf("espconn_tcp_reconnect err\n");
	}
}

/******************************************************************************
 * FunctionName : espconn_tcp_disconnect
 * Description  : disconnect with host
 * Parameters   : arg -- Additional argument to pass to the callback function
 * Returns      : none
*******************************************************************************/
static void ICACHE_FLASH_ATTR
espconn_tcp_disconnect_successful(void *arg)
{
	espconn_msg *pdiscon_cb = arg;
	sint8 dis_err = 0;
	espconn_buf *pdis_buf = NULL;
	espconn_buf *pdis_back = NULL;
	espconn_kill_oldest_pcb();
	if (pdiscon_cb != NULL) {
		struct espconn *espconn = pdiscon_cb->preverse;

		dis_err = pdiscon_cb->pcommon.err;
		if (pdiscon_cb->pespconn != NULL){
			struct tcp_pcb *pcb = NULL;
			if (espconn != NULL){/*Process the server's message block*/
				if (pdiscon_cb->pespconn->proto.tcp != NULL && espconn->proto.tcp){
					espconn_copy_partial(espconn, pdiscon_cb->pespconn);
				}
			} else {/*Process the client's message block*/
				espconn = pdiscon_cb->pespconn;
			}

			/*process the current TCP block*/
			pcb = espconn_find_current_pcb(pdiscon_cb);
			if (pcb != NULL){
				if (espconn_reuse_disabled(pdiscon_cb)) {
					struct tcp_pcb *cpcb = NULL;
					struct tcp_pcb *prev = NULL;
					u8_t pcb_remove;
					espconn_printf("espconn_tcp_disconnect_successful %d, %d\n",	pcb->state, pcb->local_port);
					cpcb = tcp_tw_pcbs;
					while (cpcb != NULL) {
						pcb_remove = 0;
						if (cpcb->local_port == pcb->local_port) {
							++pcb_remove;
						}
						/* If the PCB should be removed, do it. */
						if (pcb_remove) {
							struct tcp_pcb *backup_pcb = NULL;
							tcp_pcb_purge(cpcb);
							/* Remove PCB from tcp_tw_pcbs list. */
							if (prev != NULL) {
								LWIP_ASSERT("espconn_tcp_delete: middle cpcb != tcp_tw_pcbs",cpcb != tcp_tw_pcbs);
								prev->next = cpcb->next;
							} else {
								/* This PCB was the first. */
								LWIP_ASSERT("espconn_tcp_delete: first cpcb == tcp_tw_pcbs",tcp_tw_pcbs == cpcb);
								tcp_tw_pcbs = cpcb->next;
							}
							backup_pcb = cpcb;
							cpcb = cpcb->next;
							memp_free(MEMP_TCP_PCB, backup_pcb);
						} else {
							prev = cpcb;
							cpcb = cpcb->next;
						}
					}

				} else {
					tcp_arg(pcb, NULL);
					tcp_err(pcb, NULL);
				}
			}
		}

		/*to prevent memory leaks, ensure that each allocated is deleted*/
		pdis_buf = pdiscon_cb->pcommon.pbuf;
		while (pdis_buf != NULL) {
			pdis_back = pdis_buf;
			pdis_buf = pdis_back->pnext;
			espconn_pbuf_delete(&pdiscon_cb->pcommon.pbuf, pdis_back);
			os_free(pdis_back);
			pdis_back = NULL;
		}

		if (espconn->proto.tcp && espconn->proto.tcp->disconnect_callback != NULL) {
			espconn->proto.tcp->disconnect_callback(espconn);
		}

		/*frees the connection memory*/
		espconn_tcp_memp_free(pdiscon_cb);
	} else {
		espconn_printf("espconn_tcp_disconnect err\n");
	}
}

/******************************************************************************
 * FunctionName : espconn_Task
 * Description  : espconn processing task
 * Parameters   : events -- contain the espconn processing data
 * Returns      : none
*******************************************************************************/
static void ICACHE_FLASH_ATTR
espconn_Task(os_event_t *events)
{
	espconn_msg *plist = NULL;
	bool active_flag = false;
	espconn_msg *task_msg = NULL;
	struct espconn *pespconn = NULL;

	task_msg = (espconn_msg *) events->par;
	/*find the active connection node*/
	for (plist = plink_active; plist != NULL; plist = plist->pnext){
		if (task_msg == plist) {
			active_flag = true;
			break;
		}
	}

	if (active_flag){
		switch (events->sig) {
			case SIG_ESPCONN_WRITE: {
				pespconn = task_msg->pespconn;
				if (pespconn == NULL) {
					return;
				}

				if (pespconn->proto.tcp->write_finish_fn != NULL) {
					pespconn->proto.tcp->write_finish_fn(pespconn);
				}
			}
				break;
			case SIG_ESPCONN_ERRER:
				/*remove the node from the client's active connection list*/
			    if (espconn_manual_recv_enabled(task_msg))
			        espconn_list_delete(&plink_active, task_msg);
			        espconn_tcp_reconnect(task_msg);
				break;
			case SIG_ESPCONN_CLOSE:
				/*remove the node from the client's active connection list*/
			    if (espconn_manual_recv_enabled(task_msg))
			        espconn_list_delete(&plink_active, task_msg);
				espconn_tcp_disconnect_successful(task_msg);
				break;
			default:
				break;
		}
	}
}

/******************************************************************************
 * FunctionName : espconn_tcp_sent
 * Description  : sent data for client or server
 * Parameters   : void *arg -- client or server to send
 *                uint8* psent -- Data to send
 *                uint16 length -- Length of data to send
 * Returns      : return espconn error code.
 * - ESPCONN_OK. Successful. No error occured.
 * - ESPCONN_MEM. Out of memory.
 * - ESPCONN_RTE. Could not find route to destination address.
 * - More errors could be returned by lower protocol layers.
*******************************************************************************/
err_t ICACHE_FLASH_ATTR
espconn_tcp_sent(void *arg, uint8 *psent, uint16 length)
{
	espconn_msg *ptcp_sent = arg;
    struct tcp_pcb *pcb = NULL;
    err_t err = 0;
    u16_t len = 0;
    u8_t data_to_send = false;

    espconn_printf("espconn_tcp_sent ptcp_sent %p psent %p length %d\n", ptcp_sent, psent, length);

    /*Check the parameters*/
    if (ptcp_sent == NULL || psent == NULL || length == 0) {
        return ESPCONN_ARG;
    }

    /*Set the packet length depend on the sender buffer space*/
    pcb = ptcp_sent->pcommon.pcb;
    if (tcp_sndbuf(pcb) < length) {
        len = tcp_sndbuf(pcb);
    } else {
        len = length;
        LWIP_ASSERT("length did not fit into uint16!", (len == length));
    }

    if (len > (2*pcb->mss)) {
        len = 2*pcb->mss;
    }

    /*Write data for sending, but does not send it immediately*/
	do {
		espconn_printf("espconn_tcp_sent writing %d bytes %p\n", len, pcb);
		if (espconn_copy_disabled(ptcp_sent))
			err = tcp_write(pcb, psent, len, 1);
		else
			err = tcp_write(pcb, psent, len, 0);

        if (err == ERR_MEM) {
			if(len < 3)
				len--;
			else
            	len /= 2;
        }

    } while (err == ERR_MEM && len > 0);

	/*Find out what we can send and send it, offset the buffer point for next send*/
    if (err == ERR_OK) {
    	ptcp_sent->pcommon.ptail->punsent = psent + len;
		ptcp_sent->pcommon.ptail->unsent = length - len;
		err = tcp_output(pcb);
		/*If enable the copy option, change the flag for next write*/
		if (espconn_copy_disabled(ptcp_sent)){
			if (ptcp_sent->pcommon.ptail->unsent == 0) {
				ptcp_sent->pcommon.write_flag = true;
				ets_post(espconn_TaskPrio, SIG_ESPCONN_WRITE, (uint32_t)ptcp_sent);
			}
		}
        espconn_printf("espconn_tcp_sent %d\n", err);
    }
    return err;
}

/******************************************************************************
 * FunctionName : espconn_close
 * Description  : The connection has been successfully closed.
 * Parameters   : arg -- Additional argument to pass to the callback function
 * Returns      : none
*******************************************************************************/
void ICACHE_FLASH_ATTR espconn_tcp_disconnect(espconn_msg *pdiscon,u8 type)
{
	if (pdiscon != NULL){
		/*disconnect with the host by send the FIN frame*/
		if (pdiscon->preverse != NULL)
			espconn_server_close(pdiscon, pdiscon->pcommon.pcb,type);
		else
			espconn_client_close(pdiscon, pdiscon->pcommon.pcb,type);
	} else{
		espconn_printf("espconn_tcp_disconnect err.\n");
	}
}

/******************************************************************************
 * FunctionName : espconn_tcp_recv
 * Description  : Data has been received on this pcb.
 * Parameters   : arg -- Additional argument to pass to the callback function
 *                pcb -- The connection pcb which received data
 *                p -- The received data (or NULL when the connection has been closed!)
 *                err -- An error code if there has been an error receiving
 * Returns      : ERR_ABRT: if you have called tcp_abort from within the function!
*******************************************************************************/
static err_t ICACHE_FLASH_ATTR
espconn_tcp_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
    espconn_msg *precv_cb = arg;
    struct pbuf *pthis = NULL;
    uint8_t *ring = NULL;
    size_t  bytes_used = 0;

    tcp_arg(pcb, arg);

    if (precv_cb->readbuf == NULL) {
        precv_cb->readbuf = ringbuf_new(TCP_WND);
        if (precv_cb->readbuf == NULL)
            return ESPCONN_MEM;
    }

    if (err == ERR_OK) {
        precv_cb->pcommon.recv_check = 0;
        if (p != NULL) {
            /*store the data to the adapter for application fetches it proactive*/
            for (pthis = p; pthis != NULL ; pthis = pthis->next) {
                ring = ringbuf_memcpy_into(precv_cb->readbuf, pthis->payload, pthis->len);
                if (ring)
                    pbuf_free(pthis);
                else
                    break;
            }
            bytes_used = ringbuf_bytes_used(precv_cb->readbuf);

            /*switch the state of espconn for application process*/
            precv_cb->pespconn->state = ESPCONN_READ;
            precv_cb->pcommon.pcb = pcb;
            if (precv_cb->pespconn->recv_callback != NULL) {
                precv_cb->pespconn->recv_callback(precv_cb->pespconn, NULL, bytes_used);
            }

            /*switch the state of espconn for next packet copy*/
            if (pcb->state == ESTABLISHED)
                precv_cb->pespconn->state = ESPCONN_CONNECT;
        } else {
            if (precv_cb->preverse) {
                espconn_server_close(precv_cb, pcb, 0);
            } else {
                espconn_client_close(precv_cb, pcb, 0);
            }
        }
    }

    return ERR_OK;
}

///////////////////////////////client function/////////////////////////////////
/******************************************************************************
 * FunctionName : espconn_client_close
 * Description  : The connection shall be actively closed.
 * Parameters   : pcb -- Additional argument to pass to the callback function
 *                pcb -- the pcb to close
 * Returns      : none
*******************************************************************************/
static void ICACHE_FLASH_ATTR
espconn_client_close(void *arg, struct tcp_pcb *pcb, u8 type)
{
    err_t err;
    espconn_msg *pclose = arg;

	pclose->pcommon.pcb = pcb;
	/*avoid recalling the disconnect function*/
	tcp_recv(pcb, NULL);

	if(type == 0)
		err = tcp_close(pcb);
	else {
		tcp_sent(pcb, NULL);
		tcp_err(pcb, NULL);
		tcp_abort(pcb);
		err = ERR_OK;
	}

	if (err != ERR_OK) {
		/* closing failed, try again later */
		tcp_recv(pcb, espconn_client_recv);
	} else {
		/* closing succeeded */
		if (type == 0) {
			tcp_sent(pcb, NULL);
			tcp_err(pcb, NULL);
		}
		/*switch the state of espconn for application process*/
		pclose->pespconn->state = ESPCONN_CLOSE;
		ets_post(espconn_TaskPrio, SIG_ESPCONN_CLOSE, (uint32_t)pclose);
	}
}

//***********Code for WIFI_BLOCK from upper**************
sint8 ICACHE_FLASH_ATTR
espconn_recv_hold(struct espconn *pespconn)
{
	//1st, according to espconn code, have to find out the escpconn_msg by pespconn;
	espconn_msg *pnode = NULL;
	bool value = false;
    if (pespconn == NULL) {
        return ESPCONN_ARG;
    }
    value = espconn_find_connection(pespconn, &pnode);
	if(value != true)
	{
		os_printf("RecvHold, By pespconn,find conn_msg fail\n");
		return ESPCONN_ARG;
	}

	//2nd, the actual operation
	if(pnode->recv_hold_flag == 0)
	{
		pnode->recv_hold_flag = 1;
		pnode->recv_holded_buf_Len = 0;
	}
	return ESPCONN_OK;
}

sint8 ICACHE_FLASH_ATTR
espconn_recv_unhold(struct espconn *pespconn)
{
	//1st, according to espconn code, have to find out the escpconn_msg by pespconn;
	espconn_msg *pnode = NULL;
	bool value = false;
    if (pespconn == NULL) {
        return ESPCONN_ARG;
    }
    value = espconn_find_connection(pespconn, &pnode);
	if(value != true)
	{
		os_printf("RecvHold, By pespconn,find conn_msg fail\n");
		return ESPCONN_ARG;
	}

	//2nd, the actual operation
	if(pnode->recv_hold_flag == 1)
	{
		if(pespconn->type == ESPCONN_TCP) {
			tcp_recved(pnode->pcommon.pcb, pnode->recv_holded_buf_Len);
		}
		pnode->recv_holded_buf_Len = 0;
		pnode->recv_hold_flag = 0;
	}
	return ESPCONN_OK;
}

//***********Code for WIFI_BLOCK from upper**************
sint8 ICACHE_FLASH_ATTR
espconn_lock_recv(espconn_msg *plockmsg)
{
	if (plockmsg == NULL || plockmsg->pespconn == NULL) {
		return ESPCONN_ARG;
	}

	if (plockmsg->pespconn->recv_callback == NULL){
		if (plockmsg->readbuf == NULL){
			plockmsg->readbuf = ringbuf_new(TCP_WND);
			if (plockmsg->readbuf == NULL)
				return ESPCONN_MEM;
		}
		return espconn_recv_hold(plockmsg->pespconn);
	}

	return ESPCONN_OK;
}

sint8 ICACHE_FLASH_ATTR
espconn_unlock_recv(espconn_msg *punlockmsg)
{
	if (punlockmsg == NULL || punlockmsg->pespconn == NULL) {
		return ESPCONN_ARG;
	}

	if (punlockmsg->pespconn->recv_callback != NULL)
		return espconn_recv_unhold(punlockmsg->pespconn);

	return ESPCONN_OK;
}
/******************************************************************************
 * FunctionName : espconn_client_recv
 * Description  : Data has been received on this pcb.
 * Parameters   : arg -- Additional argument to pass to the callback function
 *                pcb -- The connection pcb which received data
 *                p -- The received data (or NULL when the connection has been closed!)
 *                err -- An error code if there has been an error receiving
 * Returns      : ERR_ABRT: if you have called tcp_abort from within the function!
*******************************************************************************/
static err_t ICACHE_FLASH_ATTR
espconn_client_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
	espconn_msg *precv_cb = arg;

	tcp_arg(pcb, arg);
	/*lock the window because of application layer don't need the data*/
	espconn_lock_recv(precv_cb);

    if (p != NULL) {
    	/*To update and advertise a larger window*/
		if(precv_cb->recv_hold_flag == 0)
        	tcp_recved(pcb, p->tot_len);
		else
			precv_cb->recv_holded_buf_Len += p->tot_len;
    }

    if (precv_cb->pespconn->recv_callback != NULL){
		if (err == ERR_OK && p != NULL) {
			char *pdata = NULL;
			u16_t length = 0;
			/*Copy the contents of a packet buffer to an application buffer.
			 *to prevent memory leaks, ensure that each allocated is deleted*/
			pdata = (char *)os_zalloc(p ->tot_len + 1);
			length = pbuf_copy_partial(p, pdata, p ->tot_len, 0);
			pbuf_free(p);

			if (length != 0) {
				/*switch the state of espconn for application process*/
				precv_cb->pespconn ->state = ESPCONN_READ;
				precv_cb->pcommon.pcb = pcb;
				precv_cb->pespconn->recv_callback(precv_cb->pespconn, pdata, length);

				/*switch the state of espconn for next packet copy*/
				if (pcb->state == ESTABLISHED)
					precv_cb->pespconn ->state = ESPCONN_CONNECT;
			}

			/*to prevent memory leaks, ensure that each allocated is deleted*/
			os_free(pdata);
			pdata = NULL;
		}
    } else{
    	/*unregister receive function*/
    	struct pbuf *pthis = NULL;
    	for (pthis = p; pthis != NULL; pthis = pthis->next) {
    		ringbuf_memcpy_into(precv_cb->readbuf, pthis->payload, pthis->len);
    		pbuf_free(pthis);
    	}
    }

    if (err == ERR_OK && p == NULL) {
        espconn_client_close(precv_cb, pcb,0);
    }

    return ERR_OK;
}

/******************************************************************************
 * FunctionName : espconn_tcp_write
 * Description  : write the packet which in the active connection's list.
 * Parameters   : arg -- the node pointer which reverse the packet
 * Returns      : ESPCONN_MEM: memory error
 * 				  ESPCONN_OK:have enough space for write packet
*******************************************************************************/
err_t ICACHE_FLASH_ATTR espconn_tcp_write(void *arg)
{
	espconn_msg *pwrite = arg;
	err_t err = ERR_OK;
	struct tcp_pcb *pcb = pwrite->pcommon.pcb;
	/*for one active connection,limit the sender buffer space*/
	if (tcp_nagle_disabled(pcb) && (pcb->snd_queuelen >= TCP_SND_QUEUELEN))
		return ESPCONN_MEM;

	while (tcp_sndbuf(pcb) != 0){
		if (pwrite->pcommon.ptail != NULL) {
			/*Find the node whether in the list's tail or not*/
			if (pwrite->pcommon.ptail->unsent == 0) {
				pwrite->pcommon.ptail = pwrite->pcommon.ptail->pnext;
				continue;
			}

			/*Send the packet for the active connection*/
			err = espconn_tcp_sent(pwrite, pwrite->pcommon.ptail->punsent,pwrite->pcommon.ptail->unsent);
			if (err != ERR_OK)
				break;
		} else
			break;
	}
	return err;
}

/******************************************************************************
 * FunctionName : espconn_tcp_reconnect
 * Description  : reconnect with host
 * Parameters   : arg -- Additional argument to pass to the callback function
 * Returns      : none
*******************************************************************************/
static void ICACHE_FLASH_ATTR espconn_tcp_finish(void *arg)
{
	espconn_msg *pfinish = arg;
	espconn_buf *premove = NULL;
	uint16 len = 0;
	espconn_tcp_write(pfinish);
	while (pfinish->pcommon.pbuf != NULL){
		premove = pfinish->pcommon.pbuf;
		pfinish->pcommon.pbuf->tot_len += len;
		/*application packet has been sent and acknowledged by the remote host,
		 * to prevent memory leaks, ensure that each allocated is deleted*/
		if (premove->tot_len >= premove->len){
			espconn_pbuf_delete(&pfinish->pcommon.pbuf,premove);
			len = premove->tot_len - premove->len;
			pfinish->pcommon.packet_info.sent_length = premove->len;
			os_free(premove);
			premove = NULL;
			pfinish->pespconn->state = ESPCONN_CONNECT;
			if (pfinish->pespconn->sent_callback != NULL) {
				pfinish->pespconn->sent_callback(pfinish->pespconn);
			}
			pfinish->pcommon.packet_info.sent_length = len;
		} else
			break;
	}
}

/******************************************************************************
 * FunctionName : espconn_client_sent
 * Description  : Data has been sent and acknowledged by the remote host.
 *                This means that more data can be sent.
 * Parameters   : arg -- Additional argument to pass to the callback function
 *                pcb -- The connection pcb for which data has been acknowledged
 *                len -- The amount of bytes acknowledged
 * Returns      : ERR_OK: try to send some data by calling tcp_output
 *                ERR_ABRT: if you have called tcp_abort from within the function!
*******************************************************************************/
static err_t ICACHE_FLASH_ATTR
espconn_client_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
{
	espconn_msg *psent_cb = arg;

	psent_cb->pcommon.pcb = pcb;
	psent_cb->pcommon.pbuf->tot_len += len;
	psent_cb->pcommon.packet_info.sent_length = len;

	/*Send more data for one active connection*/
	espconn_tcp_finish(psent_cb);

    return ERR_OK;
}

/******************************************************************************
 * FunctionName : espconn_client_err
 * Description  : The pcb had an error and is already deallocated.
 *                The argument might still be valid (if != NULL).
 * Parameters   : arg -- Additional argument to pass to the callback function
 *                err -- Error code to indicate why the pcb has been closed
 * Returns      : none
*******************************************************************************/
static void ICACHE_FLASH_ATTR
espconn_client_err(void *arg, err_t err)
{
	espconn_msg *perr_cb = arg;
	struct tcp_pcb *pcb = NULL;
    LWIP_UNUSED_ARG(err);

    if (perr_cb != NULL) {
        pcb = perr_cb->pcommon.pcb;
        perr_cb->pespconn->state = ESPCONN_CLOSE;
        espconn_printf("espconn_client_err %d %d %d\n", pcb->state, pcb->nrtx, err);

//        /*remove the node from the client's active connection list*/
//        espconn_list_delete(&plink_active, perr_cb);

        /*Set the error code depend on the error type and control block state*/
        if (err == ERR_ABRT) {
        	switch (pcb->state) {
					case SYN_SENT:
						if (pcb->nrtx == TCP_SYNMAXRTX) {
							perr_cb->pcommon.err = ESPCONN_CONN;
						} else {
							perr_cb->pcommon.err = err;
						}

						break;

					case ESTABLISHED:
						if (pcb->nrtx == TCP_MAXRTX) {
							perr_cb->pcommon.err = ESPCONN_TIMEOUT;
						} else {
							perr_cb->pcommon.err = err;
						}
						break;

					case FIN_WAIT_1:
						if (pcb->nrtx == TCP_MAXRTX) {
							perr_cb->pcommon.err = ESPCONN_CLSD;
						} else {
							perr_cb->pcommon.err = err;
						}
						break;
					case FIN_WAIT_2:
						perr_cb->pcommon.err = ESPCONN_CLSD;
						break;
					case CLOSED:
						perr_cb->pcommon.err = ESPCONN_CONN;
						break;
				}
			} else {
				perr_cb->pcommon.err = err;
			}
        	/*post the singer to the task for processing the connection*/
        	ets_post(espconn_TaskPrio, SIG_ESPCONN_ERRER, (uint32_t)perr_cb);
		}
}

/******************************************************************************
 * FunctionName : espconn_client_connect
 * Description  : A new incoming connection has been connected.
 * Parameters   : arg -- Additional argument to pass to the callback function
 *                tpcb -- The connection pcb which is connected
 *                err -- An unused error code, always ERR_OK currently
 * Returns      : connection result
*******************************************************************************/
static err_t ICACHE_FLASH_ATTR
espconn_client_connect(void *arg, struct tcp_pcb *tpcb, err_t err)
{
    espconn_msg *pcon = arg;

    espconn_printf("espconn_client_connect pcon %p tpcb %p\n", pcon, tpcb);
    if (err == ERR_OK){
    	/*Reserve the remote information for current active connection*/
		pcon->pespconn->state = ESPCONN_CONNECT;
		pcon->pcommon.err = err;
		pcon->pcommon.pcb = tpcb;
		pcon->pcommon.local_port = tpcb->local_port;
		pcon->pcommon.local_ip = tpcb->local_ip.addr;
		pcon->pcommon.remote_port = tpcb->remote_port;
		pcon->pcommon.remote_ip[0] = ip4_addr1_16(&tpcb->remote_ip);
		pcon->pcommon.remote_ip[1] = ip4_addr2_16(&tpcb->remote_ip);
		pcon->pcommon.remote_ip[2] = ip4_addr3_16(&tpcb->remote_ip);
		pcon->pcommon.remote_ip[3] = ip4_addr4_16(&tpcb->remote_ip);
		pcon->pcommon.write_flag = true;
		tcp_arg(tpcb, (void *) pcon);

		/*Set the specify function that should be called
		 * when TCP data has been successfully delivered,
		 * when active connection receives data*/
		tcp_sent(tpcb, espconn_client_sent);
		tcp_recv(tpcb, espconn_client_recv);
		/*Disable Nagle algorithm default*/
		tcp_nagle_disable(tpcb);
		/*Default set the total number of espconn_buf on the unsent lists for one*/
		espconn_tcp_set_buf_count(pcon->pespconn, 1);

		if (pcon->pespconn->proto.tcp->connect_callback != NULL) {
			pcon->pespconn->proto.tcp->connect_callback(pcon->pespconn);
		}

		/*Enable block option for fetches the data proactive*/
        if (espconn_manual_recv_disabled(pcon))
            tcp_recv(tpcb, espconn_tcp_recv);

		/*Enable keep alive option*/
		if (espconn_keepalive_disabled(pcon))
			espconn_keepalive_enable(tpcb);

//		/*lock the window because of application layer don't need the data*/
//		espconn_lock_recv(pcon);
    } else{
    	os_printf("err in host connected (%s)\n",lwip_strerr(err));
    }
    return err;
}

/******************************************************************************
 * FunctionName : espconn_tcp_client
 * Description  : Initialize the client: set up a connect PCB and bind it to
 *                the defined port
 * Parameters   : espconn -- the espconn used to build client
 * Returns      : none
*******************************************************************************/
sint8 ICACHE_FLASH_ATTR
espconn_tcp_client(struct espconn *espconn)
{
    struct tcp_pcb *pcb = NULL;
    struct ip_addr ipaddr;
    espconn_msg *pclient = NULL;

    /*Creates a new client control message*/
	pclient = (espconn_msg *)os_zalloc(sizeof(espconn_msg));
	if (pclient == NULL){
		return ESPCONN_MEM;
 	}

	/*Set an IP address given for Little-endian.*/
    IP4_ADDR(&ipaddr, espconn->proto.tcp->remote_ip[0],
    		espconn->proto.tcp->remote_ip[1],
    		espconn->proto.tcp->remote_ip[2],
    		espconn->proto.tcp->remote_ip[3]);

    /*Creates a new TCP protocol control block*/
    pcb = tcp_new();

    if (pcb == NULL) {
    	/*to prevent memory leaks, ensure that each allocated is deleted*/
    	os_free(pclient);
    	pclient = NULL;
        return ESPCONN_MEM;
    } else {

    	/*insert the node to the active connection list*/
    	espconn_list_creat(&plink_active, pclient);
    	tcp_arg(pcb, (void *)pclient);
    	tcp_err(pcb, espconn_client_err);
    	pclient->preverse = NULL;
    	pclient->pespconn = espconn;
    	pclient->pespconn->state = ESPCONN_WAIT;
    	pclient->pcommon.pcb = pcb;
    	tcp_bind(pcb, IP_ADDR_ANY, pclient->pespconn->proto.tcp->local_port);
#if 0
    	pclient->pcommon.err = tcp_bind(pcb, IP_ADDR_ANY, pclient->pespconn->proto.tcp->local_port);
    	if (pclient->pcommon.err != ERR_OK){
    		/*remove the node from the client's active connection list*/
    		espconn_list_delete(&plink_active, pclient);
    		memp_free(MEMP_TCP_PCB, pcb);
    		os_free(pclient);
    		pclient = NULL;
    		return ERR_USE;
    	}
#endif
        /*Establish the connection*/
    	pclient->espconn_mode = ESPCONN_TCPCLIENT_MODE;
        pclient->pcommon.err = tcp_connect(pcb, &ipaddr,
        		pclient->pespconn->proto.tcp->remote_port, espconn_client_connect);
        if (pclient->pcommon.err == ERR_RTE){
			/*remove the node from the client's active connection list*/
			espconn_list_delete(&plink_active, pclient);
			espconn_kill_pcb(pcb->local_port);
			os_free(pclient);
			pclient = NULL;
			return ESPCONN_RTE;
		}
        return pclient->pcommon.err;
    }
}

///////////////////////////////server function/////////////////////////////////
/******************************************************************************
 * FunctionName : espconn_server_close
 * Description  : The connection shall be actively closed.
 * Parameters   : arg -- Additional argument to pass to the callback function
 *                pcb -- the pcb to close
 * Returns      : none
*******************************************************************************/
static void ICACHE_FLASH_ATTR
espconn_server_close(void *arg, struct tcp_pcb *pcb,u8 type)
{
    err_t err;
    espconn_msg *psclose = arg;

	psclose->pcommon.pcb = pcb;
	/*avoid recalling the disconnect function*/
	tcp_recv(pcb, NULL);

	if(type ==0)
		err = tcp_close(pcb);
	else {
		tcp_poll(pcb, NULL, 0);
	    tcp_sent(pcb, NULL);
		tcp_err(pcb, NULL);
		tcp_abort(pcb);
		err = ERR_OK;
	}

    if (err != ERR_OK) {
        /* closing failed, try again later */
        tcp_recv(pcb, espconn_server_recv);
    } else {
        /* closing succeeded */
    	if (type == 0) {
			tcp_poll(pcb, NULL, 0);
			tcp_sent(pcb, NULL);
			tcp_err(pcb, NULL);
    	}
        /*switch the state of espconn for application process*/
        psclose->pespconn->state = ESPCONN_CLOSE;
        ets_post(espconn_TaskPrio, SIG_ESPCONN_CLOSE, (uint32_t)psclose);
	}
}

/******************************************************************************
 * FunctionName : espconn_server_recv
 * Description  : Data has been received on this pcb.
 * Parameters   : arg -- Additional argument to pass to the callback function
 *                pcb -- The connection pcb which received data
 *                p -- The received data (or NULL when the connection has been closed!)
 *                err -- An error code if there has been an error receiving
 * Returns      : ERR_ABRT: if you have called tcp_abort from within the function!
*******************************************************************************/
static err_t ICACHE_FLASH_ATTR
espconn_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
	espconn_msg *precv_cb = arg;

    tcp_arg(pcb, arg);
    espconn_printf("server has application data received: %d\n", system_get_free_heap_size());
    /*lock the window because of application layer don't need the data*/
    espconn_lock_recv(precv_cb);

    if (p != NULL) {
    	/*To update and advertise a larger window*/
		if(precv_cb->recv_hold_flag == 0)
        	tcp_recved(pcb, p->tot_len);
		else
			precv_cb->recv_holded_buf_Len += p->tot_len;
    }

    /*register receive function*/
	if (precv_cb->pespconn->recv_callback != NULL) {
		if (err == ERR_OK && p != NULL) {
			u8_t *data_ptr = NULL;
			u32_t data_cntr = 0;
			/*clear the count for connection timeout*/
			precv_cb->pcommon.recv_check = 0;
			/*Copy the contents of a packet buffer to an application buffer.
			 *to prevent memory leaks, ensure that each allocated is deleted*/
			data_ptr = (u8_t *) os_zalloc(p ->tot_len + 1);
			data_cntr = pbuf_copy_partial(p, data_ptr, p->tot_len, 0);
			pbuf_free(p);

			if (data_cntr != 0) {
				/*switch the state of espconn for application process*/
				precv_cb->pespconn->state = ESPCONN_READ;
				precv_cb->pcommon.pcb = pcb;
				precv_cb->pespconn->recv_callback(precv_cb->pespconn, data_ptr, data_cntr);

				/*switch the state of espconn for next packet copy*/
				if (pcb->state == ESTABLISHED)
					precv_cb->pespconn->state = ESPCONN_CONNECT;
			}

			/*to prevent memory leaks, ensure that each allocated is deleted*/
			os_free(data_ptr);
			data_ptr = NULL;
			espconn_printf("server's application data has been processed: %d\n", system_get_free_heap_size());
		}
	} else {
		/*unregister receive function*/
		struct pbuf *pthis = NULL;
		for (pthis = p; pthis != NULL; pthis = pthis->next) {
			ringbuf_memcpy_into(precv_cb->readbuf, pthis->payload, pthis->len);
			pbuf_free(pthis);
		}
	}

	if (err == ERR_OK && p == NULL) {
		espconn_server_close(precv_cb, pcb, 0);
	}
    return ERR_OK;
}

/******************************************************************************
 * FunctionName : espconn_server_sent
 * Description  : Data has been sent and acknowledged by the remote host.
 * This means that more data can be sent.
 * Parameters   : arg -- Additional argument to pass to the callback function
 *                pcb -- The connection pcb for which data has been acknowledged
 *                len -- The amount of bytes acknowledged
 * Returns      : ERR_OK: try to send some data by calling tcp_output
 *                ERR_ABRT: if you have called tcp_abort from within the function!
*******************************************************************************/
static err_t ICACHE_FLASH_ATTR
espconn_server_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
{
	espconn_msg *psent_cb = arg;

	psent_cb->pcommon.pcb = pcb;
	psent_cb->pcommon.recv_check = 0;
	psent_cb->pcommon.pbuf->tot_len += len;
	psent_cb->pcommon.packet_info.sent_length = len;

	/*Send more data for one active connection*/
	espconn_tcp_finish(psent_cb);
    return ERR_OK;
}

/******************************************************************************
 * FunctionName : espconn_server_poll
 * Description  : The poll function is called every 3nd second.
 * If there has been no data sent (which resets the retries) in 3 seconds, close.
 * If the last portion of a file has not been sent in 3 seconds, close.
 *
 * This could be increased, but we don't want to waste resources for bad connections.
 * Parameters   : arg -- Additional argument to pass to the callback function
 *                pcb -- The connection pcb for which data has been acknowledged
 * Returns      : ERR_OK: try to send some data by calling tcp_output
 *                ERR_ABRT: if you have called tcp_abort from within the function!
*******************************************************************************/
static err_t ICACHE_FLASH_ATTR
espconn_server_poll(void *arg, struct tcp_pcb *pcb)
{
	espconn_msg *pspoll_cb = arg;

	/*exception calling abandon the connection for send a RST frame*/
    if (arg == NULL) {
        tcp_abandon(pcb, 0);
        tcp_poll(pcb, NULL, 0);
        return ERR_OK;
    }

    espconn_printf("espconn_server_poll %d %d\n", pspoll_cb->pcommon.recv_check, pcb->state);
    pspoll_cb->pcommon.pcb = pcb;
    if (pcb->state == ESTABLISHED) {
		pspoll_cb->pcommon.recv_check++;
		if (pspoll_cb->pcommon.timeout != 0){/*no data sent in one active connection's set timeout, close.*/
			if (pspoll_cb->pcommon.recv_check >= pspoll_cb->pcommon.timeout) {
				pspoll_cb->pcommon.recv_check = 0;
				espconn_server_close(pspoll_cb, pcb,0);
			}
		} else {
			espconn_msg *ptime_msg = pserver_list;
			while (ptime_msg != NULL) {
				if (ptime_msg->pespconn == pspoll_cb->preverse){
					if (ptime_msg->pcommon.timeout != 0){/*no data sent in server's set timeout, close.*/
						if (pspoll_cb->pcommon.recv_check >= ptime_msg->pcommon.timeout){
							pspoll_cb->pcommon.recv_check = 0;
							espconn_server_close(pspoll_cb, pcb,0);
						}
					} else {/*don't close for ever*/
						pspoll_cb->pcommon.recv_check = 0;
					}
					break;
				}
				ptime_msg = ptime_msg->pnext;
			}
		}
    } else {
        espconn_server_close(pspoll_cb, pcb,0);
    }

    return ERR_OK;
}

/******************************************************************************
 * FunctionName : esponn_server_err
 * Description  : The pcb had an error and is already deallocated.
 *                The argument might still be valid (if != NULL).
 * Parameters   : arg -- Additional argument to pass to the callback function
 *                err -- Error code to indicate why the pcb has been closed
 * Returns      : none
*******************************************************************************/
static void ICACHE_FLASH_ATTR
esponn_server_err(void *arg, err_t err)
{
	espconn_msg *pserr_cb = arg;
	struct tcp_pcb *pcb = NULL;
    if (pserr_cb != NULL) {

    	pcb = pserr_cb->pcommon.pcb;
    	pserr_cb->pespconn->state = ESPCONN_CLOSE;

//		/*remove the node from the server's active connection list*/
//		espconn_list_delete(&plink_active, pserr_cb);

    	/*Set the error code depend on the error type and control block state*/
		if (err == ERR_ABRT) {
			switch (pcb->state) {
				case SYN_RCVD:
					if (pcb->nrtx == TCP_SYNMAXRTX) {
						pserr_cb->pcommon.err = ESPCONN_CONN;
					} else {
						pserr_cb->pcommon.err = err;
					}

					break;

				case ESTABLISHED:
					if (pcb->nrtx == TCP_MAXRTX) {
						pserr_cb->pcommon.err = ESPCONN_TIMEOUT;
					} else {
						pserr_cb->pcommon.err = err;
					}

					break;

				case CLOSE_WAIT:
					if (pcb->nrtx == TCP_MAXRTX) {
						pserr_cb->pcommon.err = ESPCONN_CLSD;
					} else {
						pserr_cb->pcommon.err = err;
					}
					break;
				case LAST_ACK:
					pserr_cb->pcommon.err = ESPCONN_CLSD;
					break;

				case CLOSED:
					pserr_cb->pcommon.err = ESPCONN_CONN;
					break;
				default :
					break;
			}
		} else {
			pserr_cb->pcommon.err = err;
		}
		/*post the singer to the task for processing the connection*/
		ets_post(espconn_TaskPrio, SIG_ESPCONN_ERRER, (uint32_t)pserr_cb);
    }
}

/******************************************************************************
 * FunctionName : espconn_tcp_accept
 * Description  : A new incoming connection has been accepted.
 * Parameters   : arg -- Additional argument to pass to the callback function
 *                pcb -- The connection pcb which is accepted
 *                err -- An unused error code, always ERR_OK currently
 * Returns      : acception result
*******************************************************************************/
static err_t ICACHE_FLASH_ATTR
espconn_tcp_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{
    struct espconn *espconn = arg;
    espconn_msg *paccept = NULL;
    remot_info *pinfo = NULL;
    LWIP_UNUSED_ARG(err);

    if (!espconn || !espconn->proto.tcp) {
    	return ERR_ARG;
    }

    tcp_arg(pcb, paccept);
    tcp_err(pcb, esponn_server_err);
    /*Ensure the active connection is less than the count of active connections on the server*/
    espconn_get_connection_info(espconn, &pinfo , 0);
	espconn_printf("espconn_tcp_accept link_cnt: %d\n", espconn->link_cnt);
	if (espconn->link_cnt == espconn_tcp_get_max_con_allow(espconn))
		return ERR_ISCONN;

	/*Creates a new active connect control message*/
    paccept = (espconn_msg *)os_zalloc(sizeof(espconn_msg));
    tcp_arg(pcb, paccept);

	if (paccept == NULL)
		return ERR_MEM;
	/*Insert the node to the active connection list*/
	espconn_list_creat(&plink_active, paccept);

    paccept->preverse = espconn;
    paccept->espconn_mode = ESPCONN_TCPSERVER_MODE;
	paccept->pespconn = (struct espconn *)os_zalloc(sizeof(struct espconn));
	if (paccept->pespconn == NULL)
		return ERR_MEM;
	paccept->pespconn->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp));
	if (paccept->pespconn->proto.tcp == NULL)
		return ERR_MEM;

	/*Reserve the remote information for current active connection*/
	paccept->pcommon.pcb = pcb;

	paccept->pcommon.remote_port = pcb->remote_port;
	paccept->pcommon.remote_ip[0] = ip4_addr1_16(&pcb->remote_ip);
	paccept->pcommon.remote_ip[1] = ip4_addr2_16(&pcb->remote_ip);
	paccept->pcommon.remote_ip[2] = ip4_addr3_16(&pcb->remote_ip);
	paccept->pcommon.remote_ip[3] = ip4_addr4_16(&pcb->remote_ip);
	paccept->pcommon.write_flag = true;

	os_memcpy(espconn->proto.tcp->remote_ip, paccept->pcommon.remote_ip, 4);
	espconn->proto.tcp->remote_port = pcb->remote_port;
	espconn->state = ESPCONN_CONNECT;
	espconn_copy_partial(paccept->pespconn, espconn);

	/*Set the specify function that should be called
	 * when TCP data has been successfully delivered,
	 * when active connection receives data,
	 * or periodically from active connection*/
	tcp_sent(pcb, espconn_server_sent);
	tcp_recv(pcb, espconn_server_recv);
	tcp_poll(pcb, espconn_server_poll, 4); /* every 1 seconds */
	/*Disable Nagle algorithm default*/
	tcp_nagle_disable(pcb);
	/*Default set the total number of espconn_buf on the unsent lists for one*/
	espconn_tcp_set_buf_count(paccept->pespconn, 1);

	if (paccept->pespconn->proto.tcp->connect_callback != NULL) {
		paccept->pespconn->proto.tcp->connect_callback(paccept->pespconn);
	}

	/*Enable block option for fetches the data proactive*/
	if (espconn_manual_recv_disabled(paccept))
	    tcp_recv(pcb, espconn_tcp_recv);

	/*Enable keep alive option*/
	if (espconn_keepalive_disabled(paccept))
		espconn_keepalive_enable(pcb);

//	/*lock the window because of application layer don't need the data*/
//	espconn_lock_recv(paccept);
    return ERR_OK;
}

/******************************************************************************
 * FunctionName : espconn_tcp_server
 * Description  : Initialize the server: set up a listening PCB and bind it to
 *                the defined port
 * Parameters   : espconn -- the espconn used to build server
 * Returns      : none
*******************************************************************************/
sint8 ICACHE_FLASH_ATTR
espconn_tcp_server(struct espconn *espconn)
{
    struct tcp_pcb *pcb = NULL;
    espconn_msg *pserver = NULL;

    /*Creates a new server control message*/
    pserver = (espconn_msg *)os_zalloc(sizeof(espconn_msg));
    if (pserver == NULL){
    	return ESPCONN_MEM;
    }

    /*Creates a new TCP protocol control block*/
    pcb = tcp_new();
    if (pcb == NULL) {
    	/*to prevent memory leaks, ensure that each allocated is deleted*/
    	os_free(pserver);
    	pserver = NULL;
        return ESPCONN_MEM;
    } else {
    	struct tcp_pcb *lpcb = NULL;
    	/*Binds the connection to a local port number and any IP address*/
        tcp_bind(pcb, IP_ADDR_ANY, espconn->proto.tcp->local_port);
        lpcb = pcb;
        /*malloc and set the state of the connection to be LISTEN*/
        pcb = tcp_listen(pcb);
        if (pcb != NULL) {
        	/*insert the node to the active connection list*/
        	espconn_list_creat(&pserver_list, pserver);
        	pserver->preverse = pcb;
        	pserver->pespconn = espconn;
        	pserver->count_opt = MEMP_NUM_TCP_PCB;
			pserver->pcommon.timeout = 0x0a;
            espconn ->state = ESPCONN_LISTEN;
            /*set the specify argument that should be passed callback function*/
            tcp_arg(pcb, (void *)espconn);
            /*accept callback function to call for this control block*/
            tcp_accept(pcb, espconn_tcp_accept);
            return ESPCONN_OK;
        } else {
        	/*to prevent memory leaks, ensure that each allocated is deleted*/
        	memp_free(MEMP_TCP_PCB,lpcb);
        	os_free(pserver);
        	pserver = NULL;
            return ESPCONN_MEM;
        }
    }
}

/******************************************************************************
 * FunctionName : espconn_tcp_delete
 * Description  : delete the server: delete a listening PCB and free it
 * Parameters   : pdeletecon -- the espconn used to delete a server
 * Returns      : none
*******************************************************************************/
sint8 ICACHE_FLASH_ATTR espconn_tcp_delete(struct espconn *pdeletecon)
{
	err_t err;
	remot_info *pinfo = NULL;
	espconn_msg *pdelete_msg = NULL;
	struct tcp_pcb *pcb = NULL;

	if (pdeletecon == NULL)
		return ESPCONN_ARG;

	espconn_get_connection_info(pdeletecon, &pinfo , 0);
	/*make sure all the active connection have been disconnect*/
	if (pdeletecon->link_cnt != 0)
		return ESPCONN_INPROGRESS;
	else {
		espconn_printf("espconn_tcp_delete %p\n",pdeletecon);
		pdelete_msg = pserver_list;
		while (pdelete_msg != NULL){
			if (pdelete_msg->pespconn == pdeletecon){
				/*remove the node from the client's active connection list*/
				espconn_list_delete(&pserver_list, pdelete_msg);
				pcb = pdelete_msg->preverse;
				os_printf("espconn_tcp_delete %d, %d\n",pcb->state, pcb->local_port);
				espconn_kill_pcb(pcb->local_port);
				err = tcp_close(pcb);
				os_free(pdelete_msg);
				pdelete_msg = NULL;
				break;
			}
			pdelete_msg = pdelete_msg->pnext;
		}
		if (err == ERR_OK)
			return err;
		else
			return ESPCONN_ARG;
	}
}

/******************************************************************************
 * FunctionName : espconn_init
 * Description  : used to init the function that should be used when
 * Parameters   : none
 * Returns      : none
*******************************************************************************/
void ICACHE_FLASH_ATTR espconn_init(void)
{
  ets_task(espconn_Task, espconn_TaskPrio, espconn_TaskQueue, espconn_TaskQueueLen);
}
