/*
 *	Glue between EPOS and uIP
 */

#ifndef __UIP_EPOS_H__
#define __UIP_EPOS_H__

#ifndef UIP_UDP_APPCALL
#define UIP_UDP_APPCALL dummy_call
#endif
#ifndef UIP_APPCALL
#define UIP_APPCALL dummy_call
#endif

#include <utility/debug.h>
#include <alarm.h>
#include <nic.h>

__USING_SYS

namespace uIP {

#include "uip-conf.h"

/*struct app_state {
  u8_t state;
  u16_t count;
  char *dataptr;
  char *script;
};*/

#include "uip.c"
#include "uip_arp.c"
#include "psock.c" 
#include "timer.c"

#ifndef LIKELY
#define LIKELY(x)   __builtin_expect(x,1)
#define UNLIKELY(x) __builtin_expect(x,0)
#endif

void dummy_call() { db<IP>(WRN) << "DUMMY CALL CALLED!!!\n"; }

NIC * nic;
unsigned long clock;

unsigned long clock_time() {
	return clock;
}

void periodic_timer() {
	CPU::int_disable();
	++clock;
	
	for(int i = 0; i < UIP_CONNS; i++) {
		uip_periodic(i);
		/* If the above function invocation resulted in data that
		   should be sent out on the network, the global variable
		   uip_len is set to a value > 0. */
		if(UNLIKELY(uip_len > 0)) {
			uip_arp_out();
			nic->raw_send(&uip_buf[0], uip_len);
		}
	}
	
	for(int i = 0; i < UIP_UDP_CONNS; i++) {
		uip_udp_periodic(i);
		/* If the above function invocation resulted in data that
		   should be sent out on the network, the global variable
		   uip_len is set to a value > 0. */
		if(UNLIKELY(uip_len > 0)) {
			uip_arp_out();
			nic->raw_send(&uip_buf[0], uip_len);
		}
	}
	
	CPU::int_enable;
}

int main(char *ip,char* gw,char *mask,Handler::Function * app_init = NULL)
{
	uip_ipaddr_t ipaddr;
	nic = new NIC;
	clock = 0;
	
	uip_init();
	
	// copy MAC address
	memcpy(uip_ethaddr.addr,(const char*)&(nic->address()), sizeof(NIC::Address));

	// set static IP configuration
	uip_ipaddr(ipaddr, ip[0],ip[1],ip[2],ip[3]);
	uip_sethostaddr(ipaddr);
	uip_ipaddr(ipaddr, gw[0],gw[1],gw[2],gw[3]);
	uip_setdraddr(ipaddr);
	uip_ipaddr(ipaddr, mask[0],mask[1],mask[2],mask[3]);
	uip_setnetmask(ipaddr);

	// Install timers
	Function_Handler periodic_handler(periodic_timer);
	Function_Handler arp_handler(uip_arp_timer);

	Alarm periodic(500000, &periodic_handler, Alarm::INFINITE); //0.5s
	Alarm arp(10000000,&arp_handler, Alarm::INFINITE); //10s

	// Application callback (app can start using uIP here)
	if (app_init != NULL)
		app_init();


	while(1) {
		uip_len = nic->raw_receive(&uip_buf[0], Traits<IP>::BUFFER_SIZE);
		short prot = CPU::ntohs(((struct uip_eth_hdr *)&uip_buf[0])->type);
		
		db<IP>(TRC) << "Received "<<uip_len<<" bytes from proto "<<prot<<endl;
		if(UNLIKELY(uip_len <= 0)) 
			continue;
			
		
		if (LIKELY(prot == NIC::IP)) {
			uip_arp_ipin();
			uip_input();
			
			if(LIKELY(uip_len > 0)) {
				uip_arp_out();
				nic->raw_send(&uip_buf[0], uip_len);
			}
		} else if (prot == NIC::ARP) {
			uip_arp_arpin();
			
			if(UNLIKELY(uip_len > 0)) {
				nic->raw_send(&uip_buf[0], uip_len);
			}
		}

	}
	return 0;
}

void uip_log(char *m)
{
	db<IP>(TRC) << "uIP log: " << m << endl;
}

};

#endif
