#include "UDPConnection.h"
#include <ant.h>
#include <IPAddress.h>
#include <OPENR/OSyslog.h>
#include <EndpointTypes.h>
#include <UDPEndpointMsg.h>

/*UDPRecvConnection*/
UDPRecvConnection::UDPRecvConnection(antStackRef ipStack, int bs, Port port) : UDPConnection(ipStack) {
  recvSize=bs;
  recvPort=port;
}

UDPRecvConnection::~UDPRecvConnection(){
  if(state != CONNECTION_INVALID)
    {
      recvBuffer.UnMap();
      antEnvDestroySharedBufferMsg destroyRecvBufferMsg(recvBuffer);
      destroyRecvBufferMsg.Call(ipstackRef, sizeof(destroyRecvBufferMsg));
    }
}

OStatus UDPRecvConnection::initConnection(){
  OSYSDEBUG(("UDPRecvConnection::initConnection()\n"));
  bool someFail=false;
  if(state != CONNECTION_INVALID)
    {
      if(initBuffer()!= oSUCCESS) someFail=true;
      if(initUdpEndpoint()!= oSUCCESS) someFail=true;
      if(initBind()!= oSUCCESS) someFail=true;
    }
  
  if(someFail)
    return oFAIL;
  else
    return oSUCCESS;
}
OStatus UDPRecvConnection::stop(){
  OSYSDEBUG(("UDPRecvConnection::stop()\n"));
  if (state != CONNECTION_CLOSED && state != CONNECTION_CLOSING && state != CONNECTION_INVALID) 
    {
      state = CONNECTION_CLOSING;
      UDPEndpointCloseMsg closeMsg(endpoint);
      closeMsg.Call(ipstackRef, sizeof(closeMsg));
      state = CONNECTION_CLOSED;
      return oSUCCESS;
    }
  return oFAIL;
}

OStatus UDPRecvConnection::initBuffer(){
  OSYSDEBUG(("UDPRecvConnection::initBuffer()\n"));
  antEnvCreateSharedBufferMsg recvBufferMsg(recvSize);
  
  recvBufferMsg.Call(ipstackRef, sizeof(recvBufferMsg));
  if (recvBufferMsg.error != ANT_SUCCESS) 
    {
      OSYSLOG1((osyslogERROR, "%s : %s antError %d", "Telepati::initRecvBuffer()", "Can't allocate receive buffer", recvBufferMsg.error));
      return oFAIL;
    }
  
  recvBuffer=recvBufferMsg.buffer;
  recvBuffer.Map();
  recvData=(byte*)(recvBuffer.GetAddress());

  return oSUCCESS;
}

OStatus UDPRecvConnection::initUdpEndpoint(){
  OSYSDEBUG(("UDPRecvConnection::initUdpEndpoint()\n"));
  if (state != CONNECTION_CLOSED) return oFAIL;
  
  antEnvCreateEndpointMsg udpCreateMsg(EndpointType_UDP, recvSize * 3);
  udpCreateMsg.Call(ipstackRef, sizeof(udpCreateMsg));
  
  if (udpCreateMsg.error != ANT_SUCCESS) 
    {
      OSYSLOG1((osyslogERROR, "%s : %s antError %d", "Telepati::initRecvUdpEndpoint()","Can't create endpoint", udpCreateMsg.error));
      return oFAIL;
    }
  endpoint = udpCreateMsg.moduleRef;

  return oSUCCESS;
}

OStatus UDPRecvConnection::initBind(){
  OSYSDEBUG(("UDPRecvConnection::initBind()\n"));
  if (state != CONNECTION_CLOSED) return oFAIL;
  
  UDPEndpointBindMsg bindMsg(endpoint, IP_ADDR_ANY, recvPort);
  bindMsg.Call(ipstackRef,sizeof(bindMsg));
  
  if (bindMsg.error != UDP_SUCCESS)  
    {
      OSYSLOG1((osyslogERROR, "%s : %s antError %d", "Telepati::initRecvBind()", "Can't bind the endpoint", bindMsg.error));
      return oFAIL;
    }
  
  state = CONNECTION_BINDED;
  
  return oSUCCESS;
}

/*UDPSendConnection*/
UDPSendConnection::UDPSendConnection(antStackRef ipStack, int bs) : UDPConnection(ipStack) {
  bufferSize=bs;
  sendSize=0;
}

UDPSendConnection::~UDPSendConnection(){
  if(state != CONNECTION_INVALID)
    {
      sendBuffer.UnMap();
      antEnvDestroySharedBufferMsg destroySendBufferMsg(sendBuffer);
      destroySendBufferMsg.Call(ipstackRef, sizeof(destroySendBufferMsg));
    }
}

OStatus UDPSendConnection::initConnection(){
  OSYSDEBUG(("UDPSendConnection::initConnection()\n"));
  bool someFail=false;
  if(state != CONNECTION_INVALID)
    {
      if(initBuffer()!= oSUCCESS) someFail=true;
      if(initUdpEndpoint()!= oSUCCESS) someFail=true;
      if(initBind()!= oSUCCESS) someFail=true;
    }
  
  if(someFail)
    return oFAIL;
  else
    return oSUCCESS;
}
OStatus UDPSendConnection::stop(){
  OSYSDEBUG(("UDPSendConnection::stop()\n"));
  if (state != CONNECTION_CLOSED && state != CONNECTION_CLOSING && state != CONNECTION_INVALID) 
    {
      state = CONNECTION_CLOSING;
      UDPEndpointCloseMsg closeMsg(endpoint);
      closeMsg.Call(ipstackRef, sizeof(closeMsg));
      state = CONNECTION_CLOSED;
      return oSUCCESS;
      }
  return oFAIL;
}

OStatus UDPSendConnection::initBuffer(){
  OSYSDEBUG(("UDPSendConnection::initBuffer()\n"));
  antEnvCreateSharedBufferMsg sendBufferMsg(bufferSize);
  
  sendBufferMsg.Call(ipstackRef, sizeof(sendBufferMsg));
  if (sendBufferMsg.error != ANT_SUCCESS) 
    {
      OSYSLOG1((osyslogERROR, "%s : %s antError %d", "Telepati::initSendBuffer()", "Can't allocate send buffer", sendBufferMsg.error));
      return oFAIL;
    }
  
  sendBuffer=sendBufferMsg.buffer;
  sendBuffer.Map();
  sendData=(byte*)(sendBuffer.GetAddress());

  return oSUCCESS;
}
OStatus UDPSendConnection::initUdpEndpoint(){
  OSYSDEBUG(("UDPSendConnection::initUdpEndpoint()\n"));
  if (state != CONNECTION_CLOSED) return oFAIL;

  antEnvCreateEndpointMsg udpCreateMsg(EndpointType_UDP,bufferSize * 3);
  udpCreateMsg.Call(ipstackRef, sizeof(udpCreateMsg));
  
  if (udpCreateMsg.error != ANT_SUCCESS) 
    {
      OSYSLOG1((osyslogERROR, "%s : %s antError %d", "Telepati::initSendUdpEndpoint()","Can't create endpoint", udpCreateMsg.error));
      return oFAIL;
    }
  endpoint = udpCreateMsg.moduleRef;

  return oSUCCESS;
}
OStatus UDPSendConnection::initBind(){
  OSYSDEBUG(("UDPSendConnection::initBind()\n"));

  if (state != CONNECTION_CLOSED) return oFAIL;

  UDPEndpointBindMsg bindMsg(endpoint, IP_ADDR_ANY, IP_PORT_ANY);
  bindMsg.Call(ipstackRef,sizeof(bindMsg));

  if (bindMsg.error != UDP_SUCCESS)  
    {
      OSYSLOG1((osyslogERROR, "%s : %s antError %d", "Telepati::initSendBind()", "Can't bind the endpoint", bindMsg.error));
      return oFAIL;
    }

  state = CONNECTION_WAITING; 
  
  return oSUCCESS;
}
