#include "lwip/arch.h"

#include "tcpconnection.h"
//#include "tcplistener.h"

using namespace std;
using namespace mbed;

err_t tcp_connection_connected_callback(void *arg, struct tcp_pcb *pcb, err_t err) {
  TCPConnection *connection = static_cast<TCPConnection *>(arg);
  LWIP_UNUSED_ARG(pcb);
  if(connection) {
    return (connection->connected)(err);
  }
  return ERR_OK;
}

err_t tcp_connection_sent_callback(void *arg, struct tcp_pcb *pcb, u16_t space) {
  TCPConnection *connection = static_cast<TCPConnection *>(arg);
  LWIP_UNUSED_ARG(pcb);
  return (connection->sent)(space);
}

err_t tcp_connection_recv_callback(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
  TCPConnection *connection = static_cast<TCPConnection *>(arg);
  LWIP_UNUSED_ARG(pcb);
  return (connection->recv)(p, err);
}

err_t tcp_connection_poll_callback(void *arg, struct tcp_pcb *pcb) {
  TCPConnection *connection = static_cast<TCPConnection *>(arg);
  LWIP_UNUSED_ARG(pcb);
  return (connection->poll)();
}

void tcp_connection_error_callback(void *arg, err_t err) {
  TCPConnection *connection = static_cast<TCPConnection *>(arg);
  (connection->err)(err);
}

TCPConnection::TCPConnection(struct ip_addr ip, u16_t port) :  _parent(NULL) {
  this->_ipaddr = ip;
  this->_port   = port;
  this->_pcb    = tcp_new();
  tcp_arg(this->_pcb, static_cast<void *>(this));
}

TCPConnection::TCPConnection(TCPListener *parent, struct tcp_pcb *pcb)
  : TCPItem(pcb), _parent(parent), _ipaddr(pcb->remote_ip), _port(pcb->remote_port) {
  tcp_arg(this->_pcb, static_cast<void *>(this));
  printf("TCPConnection\n");
  connected(ERR_OK);
}

TCPConnection::~TCPConnection() {
  tcp_arg(this->_pcb, NULL);
  tcp_sent(this->_pcb, NULL);
  tcp_recv(this->_pcb, NULL);
  tcp_poll(this->_pcb, NULL, 255);
  tcp_accept(this->_pcb, NULL);
  tcp_err(this->_pcb, NULL);
  tcp_close(this->_pcb);
}

err_t TCPConnection::write(void *msg, u32_t len) {
  return tcp_write(this->_pcb, msg, len, 1);
}
    
void TCPConnection::recved(u32_t len) {
  tcp_recved(this->_pcb, len);
}

err_t TCPConnection::connected(err_t err) {
  printf("Connected\n");
  tcp_recv(this->_pcb, tcp_connection_recv_callback);
  tcp_sent(this->_pcb, tcp_connection_sent_callback);
  tcp_poll(this->_pcb, tcp_connection_poll_callback, 100); // addjust time (in twice a sec)
  tcp_err(this->_pcb, tcp_connection_error_callback);
  return ERR_OK;                                                    
}

void TCPConnection::connect() {
  tcp_connect(this->_pcb, &this->_ipaddr, this->_port, tcp_connection_connected_callback); 
}
