/*
 * Copyright (C) 2009 - 2019 Xilinx, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 */

#include <stdio.h>
#include <string.h>
#include "dna_read.h"
#include "crc16.h"
#include "GPIO_Driv.h"
#include "SPI_Driv.h"
#include "cal_meas.h"

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

char *recv_data;
char dna_data[10];
unsigned short recv_len;

unsigned  read_dat = 0;
char 	  dat_0[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
unsigned  crc_read_dat = 0;

char     crc_alg_dat[2] = {0,0};

char     send_data_cpld1[2] = {0xAA,0x56};

int transfer_data() {
	return 0;
}

void print_app_header()
{
#if (LWIP_IPV6==0)
	xil_printf("\n\r\n\r-----lwIP TCP echo server ------\n\r");
#else
	xil_printf("\n\r\n\r-----lwIPv6 TCP echo server ------\n\r");
#endif
	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);

	/* echo back the payload */
	/* in this case, we assume that the payload is < TCP_SND_BUF */
	if (tcp_sndbuf(tpcb) > p->len) {

		recv_len = p->len;
		recv_data = (char*)malloc(sizeof(char)*recv_len);
	    memcpy(recv_data,p->payload,recv_len);

	    if(recv_data[6] == 0x0E)
	    {
	    	crc_read_dat = do_crc_table(recv_data+6, recv_len-8);

	    	crc_alg_dat[0] = (u8)crc_read_dat;
	    	crc_alg_dat[1] = (u8)(crc_read_dat>>8);
	    	dat_0[0]= 0x00;

	    	switch(recv_data[7])
	    	{

	    	  case 0xA2://SLOT
	    	          {
						  dat_0[1]= 0x06;
						  dat_0[2]= recv_data[2];
						  dat_0[3]= recv_data[3];
						  dat_0[4]= recv_data[4];
						  dat_0[5]= recv_data[5];
						  dat_0[6]= recv_data[6];
						  dat_0[7]= recv_data[7];
	    	        	  if(recv_data[15]==crc_alg_dat[1]&&recv_data[16]==crc_alg_dat[0])
	    	        	  {
	    	        		  dat_0[8]= 0x01;
	    	        		  dat_0[9]= 0x01;
	    	        	  }
	    	        	  else
						  {
							  dat_0[8]= 0xff;
							  dat_0[9]= 0xff;
						  }
	    	        	  dat_0[10]= recv_data[15];
	    	        	  dat_0[11]= recv_data[16];

	    	        	  char slot_start[7] = {recv_data[8],recv_data[9],recv_data[10],recv_data[11],recv_data[12],recv_data[13],recv_data[14]};
	    	        	  //xil_printf("slot_start[0]:%x, slot_start[1]:%x,slot_start[2]:%x,slot_start[3]:%x,slot_start[4]:%x,slot_start[5]:%x,slot_start[6]:%x\n\r", slot_start[0],slot_start[1],slot_start[2],slot_start[3],slot_start[4],slot_start[5],slot_start[6]);
	    	        	  rst_CPLD(CPLD_U1,4);
	    	        	  rst_CPLD(CPLD_U2,4);
	    	        	  rst_CPLD(CPLD_U3,4);
	    	        	  sw_config(slot_start);

	    	        	  tcp_write(tpcb, dat_0, 12, 1);

	    	        	  break;
	    	          }

	    	  case 0xA3://dna_port_read
					  {
						  dna_port_read(dna_data);
						  dat_0[1]=0x0e;
						  dat_0[2]=recv_data[2];
						  dat_0[3]=recv_data[3];
						  dat_0[4]=recv_data[4];
						  dat_0[5]=recv_data[5];
						  dat_0[6]=recv_data[6];
						  dat_0[7]=recv_data[7];
						  if(recv_data[8]==crc_alg_dat[1]&&recv_data[9]==crc_alg_dat[0])
						  {
							  dat_0[8]= 0x01;
							  dat_0[9]= 0x01;
						  }
						  else
						  {
							  dat_0[8]= 0xff;
							  dat_0[9]= 0xff;
						  }

						  dat_0[10]= dna_data[0];
						  dat_0[11]= dna_data[1];
						  dat_0[12]= dna_data[2];
						  dat_0[13]= dna_data[3];
						  dat_0[14]= dna_data[4];
						  dat_0[15]= dna_data[5];
						  dat_0[16]= dna_data[6];
						  dat_0[17]= dna_data[7];

						  dat_0[18]= recv_data[8];
						  dat_0[19]= recv_data[9];

						  tcp_write(tpcb, dat_0, 20, 1);
						  break;
					  }
	    	}
	    }
	} else
		xil_printf("no space in tcp_sndbuf\n\r");



	/* 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++;

	return ERR_OK;
}


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

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

	/* bind to specified @port */
	err = tcp_bind(pcb, IP_ANY_TYPE, 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);

	return 0;
}
