#include "lwip/opt.h" 
#include "lwip/stats.h" 
#include "lwip/sys.h" 
#include "lwip/pbuf.h" 
#include "lwip/udp.h" 
#include "lwip/tcp.h" 
#include "lwip/dns.h" 
#include "lwip/dhcp.h" 
#include "lwip/init.h"
#include "lwip/netif.h" 
#include "netif/etharp.h" 
#include "netif/loopif.h" 
#include "netif/rmiiif.h" 

#include "netserver.h"
#include "tcplistener.h"
#include "tcpcallbacklistener.h"
#include "tcpconnection.h"

using namespace std;
using namespace mbed;

NetServer::NetServer() : netif(&netif_data), dhcp(true), hostname("mbed-c3p0") {
  IP4_ADDR(&netmask, 255,255,255,255);
  IP4_ADDR(&gateway, 0,0,0,0);
  IP4_ADDR(&ipaddr, 0,0,0,0);
  mac.addr[0] = 0x00;
  mac.addr[1] = 0x30;
  mac.addr[2] = 0x6C;
  mac.addr[3] = 0x00;
  mac.addr[4] = 0x00;
  mac.addr[5] = 0x02;
}

NetServer::NetServer(struct ip_addr ip, struct ip_addr nm, struct ip_addr gw)
 : netif(&netif_data), ipaddr(ip), netmask(nm), gateway(gw), dhcp(false), hostname("mbed-c3p0") {
}

NetServer::~NetServer() {
  
}

void NetServer::emac_tmr() {
  emac_input(&netif_data);
}

void NetServer::init() {
  lwip_init();
  
  netif->hwaddr_len = ETHARP_HWADDR_LEN;
  *((struct eth_addr *)&(netif->hwaddr[0])) = mac;

  netif = netif_add(netif, &ipaddr, &netmask, &gateway, NULL, emac_init, ip_input);
  netif->hostname = this->hostname;
  netif_set_default(netif);
  if(!dhcp) {
    netif_set_up(netif);
  } else {
    dhcp_start(netif);
  }

  tickARP.attach_us( &etharp_tmr,  ARP_TMR_INTERVAL  * 1000); 
  tickFast.attach_us(&tcp_fasttmr, TCP_FAST_INTERVAL * 1000); 
  tickSlow.attach_us(&tcp_slowtmr, TCP_SLOW_INTERVAL * 1000); 
  eth_tick.attach_us<NetServer>(this,&emac_tmr, TCP_FAST_INTERVAL * 1000);
  dns_tick.attach_us(&dns_tmr, DNS_TMR_INTERVAL * 1000);
  if(dhcp) {
    dhcp_coarse.attach_us(&dhcp_coarse_tmr, DHCP_COARSE_TIMER_MSECS * 1000);
    dhcp_fine.attach_us(&dhcp_fine_tmr, DHCP_FINE_TIMER_MSECS * 1000);
  }
}

void NetServer::setUp() {
  netif_set_up(netif);
}

void NetServer::setDown() {
  netif_set_down(netif);
}

void NetServer::waitForDHCP() {
  while(!netif_is_up(netif)) {
    wait(1);
  }
}

TCPCallbackListener *NetServer::bindTCPPort(u16_t port, err_t (*accept)(TCPCallbackListener *, struct tcp_pcb *, err_t)) {
  TCPCallbackListener *listener = new TCPCallbackListener(port, accept);
  listener->bind();
  return listener;
}

TCPListener *NetServer::bindTCPPort(TCPListener *listener) {
  listener->bind();
  return listener;
}

void NetServer::etablishTCPConnection(TCPConnection *con) {
  con->connect();
}
