/******************************************************************************
*
* Copyright (C) 2009 - 2014 Xilinx, Inc.  All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* XILINX  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/**************************************************************
 * Project description 26/07/2018
 * Initialize HiCCE -> DMA_tranfer -> waiting for Ethernet
 * waiting for arrive "read" command
 * sent 32x1024*4*4 (bytes) data segments to the PC over TCP/IP connection
 * in DMA - transfer block by block intan chip data
 * in ethernet send whole data
 *
 *************************************************************/
#include <stdio.h>
#include <string.h>
#include "project_para.h"

#include "lwip/err.h"
#include "lwip/tcp.h"
#if defined (__arm__) || defined (__aarch64__)
#include "xil_printf.h"
#endif

static volatile u8* cur_buf = NULL;

static struct tcp_pcb *connected_pcb = NULL;
#define TCP_PAYLOAD_SIZE	( 1024 )

static char *strvar;
int val, preval=0;

int transfer_data() {
#if __arm__
	int copy = 3;
#else
	int copy = 0;
#endif
	err_t err;
	struct tcp_pcb *tpcb = connected_pcb;

	if (!connected_pcb)
		return ERR_OK;

	if(strncmp(strvar, "read", 4) == 0){
			while ((eTxCnt < No_KB_Segs) & (tcp_sndbuf(tpcb) > SEND_BUFSIZE)) {
//					u32 *EthRxBufferPtr;
//					EthRxBufferPtr = (u32 *)(DMA_RX_BASEADDR+1024*eTxCnt);

//					xil_printf("EthRxBufferPtr = 0x%x, %d\r\n", cur_buf, eTxCnt);

					err = tcp_write(tpcb,( void* ) cur_buf, 1024, copy);
					if (err != ERR_OK) {
						xil_printf("txperf: Error on tcp_write: %d\r\n", err);
						connected_pcb = NULL;
						return -1;
					}
					err = tcp_output(tpcb);
					if (err != ERR_OK) {
						xil_printf("txperf: Error on tcp_output: %d\r\n",err);
					}
					if(err == ERR_OK){
						eTxCnt = eTxCnt+1;
						cur_buf += TCP_PAYLOAD_SIZE;
					}
			}
			if(eTxCnt >= No_KB_Segs){
				eTxCnt = 0;
//				XTime_GetTime(&tStart);
//				preval = tStart;
//				printf("Output took %.2f us.\n", 1.0 * (preval - val) / (COUNTS_PER_SECOND/1000000));
//				val = preval;

				HiCCE_DMA_Tranfer();
//				HiCCE_DMA_Tranfer_sim();			// for simulate the DMA
				if(strncmp(globalstr_intan, "INTAN_A",7)==0){
					cur_buf = ( u8* ) INTAN_A_RX_BUF_BASE;
				}else if(strncmp(globalstr_intan, "INTAN_B",7)==0){
					cur_buf = ( u8* ) INTAN_B_RX_BUF_BASE;
				}else if(strncmp(globalstr_intan, "INTAN_C",7)==0){
					cur_buf = ( u8* ) INTAN_C_RX_BUF_BASE;
				}else if(strncmp(globalstr_intan, "INTAN_D",7)==0){
					cur_buf = ( u8* ) INTAN_D_RX_BUF_BASE;
				}else{
					cur_buf = ( u8* ) INTAN_A_RX_BUF_BASE;
				}

//				XTime_GetTime(&tEnd);

//				printf("Output took %llu clock cycles.\r\n", 2*(tEnd - tStart));
//				printf("Output took %.2f us.\n", 1.0 * (tEnd - tStart) / (COUNTS_PER_SECOND/1000000));

			}

	}
	else if(strncmp(strvar, "all", 3) == 0){
			while ((eTxCnt < No_KB_Segs) & (tcp_sndbuf(tpcb) > SEND_BUFSIZE)) {
//					u32 *EthRxBufferPtr;
//					EthRxBufferPtr = (u32 *)(DMA_RX_BASEADDR+1024*eTxCnt);

//					xil_printf("EthRxBufferPtr = 0x%x, %d\r\n", cur_buf, eTxCnt);

					err = tcp_write(tpcb,( void* ) cur_buf, 1024, copy);
					if (err != ERR_OK) {
						xil_printf("txperf: Error on tcp_write: %d\r\n", err);
						connected_pcb = NULL;
						return -1;
					}
					err = tcp_output(tpcb);
					if (err != ERR_OK) {
						xil_printf("txperf: Error on tcp_output: %d\r\n",err);
					}
					if(err == ERR_OK){
						eTxCnt = eTxCnt+1;
						cur_buf += TCP_PAYLOAD_SIZE;
					}
			}
			if(eTxCnt >= 4*No_KB_Segs){
				eTxCnt = 0;
//				XTime_GetTime(&tStart);
//				preval = tStart;
//				printf("Output took %.2f us.\n", 1.0 * (preval - val) / (COUNTS_PER_SECOND/1000000));
//				val = preval;

				HiCCE_DMA_Tranfer();
//				HiCCE_DMA_Tranfer_sim();			// for simulate the DMA
				if(strncmp(globalstr_intan, "INTAN_A",7)==0){
					cur_buf = ( u8* ) INTAN_A_RX_BUF_BASE;
				}else if(strncmp(globalstr_intan, "INTAN_B",7)==0){
					cur_buf = ( u8* ) INTAN_B_RX_BUF_BASE;
				}else if(strncmp(globalstr_intan, "INTAN_C",7)==0){
					cur_buf = ( u8* ) INTAN_C_RX_BUF_BASE;
				}else if(strncmp(globalstr_intan, "INTAN_D",7)==0){
					cur_buf = ( u8* ) INTAN_D_RX_BUF_BASE;
				}else if(strncmp(globalstr_intan, "ALL",3)==0){
					cur_buf = ( u8* ) INTAN_A_RX_BUF_BASE;
				}else{
					cur_buf = ( u8* ) INTAN_A_RX_BUF_BASE;
				}

//				XTime_GetTime(&tEnd);

//				printf("Output took %llu clock cycles.\r\n", 2*(tEnd - tStart));
//				printf("Output took %.2f us.\n", 1.0 * (tEnd - tStart) / (COUNTS_PER_SECOND/1000000));

			}

	}
	else if(strncmp(strvar, "stop", 4) == 0){
///		break;
	}

	return 0;
}

static err_t
tcp_sent_callback_fn(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
	//xil_printf(" in side the tcp_sent_callback_fn\n\r");
	//xil_printf("%d\r\n", send_buf);
//	if(eTxCnt == 32){
//	//	HiCCE_DMA_Tranfer();
//		xil_printf("Dma Transfer complete .. \n\r");
//		eTxCnt = 0;
//	}
	return ERR_OK;
}

void print_app_header()
{
	xil_printf("\n\r\n\r-----lwIP TCP echo server ------\n\r");
	xil_printf("TCP packets sent to port 6001 will be echoed back\n\r");
}

err_t recv_callback(void *arg, struct tcp_pcb *tpcb,
                               struct pbuf *p, err_t err)
{
	/* do not read the packet if we are not in ESTABLISHED state */
	if (!p) {
		tcp_close(tpcb);
		tcp_recv(tpcb, NULL);
		return ERR_OK;
	}

	/* indicate that the packet has been received */
	tcp_recved(tpcb, p->len);

	char *eth_rx_buf;
	eth_rx_buf = p->payload;
	if(strncmp(eth_rx_buf, "read", 4) == 0){
		xil_printf("receive cmd = read \n\r");

		if(strncmp(globalstr_intan, "INTAN_A",7)==0){
			cur_buf = ( u8* ) INTAN_A_RX_BUF_BASE;
		}else if(strncmp(globalstr_intan, "INTAN_B",7)==0){
			cur_buf = ( u8* ) INTAN_B_RX_BUF_BASE;
		}else if(strncmp(globalstr_intan, "INTAN_C",7)==0){
			cur_buf = ( u8* ) INTAN_C_RX_BUF_BASE;
		}else if(strncmp(globalstr_intan, "INTAN_D",7)==0){
			cur_buf = ( u8* ) INTAN_D_RX_BUF_BASE;
		}else{
			cur_buf = ( u8* ) INTAN_A_RX_BUF_BASE;
		}

		strvar = "read";
		eTxCnt = 0;
	}
	else if(strncmp(eth_rx_buf, "INTANA", 6) == 0){
		cur_buf = ( u8* ) INTAN_A_RX_BUF_BASE;
		globalstr_intan = "INTAN_A";
		strvar = "read";
		eTxCnt = 0;
		init_intan();
	}
	else if(strncmp(eth_rx_buf, "INTANB", 6) == 0){
		cur_buf = ( u8* ) INTAN_B_RX_BUF_BASE;
		globalstr_intan = "INTAN_B";
		strvar = "read";
		eTxCnt = 0;
		init_intan();
	}
	else if(strncmp(eth_rx_buf, "INTANC", 6) == 0){
		cur_buf = ( u8* ) INTAN_C_RX_BUF_BASE;
		globalstr_intan = "INTAN_C";
		strvar = "read";
		eTxCnt = 0;
		init_intan();
	}
	else if(strncmp(eth_rx_buf, "INTAND", 6) == 0){
		cur_buf = ( u8* ) INTAN_D_RX_BUF_BASE;
		globalstr_intan = "INTAN_D";
		strvar = "read";
		eTxCnt = 0;
		init_intan();
	}
	else if(strncmp(eth_rx_buf, "ALL", 3) == 0){
		cur_buf = ( u8* ) INTAN_A_RX_BUF_BASE;
		globalstr_intan = "ALL";
		strvar = "all";
		eTxCnt = 0;
		init_intan();
	}
	else if(strncmp(eth_rx_buf, "stop", 4) == 0){
		xil_printf("receive cmd = stop \n\r");
		strvar = "stop";
		eTxCnt = 0;
	}
	else if(strncmp(eth_rx_buf, "arm_stop", 8) == 0){
		xil_printf("receive cmd = arm_stop \n\r");
		gloablStr = "arm_stop";
		strvar = "stop";
		eTxCnt = 0;
	}
	else if(strncmp(eth_rx_buf, "config", 6) == 0){
		init_intan();
	}

//	xil_printf("strvar in echo = %s\r\n", strvar);



	/* free the received pbuf */
	pbuf_free(p);

	return ERR_OK;
}

err_t accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err)
{
	static int connection = 1;

	/* set the receive callback for this connection */
	tcp_recv(newpcb, recv_callback);

	/* just use an integer number indicating the connection id as the
	   callback argument */
	tcp_arg(newpcb, (void*)(UINTPTR)connection);

	/* increment for subsequent accepted connections */
	connection++;

	tcp_sent(newpcb, tcp_sent_callback_fn);		////---

	/* store state */
	connected_pcb = newpcb;

	return ERR_OK;
}


int start_application()
{
	struct tcp_pcb *pcb;
	err_t err;
	unsigned port = 7;

	/* create new TCP PCB structure */
	pcb = tcp_new();
	if (!pcb) {
		xil_printf("Error creating PCB. Out of Memory\n\r");
		return -1;
	}

	/* bind to specified @port */
	err = tcp_bind(pcb, IP_ADDR_ANY, port);
	if (err != ERR_OK) {
		xil_printf("Unable to bind to port %d: err = %d\n\r", port, err);
		return -2;
	}

	/* we do not need any arguments to callback functions */
	tcp_arg(pcb, NULL);

	/* listen for connections */
	pcb = tcp_listen(pcb);
	if (!pcb) {
		xil_printf("Out of memory while tcp_listen\n\r");
		return -3;
	}

	/* specify callback to use for incoming connections */
	tcp_accept(pcb, accept_callback);

	xil_printf("TCP echo server started @ port %d\n\r", port);

//	data_gen_ramp(SEND_BUFSIZE);

	return 0;
}
