/******************************************************************************
*
* Copyright (C) 2016 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.
*
******************************************************************************/

#include <stdio.h>
#include <string.h>

#include "lwip/sockets.h"
#include "netif/xadapter.h"
#include "lwipopts.h"
#include "xil_printf.h"
#include "FreeRTOS.h"
#include "task.h"

#include "ARM_CommBlock_v1.h"

#define DEBUG_TASKS	0


#define THREAD_STACKSIZE 1024
static volatile u8* cur_buf = NULL;

static xQueueHandle xQueue = NULL;
static xQueueHandle xQueueTx = NULL;

u16_t echo_port = 7;

void print_echo_app_header()
{
    xil_printf("%20s %6d %s\r\n", "echo server",
                        echo_port,
                        "$ telnet <board_ip> 7");

}

/* thread spawned for each connection */
void Ethernet_Rx_task(void *p)
{
	int sd = (int)p;
	int RECV_BUF_SIZE = 128;
	char recv_buf[RECV_BUF_SIZE];
	int n;
	xPacaket tmpPack;

	xQueue = xQueueCreate(100, sizeof(xPacaket));
	if(xQueue == NULL){
		xil_printf("Queue was not created \r\n");
	}

	while (1) {
		/* read a max of RECV_BUF_SIZE bytes from socket */
		if ((n = read(sd, recv_buf, RECV_BUF_SIZE)) < 0) {
			xil_printf("%s: error reading from socket %d, closing socket\r\n", __FUNCTION__, sd);
			break;
		}

		xil_printf("echo recived = %s\r\n", recv_buf);

		if(n > 0){
			tmpPack = Analyze_PC_request(recv_buf);

#if DEBUG_TASKS == 1
			xil_printf("xPacket command = %s\n",tmpPack.cmd);
			xil_printf("xPacket addr = %d\n",*(tmpPack.addr));
			xil_printf("xPacket values = %d\n",*(tmpPack.values));
#endif

			if(	xQueueSend(xQueue, ( void * ) &tmpPack, ( TickType_t ) 10) != pdPASS){
				xil_printf(" Failed to send \r\n");
			}
		}

		/* break if client closed connection */
		if (n <= 0)
			break;

	}

	/* close connection */
	close(sd);
	vTaskDelete(NULL);
}


void Messege_decoding_task(void *p){
	int sd = (int)p;
	xPacaket x;
	txPacket tmptxpack;


	xQueueTx = xQueueCreate(100, sizeof(txPacket));
	if(xQueueTx == NULL){
		xil_printf("Queue was not created \r\n");
	}

	while(1){
		if(uxQueueSpacesAvailable(xQueue) < 100){
			if(xQueueReceive(xQueue, &x, ( TickType_t ) 10)){
				tmptxpack = Execute_PC_request(&x);

#if DEBUG_TASKS == 1
				xil_printf("de-queued x = %s\r\n", x.cmd);
				xil_printf("return Execute fn(addr) = 0x%x\n", tmptxpack.txaddr);
				xil_printf("return Execute fn(size) = %d\n", tmptxpack.len);
#endif

				if(	xQueueSend(xQueueTx, ( void * ) &tmptxpack, ( TickType_t ) 10) != pdPASS){
					xil_printf(" Failed to send \r\n");
				}

			}
			else
				xil_printf("de-queued failed \r\n");
		}
	}

	/* close connection */
	close(sd);
	vTaskDelete(NULL);
}

void Ethernet_Tx_task(void *p){
	int sd = (int)p;
	int nwrote;
	txPacket tmptxpackptr;
	unsigned int DMA_Buff_addr = 0;
	int Buff_Size = 0;

	while(1){
		if(uxQueueSpacesAvailable(xQueueTx) < 100){
			if(xQueueReceive(xQueueTx, &tmptxpackptr, ( TickType_t ) 10)){
#if DEBUG_TASKS == 1
				xil_printf("txPacket in decode (addr) = 0x%x\n", tmptxpackptr.txaddr);
				xil_printf("txPacket in decode (size) = %d\n", tmptxpackptr.len);
#endif
				DMA_Buff_addr = tmptxpackptr.txaddr;
				Buff_Size = tmptxpackptr.len;

				cur_buf = ( u8* ) DMA_Buff_addr;

#if DEBUG_TASKS == 1
				xil_printf("cur_buf = 0x%x\n", cur_buf);
				xil_printf("Buff_Size = %d\n", Buff_Size);
#endif

				if((Buff_Size >= 0) && (Buff_Size <= 131072)){
					/* handle request */
					if ((nwrote = write(sd, (char *) cur_buf, Buff_Size)) < 0) {
						xil_printf("%s: ERROR responding to client echo request. received = %d, written = %d\r\n",
								__FUNCTION__, Buff_Size, nwrote);
						xil_printf("Closing socket %d\r\n", sd);
						break;
					}
				}
			}
		}
	}

	/* close connection */
	close(sd);
	vTaskDelete(NULL);
}


void echo_application_thread()
{
	int sock, new_sd;
	int size;
#if LWIP_IPV6==0
	struct sockaddr_in address, remote;

	memset(&address, 0, sizeof(address));

	if ((sock = lwip_socket(AF_INET, SOCK_STREAM, 0)) < 0)
		return;

	address.sin_family = AF_INET;
	address.sin_port = htons(echo_port);
	address.sin_addr.s_addr = INADDR_ANY;
#else
	struct sockaddr_in6 address, remote;

	memset(&address, 0, sizeof(address));

	address.sin6_len = sizeof(address);
	address.sin6_family = AF_INET6;
	address.sin6_port = htons(echo_port);

	memset(&(address.sin6_addr), 0, sizeof(address.sin6_addr));

	if ((sock = lwip_socket(AF_INET6, SOCK_STREAM, 0)) < 0)
		return;
#endif

	if (lwip_bind(sock, (struct sockaddr *)&address, sizeof (address)) < 0)
		return;

	lwip_listen(sock, 0);

	size = sizeof(remote);

	while (1) {
		if ((new_sd = lwip_accept(sock, (struct sockaddr *)&remote, (socklen_t *)&size)) > 0) {
			sys_thread_new("ethe_rxs", Ethernet_Rx_task,
				(void*)new_sd,
				THREAD_STACKSIZE,
				DEFAULT_THREAD_PRIO);

			sys_thread_new("msg_decos", Messege_decoding_task,
				(void*)new_sd,
				THREAD_STACKSIZE,
				DEFAULT_THREAD_PRIO);
			sys_thread_new("ethe_txs", Ethernet_Tx_task,
				(void*)new_sd,
				THREAD_STACKSIZE,
				DEFAULT_THREAD_PRIO);
		}
	}
}
