/*****************************
File:       sockwrap.CPP
Language:   C++ (header)
Project:    H3DNetworkingUtils
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is H3DNetworkingUtils v1.0.

The Initial Developer of the Original Code is CSIRO.
Portions created by the Initial Developer are Copyright (C) 1009 CSIRO. All Rights Reserved.
Contributor(s):
    Chris Gunn  <Chris.Gunn@csiro.au> <ChrisJGunn@gmail.com>
***************************/

/* Distributed under a BSD type license. See the netwrap COPYRIGHT file */

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

#include <winsock.h>

#include "H3DNetworkingUtils/fail.h"
#include "H3DNetworkingUtils/inetaddr.h"

#include "H3DNetworkingUtils/sockwrap.h"

//	Socket code for Windows.
//	Uses original winsock, not the newer winsock 2, because
//	doing so made it possible to reuse all the Unix code
//	with only minor changes.

//	Code based heavily on W. Richard Stevens


#define CR 13
#define LF 10

// Must detect first use under Windows. See also inetaddr.cpp
// Yes, it's ugly to have initialisation code checks every time
// you create a new object, but this avoids forcing the user of
// the library to call an initialisation routine.

static int SockFirstUse = 0;

static void WinInitCheck ()
{
    WORD vers;
    WSADATA data;

    if (! SockFirstUse) {
	vers = MAKEWORD(1, 1);
	FailStatus(WSAStartup(vers, &data), "WSAStartup");
	SockFirstUse = 1;
    }
}

//	Closing a CoreSock is harmless, IO attempt is error.

CoreSock::CoreSock ()
{
  WinInitCheck();
}

CoreSock::~CoreSock ()
{
  this->close();
}

void CoreSock::close (void)
{
  /* NOP */
}

int CoreSock::read (void * dest, int max)
{
  fprintf(stderr, "read must be implemented by subclass\n");
  return EOF;
}

int CoreSock::readLine (char * line, int max)
{
  int n;
  
  n = this->read(line, max - 1);
  // Strip any line terminators
  while (n > 0 && (line[n - 1] == CR || line[n - 1] == LF))
    n--;
  line[n] = 0;
  
  return n;
}

int CoreSock::write (void * source, int count)
{
  fprintf(stderr, "write must be implemented by subclass\n");
  return EOF;
}

int CoreSock::writeLine (char * line)
{
  int n;

  // Strip any line terminators
  n = strlen(line);
  while (n > 0 && (line[n - 1] == CR || line[n - 1] == LF))
    n--;
  return this->write(line, n);
}

//******************************************
//****		Real internet socket	****
//******************************************

InetSock::InetSock (int kind, int fd)
{
  memset((char*)&tv, 0, sizeof(tv));
  setTimeOut(500);
  if (fd > 0)
    this->sock = fd;
  else {
    this->sock = socket(AF_INET, kind, 0);
    FailErr(this->sock, "new InetSock");
  }
  this->kind = kind;
}

InetSock::~InetSock ()
{
}

void InetSock::close ()
{
  if (this->sock > 0)
    ::closesocket(this->sock);
  this->sock = 0;
}

//	Client/server setup

int InetSock::connect (InetAddr * endpoint)
{
  return ::connect((SOCKET)this->sock,
		(struct sockaddr *)endpoint->addr(), endpoint->addrSize());
}

int InetSock::bind (InetAddr * endpoint, int qLength)
{
  int	   err, flag;

  if (kind == SOCK_STREAM) {
    // Standard server can reuse port
    flag = 1;
    FailErr(setsockopt((SOCKET)this->sock,
				    SOL_SOCKET, SO_REUSEADDR,
				   (char *)&flag, sizeof(flag)),
	    "InetSock::bind reuse address");
  }
  err = ::bind((SOCKET)this->sock,
		(struct sockaddr *)endpoint->addr(), endpoint->addrSize());
  if (err >= 0 && kind == SOCK_STREAM && qLength != ActiveBind)
    err = ::listen(this->sock, qLength);

  return err;   
}

//	Socket attributes

InetAddr * InetSock::localAddress ()
{
  int size;

  size = local.addrSize();
  if (getsockname((SOCKET)this->sock,
		    (struct sockaddr *)local.addr(), &size) != 0)
    // Not an error, just not connected yet
    return NULL;
  else
    return &local;
}

InetAddr * InetSock::remoteAddress ()
{
  int size;

  size = remote.addrSize();
  if (getpeername((SOCKET)this->sock,
		    (struct sockaddr *)remote.addr(), &size) != 0)
    // Normal for server master socket
    return NULL;
  else
    return &remote;
}

int InetSock::isUDP ()
{
  return this->kind == SOCK_DGRAM;
}

int InetSock::isTCP ()
{
  return this->kind == SOCK_STREAM;
}

// Returns true if there is data on the socket ready for reading.
bool InetSock::dataReady() {
  int	 err;
  fd_set rset;
  
  FD_ZERO(&rset);
  FD_SET((int)this->sock, &rset);
  err = select(-1, &rset, NULL, NULL, &tv);
  return (err > 0);
}

// Time limit on read/write operations.
void InetSock::setTimeOut (int millisecs)
{
  if (millisecs < 0)
    Fail("UDPSock::setTimeOut bad value");

  ioWait = millisecs;
  tv.tv_sec  = ioWait / 1000;
  tv.tv_usec = (ioWait % 1000) * 1000;
  // Win32 winsock declares the socket timeout options but
  // they don't work. Does ANYBODY actually implement them?
}

int InetSock::timeOut ()
{
  return this->ioWait;
}


//**********************************
//****		UDP socket	****
//**********************************

UDPSock::UDPSock ()
  :InetSock(SOCK_DGRAM, 0)
{
  this->ioWait = 0;
}

UDPSock::~UDPSock ()
{
}

// IO on UDP sockets is easy: one packet at a time.
// Well, it would be easy without the timeout code.
// Use the same select code as for IRIX

int UDPSock::read (void * dest, int max)
{  
  if (dataReady()) {
     return recv(this->sock, (char *)dest, max, 0);
  } else {
     return 0;
  }
}

int UDPSock::write (void * source, int count)
{
  int	 err;
  fd_set wset;
  struct timeval tv;
  
  if (ioWait > 0) {
    tv.tv_sec  = ioWait / 1000;
    tv.tv_usec = (ioWait % 1000) * 1000;
    FD_ZERO(&wset);
    FD_SET((int)this->sock, &wset);
    err = select((int)this->sock + 1, NULL, &wset, NULL, &tv);
    if (err <= 0)
      return err;
  }
  return ::send(this->sock, (const char *)source, count, 0);
}

int UDPSock::receive (void * dest, int max, InetAddr * sender)
{
  int len;
  if (dataReady()) {
     len = sender->addrSize();
     // Should check that len returned matches sender addrSize...
     return ::recvfrom((SOCKET)this->sock,
		    (char *)dest, max,
		    0, (struct sockaddr *)sender->addr(), &len);
  } else {
     return 0;
  }
}

int UDPSock::send (void * source, int count, InetAddr * target)
{
  return ::sendto((SOCKET)this->sock,
		    (char *)source, count,
		    0, (struct sockaddr *)target->addr(), target->addrSize());
	 
}

//**********************************
//****		TCP stream	****
//**********************************


TCPSock::TCPSock (int fd)
    :InetSock(SOCK_STREAM, fd)
{
  readBuf  = (char *)malloc(MaxLineSize);
  FailNull(readBuf, "TCPSock::init");
  bufPtr   = readBuf;
  bufCount = 0;
}

TCPSock::~TCPSock ()
{
  free(readBuf);
}

//	Server setup

TCPSock * TCPSock::accept ()
{
  int result;

  result = ::accept(this->sock, NULL, NULL);
  if (result < 0)
    return NULL;
  else
    return new TCPSock(result);
}

void TCPSock::shutdown (int direction)
{
  ::shutdown(this->sock, direction);
}

//	Binary IO

int TCPSock::read (void * dest, int count)
{
  int    nLeft, nRead;
  char * ptr;

  ptr   = (char *)dest;
  nLeft = count;
  while (nLeft > 0) {
    if ((nRead = ::recv(this->sock, ptr, nLeft, 0)) < 0) {
      if (errno == EINTR)
        nRead = 0; // and try again
      else
        return -1; // Problem
    } else if (nRead == 0 && nLeft == count) // EOF
      return EOF;
    else if (nRead == 0) // EOF with data
      break;
    // Adjust counter, pointer
    nLeft -= nRead;
    ptr += nRead;
  }
  // Result not necessarily = count on EOF
  return count - nLeft;
}

int TCPSock::write (void * source, int count)
{
  int    nLeft, nWritten;
  char * ptr;

  ptr   = (char *)source;
  nLeft = count;
  while (nLeft > 0) {
    if ((nWritten = ::send(this->sock, ptr, nLeft, 0)) <= 0) {
      if (errno == EINTR)
        nWritten = 0; // And try again
      else
        return -1; // Problem
    }
    // Adjust pointer, counter
    nLeft -= nWritten;
    ptr += nWritten;
  }
  return count;
}

//	Line orientated IO. Tricky because we can't
//	rely on line boundaries on incoming data

int TCPSock::nextChar (char * c)
{
  // Need to fill buffer?
  while (bufCount <= 0) {
    if ((bufCount = ::recv(this->sock, readBuf, MaxLineSize, 0)) < 0) {
       if (errno != EINTR)
          return -1; // Problem
    } else if (bufCount == 0)
      return 0; // EOF
    else
      bufPtr = readBuf;
  }
  // Take next char
  *c = *bufPtr++;
  bufCount --;
  return 1;
}

int TCPSock::readLine (char * line, int max)
{
  int    n, rc;
  char   c;

  n = 0;
  c = 0;
  while (c != LF) {
    rc = nextChar(&c);
    if (rc == 1) {
      // Got a char
      if (n < max - 1 && c != CR && c != LF) {
        line[n] = c;
	n++;
      }
    } else if (rc == 0) {
      if (n == 0) // EOF
        return EOF;
      else
        // Last line without CR/LF
        break;
    } else
      return -1; // Error
  }
  // Add zero
  line[n] = 0;
  return n;
}

int TCPSock::writeLine (char * line)
{
  int count, err;
  static char CRLF[2] = { CR, LF};

  count = strlen(line);
   // Properly terminated?
  if (count >= 2 && line[count - 2] == CR && line[count - 1] == LF)
    return this->write(line, count);
  else {
    // Remove any existing line terminator
    if (count > 0 && (line[count - 1] == CR || line[count - 1] == LF))
      count --;
    // Windows doesn't have writev.
    err = ::send((SOCKET)this->sock, (char *)line, count, 0);
    if (err >= 0)
      err =::send((SOCKET)this->sock, CRLF, 2, 0);
    return err;
  }
}



