#include "lwip_server.h"

int transfer_data() {
	return 0;
}

void print_app_header()
{
	xil_printf("[I] New ExtInstrument Calibration Board Zynq Controller lwip stack...\r\n");
	xil_printf("[I] Net: Hello World!\r\n");
}

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("[E] 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("[E] 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("[I] NewExtInstCal Zynq server started @ port %d\n\r", port);

	return 0;
}

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;
}

err_t recv_callback(void *arg, struct tcp_pcb *tpcb,
                               struct pbuf *p, err_t err)
{
    u32 Recv_Len;
    char *Recv_Data;
    u16 uCRC_Read_Dat;
    u8 uCRC_Result[2]={0};
    u8 uSend_Data[20] = {0};
    u8 uCRC_Flag = 255;


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

	/* Here, 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);
        
    //Start of CRC Check 
        uCRC_Read_Dat = do_crc_table(Recv_Data+6, Recv_Len-8);

        uCRC_Result[0] = (u8)uCRC_Read_Dat;
        uCRC_Result[1] = (u8)(uCRC_Read_Dat>>8);

        if (!(Recv_Data[15]==uCRC_Result[1]&&Recv_Data[16]==uCRC_Result[0]))
        {
            xil_printf("[E] CRC Mismatch! Check communication!\r\n");
            uCRC_Flag = 255;
        }
        else
        {
            xil_printf("[D] CRC Match!\r\n");
            uCRC_Flag = 1;
        }
    //End of CRC Check 

    //Start of SendBack Formation
        uSend_Data[0] = 0x00;
        uSend_Data[1] = Recv_Data[1];
        uSend_Data[2] = Recv_Data[2];
        uSend_Data[3] = Recv_Data[3];
        uSend_Data[4] = Recv_Data[4];
        uSend_Data[5] = Recv_Data[5];
        uSend_Data[6] = Recv_Data[6];
        uSend_Data[7] = Recv_Data[7];
        uSend_Data[8] = uCRC_Flag;
        uSend_Data[9] = uCRC_Flag;
        uSend_Data[10] = Recv_Data[15];             //Shouldn't We Re-calculate CRC? Why send back the old CRC?
        uSend_Data[11] = Recv_Data[16];             //Same as above
    //End of SendBack Formation

        //Matching Zynq Cal Instruction Header
        if(Recv_Data[6] == 0x0E)
        {
            if (Recv_Data[7] == 0xA2)               //Cal
            {
                switch (Recv_Data[14])              //By Judging Equipment to decide cal type
                {
                case 0x01:                          //DC Cal
                {
                    u8 uCMD[DC_CMD_LEN] = {0};

                    uCMD[0] = Recv_Data[8];
                    if (Recv_Data[9] < 3)
                    {
                        xil_printf("[E] DPS Single Force/Sense Not Support!\r\n");
                    }
                    else
                    {
                        if (Recv_Data[9] == 0x03)   //Select DPS
                        {
                            uCMD[1] = 0x2;
                        }
                        else if (Recv_Data[9] == 0x04)//Select PMU
                        {
                            uCMD[1] = 0x1;
                        }
                        else
                        {
                            xil_printf("[E] Device Not Support!\r\n");
                        }
                    }
                    uCMD[2] = Recv_Data[10];
                    uCMD[3] = Recv_Data[11];
                    uCMD[4] = Recv_Data[12] + 1;
                    uCMD[5] = Recv_Data[13];

                    DC_Control(uCMD);

                    break;
                }
                case 0x02:                          //Freq Cal
                {
                    u8 uCMD[Freq_CMD_LEN] = {Recv_Data[8],Recv_Data[9],Recv_Data[10],Recv_Data[11],Recv_Data[12],Recv_Data[13],Recv_Data[14]};
                    sw_config(uCMD);
                    break;
                }
                
                default:                            //You shouldn't be here
                    break;
                }
            }
            
        }

        tcp_write(tpcb, uSend_Data, sizeof(uSend_Data), 1);
    }
    else
    {
		xil_printf("no space in tcp_sndbuf\n\r");
    }
    /* free the received pbuf */
	pbuf_free(p);

	return ERR_OK;

}