/*  $Id$

    Part of SWI-Prolog

    Author:        Jan Wielemaker
    E-mail:        J.Wielemaker@uva.nl
    WWW:           http://www.swi-prolog.org
    Copyright (C): 2000-2008, University of Amsterdam

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Shared stuff between the normal socket interface and the TIPC one.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


		 /*******************************
		 *	  IO-STREAM STUFF	*
		 *******************************/

#define fdFromHandle(p) ((nbio_sock_t)((intptr_t)(p)))

static ssize_t
tcp_read_handle(void *handle, char *buf, size_t bufSize)
{ nbio_sock_t sock = fdFromHandle(handle);

  return nbio_read(sock, buf, bufSize);
}


static ssize_t
tcp_write_handle(void *handle, char *buf, size_t bufSize)
{ nbio_sock_t sock = fdFromHandle(handle);

  return nbio_write(sock, buf, bufSize);
}


static long
tcp_seek_null(void *handle, long offset, int whence)
{ return -1;
}


static int
tcp_close_input_handle(void *handle)
{ nbio_sock_t socket = fdFromHandle(handle);

  return nbio_close_input(socket);
}


static int
tcp_close_output_handle(void *handle)
{ nbio_sock_t socket = fdFromHandle(handle);

  return nbio_close_output(socket);
}


static int
tcp_control(void *handle, int action, void *arg)
{ nbio_sock_t socket = fdFromHandle(handle);

  switch(action)
  {
#ifdef __WINDOWS__
    case SIO_GETFILENO:
      return -1;
    case SIO_GETWINSOCK:
#else
    case SIO_GETFILENO:
#endif
    { SOCKET fd = nbio_fd(socket);
      SOCKET *fdp = arg;
      *fdp = fd;
      return 0;
    }
    case SIO_LASTERROR:
    { const char *s;

      if ( (s=nbio_last_error(socket)) )
      { const char **sp = arg;
	*sp = s;
	return 0;
      }

      return -1;
    }
    case SIO_SETENCODING:
    case SIO_FLUSHOUTPUT:
      return 0;
    default:
      return -1;
  }
}


static IOFUNCTIONS readFunctions =
{ tcp_read_handle,
  tcp_write_handle,
  tcp_seek_null,
  tcp_close_input_handle,
  tcp_control
};


static IOFUNCTIONS writeFunctions =
{ tcp_read_handle,
  tcp_write_handle,
  tcp_seek_null,
  tcp_close_output_handle,
  tcp_control
};


static foreign_t
pl_open_socket(term_t Socket, term_t Read, term_t Write)
{ IOSTREAM *in, *out;
  int socket;
  void *handle;

  if ( !tcp_get_socket(Socket, &socket) )
    return FALSE;
  handle = (void *)(intptr_t)socket;

  in  = Snew(handle, SIO_INPUT|SIO_RECORDPOS|SIO_FBUF,  &readFunctions);
  in->encoding = ENC_OCTET;
  if ( !PL_open_stream(Read, in) )
    return FALSE;
  nbio_setopt(socket, TCP_INSTREAM, in);

  if ( !(nbio_get_flags(socket) & PLSOCK_LISTEN) )
  { out = Snew(handle, SIO_OUTPUT|SIO_RECORDPOS|SIO_FBUF, &writeFunctions);
    out->encoding = ENC_OCTET;
    if ( !PL_open_stream(Write, out) )
      return FALSE;
    nbio_setopt(socket, TCP_OUTSTREAM, out);
  }

  return TRUE;
}


static foreign_t
pl_listen(term_t Sock, term_t BackLog)
{ int socket;
  int backlog;

  if ( !tcp_get_socket(Sock, &socket) )
    return FALSE;

  if ( !PL_get_integer(BackLog, &backlog) )
    return pl_error(NULL, 0, NULL, ERR_ARGTYPE, -1, BackLog, "integer");

  if ( nbio_listen(socket, backlog) < 0 )
    return FALSE;

  return TRUE;
}


static foreign_t
pl_close_socket(term_t socket)
{ int sock;

  if ( !tcp_get_socket(socket, &sock) )
    return FALSE;

  if ( nbio_closesocket(sock) < 0 )
    return nbio_error(errno, TCP_ERRNO);;

  return TRUE;
}
