#ifndef _IO_H_
#define _IO_H_
/* io.h:
 *
 * Copyright (C) 1997 Art Technology Group, Inc.
 * All Rights Reserved.  No use, copying or distribution of this
 * work may be made except in accordance with a valid license
 * agreement from Art Technology Group.  This notice must be
 * included on all copies, modifications and derivatives of this
 * work.
 */

#include "system.h"

/* IOStatus error values.
 *
 * WARNING: IF YOU CHANGE THIS, YOU MUST ALSO INSERT THE TEXT VERSION OF YOUR
 *          NEW ERROR(S) IN THE CORRESPONDING MESSAGE TABLE IN sockpool.c.
 */
typedef enum IOStatus
{
  IOStatusOk = 0,
  IOStatusFailed,
  IOStatusNotImplemented,
  IOStatusNoMoreData,
  IOStatusReadError,
  IOStatusWriteError,
  IOStatusBadUrl,
  IOStatusUnknownHost,
  IOStatusNoMoreSockets,
  IOStatusConnectionRefused,
  IOStatusNetworkError,
  IOStatusMemoryExhausted,
  IOStatusNoMoreFiles,
  IOStatusNoSuchFile,
  IOStatusPermissionDenied,
  IOStatusBufferOverflow,
  IOStatusProtocolError,
  IOStatusProtocolVersionMismatch,
  IOStatusTimeout,
  IOStatusInitializationFailed,
  IOStatusCount
} IOStatus;

typedef enum IOStreamOption {
  IOStreamOptionTimeout = 1
} IOStreamOption;

#define IO_SOCKET 1001

/* Returns a human-readable string describing the indicated IOStatus value.
 */
const char* getIOStatusString(IOStatus status);

/* Signature of an input stream reader function.  The reader must read as much
 * data as is immediately available, but never return before reading something.
 */
typedef IOStatus (*InputStreamReader)(void* handle, void* buf, int size,
				      int timeout, int* written);

/* Signature of an input stream closer function.  If NULL is passed for one of
 * these, it is not closed when the stream is destroyed.
 */
typedef IOStatus (*InputStreamCloser)(void* handle);

/* Signature of an output stream writer function.  The writer must not return
 * until all information has been written.
 */
typedef IOStatus (*OutputStreamWriter)(void* handle, const void* buf, int size);

/* Signature of an output stream closer function.  If NULL is passed for one
 * of these, it is not closed when the stream is destroyed.
 */
typedef IOStatus (*OutputStreamCloser)(void* handle);

/* An input stream structure.
 */
typedef struct InputStream
{
  void* handle;
  InputStreamReader reader;
  InputStreamCloser closer;
  char* buffer;
  int bufferSize;
  int avail;
  int readp;
  int timeout; /* length of time to wait for input, in ms */
  IOStatus status; /* status of last source read */
} InputStream;

/* An output stream structure.
 */
typedef struct OutputStream
{
  void* handle;
  OutputStreamWriter writer;
  OutputStreamCloser closer;
  char* buffer;
  int bufferSize;
  int writep;
  IOStatus status;
} OutputStream;

/* Creates a buffered input stream.
 */
InputStream* createInputStream(void* handle,
			       InputStreamReader reader,
			       InputStreamCloser closer,
			       int bufferSize);

/* Fills the buffer of an input stream as much as immediately possible.  Waits
 * for more input if none is immediately available.  This is intended for
 * internal use.
 */
IOStatus fillInputStream(InputStream* stream);

/* Reads some data from an input stream.
 */
IOStatus readSomeFromInputStream(InputStream* stream, void* buf, int size, int* bytesRead);

/* Reads as much data as is requested from an input stream, if possible.
 */
IOStatus readFromInputStream(InputStream* stream, void* retBuffer, int size, unsigned int *read);

/* Fast way to read a single character from an input stream.
 */
#define readCharFromInputStream(STREAM, RETCHAR) \
  (((STREAM)->readp == (STREAM)->avail ? fillInputStream(STREAM) : \
    IOStatusOk),\
   ((STREAM)->readp < (STREAM)->avail ? \
    (*(RETCHAR) = (STREAM)->buffer[(STREAM)->readp++]), IOStatusOk : \
    (STREAM)->status))

/* Pushes the last char read back onto the input stream.  This is
 * only valid if the last InputStream operation was readCharFromInputSTream().
 */
#define pushCharBackOnInputStream(STREAM) \
  ((STREAM)->readp > 0 ? ((STREAM)->readp--, IOStatusOk) : \
   IOStatusBufferOverflow)

/* Fast way to write a single character to an output stream.
 */
#define writeCharToOutputStream(STREAM, CHAR) \
     ((STREAM)->buffer[(STREAM)->writep++] = (CHAR), \
      ((STREAM)->writep == (STREAM)->bufferSize ? flushOutputStream(STREAM) : \
       IOStatusOk))

/* Destroys an input stream, closing its source if a closer function was
 * specified.
 */
IOStatus destroyInputStream(InputStream* stream);

/* Sets the timeout value, in ms, for an input stream.
 */
#define setInputStreamTimeout(STREAM, TIMEOUT) \
  (STREAM)->timeout = (TIMEOUT)

/* Creates a buffered output stream.
 */
OutputStream* createOutputStream(void* handle,
				 OutputStreamWriter writer,
				 OutputStreamCloser closer,
				 int bufferSize);

/* Writes data to a buffered output stream.  All data will be written before this returns
 * if possible.
 */
IOStatus writeToOutputStream(OutputStream* stream, const void* buf, int size);

/* Prints a C-style string to the output stream.
 */
#define printToOutputStream(STREAM, STRING) \
  writeToOutputStream((STREAM),(STRING),strlen(STRING))

/* Flushes buffered data to the output stream.
 */
IOStatus flushOutputStream(OutputStream* stream);

/* Copies an InputStream to an OutputStream.
 */
IOStatus copyToOutputStream(OutputStream* outStream, InputStream* inStream);

/* Destroys an output stream, flushing its buffered data and closing its
 * destination if a closer function was specified.
 */
IOStatus destroyOutputStream(OutputStream* stream);

/* Mode values for deferred output streams.
 */
typedef enum DeferredOutputStreamMode {
  OutputDeferred, /* All output buffered */
  OutputPassedThrough /* Buffered output flushed, subsequent output passed through */
} DeferredOutputStreamMode;

/* Buffer chain structure for deferred output streams.
 */
typedef struct DeferredOutputStreamBuffer
{
  char* buffer;
  int bufferSize;
  struct DeferredOutputStreamBuffer* next;
} DeferredOutputStreamBuffer;

/* Handle information for a deferred output stream
 */
typedef struct DeferredOutputStreamHandle
{
  /* Output stream object that holds this handle
   */
  OutputStream* myOutputStream;

  /* Output mode for the stream, either deferred or passed-through.
   */
  DeferredOutputStreamMode mode;

  /* Raw output stream.
   */
  OutputStream* rawOutputStream;

  /* Chain of deferred output buffers
   */
  DeferredOutputStreamBuffer* deferredBuffers;

  /* Last deferred buffer in chain.
   */
  DeferredOutputStreamBuffer* lastDeferredBuffer;
} DeferredOutputStreamHandle;

/* Creates a deferred (buffered) output stream.
 */
IOStatus createDeferredOutputStream(OutputStream* rawOutputStream, OutputStream** retStream);

/* Returns the raw output stream for a deferred output stream.
 */
#define getRawOutputStreamForDeferred(OUTSTREAM) \
  ((DeferredOutputStreamHandle*)outStream->handle)->rawOutputStream

/* Changes the deferred output stream mode between buffered and passed-through.
 */
IOStatus setDeferredOutputMode(OutputStream* outStream, DeferredOutputStreamMode mode);

/* Writes all deferred output to the raw output stream.
 */
IOStatus writeDeferredOutput(OutputStream* outStream);

/* Creates (opens) a new file input stream.
 */
IOStatus createFileInputStream(const char* filename, InputStream** retStream);

/* Creates (opens) a new file output stream.
 */
IOStatus createFileOutputStream(const char* filename, OutputStream** retStream);

/* Connects a socket to a server and returns input and output streams for it.
 */
#define createSocketIOStreams(HOSTNAME, PORT, RETINSTREAM, RETOUTSTREAM) \
  createSocketIOStreamsWithTimeout((HOSTNAME), (PORT), 0, (RETINSTREAM), (RETOUTSTREAM))


#ifdef WIN32
/* Connects a socket to a server and returns input and output streams for it,
 * or fails if a connection cannot be established in a given number of
 * milliseconds.
 *
 * FIXME: Because createSocketIOStreamsFromAddressWithTimeout() is
 * only supported on WIN32, this function can only be supported on
 * WIN32
 */
IOStatus createSocketIOStreamsWithTimeout(const char* hostname, int port,
			       int timeout,
			       InputStream** retStream,
			       OutputStream** outStream);
#endif

/* Creates a new pair of I/O streams around a socket that is connected
 * at the indicated address.
 */
#define createSocketIOStreamsFromAddress(ADDR, RETINSTREAM, RETOUTSTREAM) \
  createSocketIOStreamsFromAddressWithTimeout((ADDR), 0, (RETINSTREAM), (RETOUTSTREAM))

#ifdef WIN32
/* FIXME: This function never worked reliably on UNIX hence the reason
 * it is only defined on WIN32.
 */
IOStatus
createSocketIOStreamsFromAddressWithTimeout(struct sockaddr_in address,
					  int timeout,
					  InputStream** retInStream,
					  OutputStream** retOutStream);
#endif

#ifndef WIN32 /* assume it's UNIX */
IOStatus createSocketIOStreamsFromAddressWithBlocking(struct sockaddr_in address,
						  InputStream** retInStream,
						  OutputStream** retOutStream);
#endif

/* Given a host name and port number, this returns an internet address.
 */
IOStatus getSocketAddress(const char* hostname, int port, struct sockaddr_in* retAddress);

#ifdef WIN32
/* Wraps a generic I/O handle with an input stream.
 */
IOStatus createHandleInputStream(HANDLE handle, int doClose, InputStream** retStream);

/* Wraps a generic I/O handle with an output stream.
 */
IOStatus createHandleOutputStream(HANDLE handle, int doClose, OutputStream** retStream);

/* Wraps a SOCKET with an input stream.
 */
IOStatus createSocketInputStream(SOCKET sock, int doClose, InputStream** retStream);

/* Wraps a SOCKET with an output stream.
 */
IOStatus createSocketOutputStream(SOCKET sock, int doClose, OutputStream** retStream);
#endif /* WIN32 */

#ifdef UNIX
/* Wraps a file descriptor with an input stream
 */
IOStatus createFdInputStream(int fd, int doClose, InputStream** retStream);

/* Wraps a file descriptor with an output stream.
 */
IOStatus createFdOutputStream(int fd, int doClose, OutputStream** retStream);
#endif /* UNIX */

/* Destroy Input/Output stream pair
 */
IOStatus destroyIOStreams(InputStream* pInStream,OutputStream* pOutStream);

#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif

#endif /* !_IO_H_ */
