/*							       	       HTANSI.c
**	ANSI C FILE DESCRIPTOR TRANSPORT
**
**	(c) COPYRIGHT MIT 1995.
**	Please first read the full copyright statement in the file COPYRIGH.
**	@(#) $Id: HTANSI.c,v 2.8 1999/02/22 22:10:10 frystyk Exp $
**
**      History:
**         HFN: writtem
*/

/* Library include files */
#include "wwwsys.h"
#include "WWWUtil.h"
#include "WWWCore.h"
#include "HTNetMan.h"
#include "HTANSI.h"					 /* Implemented here */

#include  "HTHstMan.h"			/* @@@ FIX ME @@@ */

struct _HTStream {
    const HTStreamClass *	isa;
    /* ... */
};

struct _HTInputStream {
    const HTInputStreamClass *	isa;    
    HTChannel *			ch;
    HTHost *			host;
    char *			write;			/* Last byte written */
    char *			read;			   /* Last byte read */
    int				b_read;
    char			data [FILE_BUFFER_SIZE];
};

struct _HTOutputStream {
    const HTOutputStreamClass *	isa;
    HTChannel *			ch;
    HTHost *			host;
    FILE *			fp;
};

/* ------------------------------------------------------------------------- */
/*				 READ STREAM				     */
/* ------------------------------------------------------------------------- */

PRIVATE int HTANSIReader_flush (HTInputStream * me)
{
    HTNet * net = HTHost_getReadNet(me->host);
    return net && net->readStream ?
	(*net->readStream->isa->flush)(net->readStream) : HT_OK;
}

PRIVATE int HTANSIReader_free (HTInputStream * me)
{
    HTNet * net = HTHost_getReadNet(me->host);
    if (net && net->readStream) {
	int status = (*net->readStream->isa->_free)(net->readStream);
	if (status == HT_OK) net->readStream = NULL;
	return status;
    }
    return HT_OK;
}

PRIVATE int HTANSIReader_abort (HTInputStream * me, HTList * e)
{
    HTNet * net = HTHost_getReadNet(me->host);
    if (net && net->readStream) {
	int status = (*net->readStream->isa->abort)(net->readStream, NULL);
	if (status != HT_IGNORE) net->readStream = NULL;
    }
    return HT_ERROR;
}

PRIVATE int HTANSIReader_read (HTInputStream * me)
{
    FILE * fp = HTChannel_file(me->ch);
    HTNet * net = HTHost_getReadNet(me->host);
    int status;

    /* Read the file desriptor */
    while (fp) {
	if ((me->b_read = fread(me->data, 1, FILE_BUFFER_SIZE, fp)) == 0){
	    if (ferror(fp)) {
		HTTRACE(PROT_TRACE, "ANSI read... READ ERROR\n");
	    } else {
		HTAlertCallback *cbf = HTAlert_find(HT_PROG_DONE);
		HTTRACE(PROT_TRACE, "ANSI read... Finished loading file %p\n" _ fp);
		if (cbf) (*cbf)(net->request, HT_PROG_DONE, HT_MSG_NULL,NULL,NULL,NULL);
		return HT_CLOSED;
	    }
	}

	/* Remember how much we have read from the input socket */
	HTTRACEDATA(me->data, me->b_read, "HTANSIReader_read me->data:");
	me->write = me->data;
	me->read = me->data + me->b_read;

	{
	    HTAlertCallback * cbf = HTAlert_find(HT_PROG_READ);
	    HTNet_addBytesRead(net, me->b_read);
	    if (cbf) {
		int tr = HTNet_bytesRead(net);
		(*cbf)(net->request, HT_PROG_READ, HT_MSG_NULL, NULL, &tr, NULL);
	    }
	}

	/* Now push the data down the stream */
	if ((status = (*net->readStream->isa->put_block)
	     (net->readStream, me->data, me->b_read)) != HT_OK) {
	    if (status == HT_WOULD_BLOCK) {
		HTTRACE(PROT_TRACE, "ANSI read... Target WOULD BLOCK\n");
		return HT_WOULD_BLOCK;
	    } else if (status == HT_PAUSE) {
		HTTRACE(PROT_TRACE, "ANSI read... Target PAUSED\n");
		return HT_PAUSE;
	    } else if (status > 0) {	      /* Stream specific return code */
		HTTRACE(PROT_TRACE, "ANSI read... Target returns %d\n" _ status);
		me->write = me->data + me->b_read;
		return status;
	    } else {				     /* We have a real error */
		HTTRACE(PROT_TRACE, "ANSI read... Target ERROR\n");
		return status;
	    }
	}
	me->write = me->data + me->b_read;
    }
    HTTRACE(PROT_TRACE, "ANSI read... File descriptor is NULL...\n");
    return HT_ERROR;
}

/*
**	The difference between the close and the free method is that we don't
**	close the connection in the free method - we only call the free method
**	of the target stream. That way, we can keep the output stream as long 
**	as the channel itself.
*/
PRIVATE int HTANSIReader_close (HTInputStream * me)
{
    HTTRACE(PROT_TRACE, "ANSI read... FREEING...\n");
    HT_FREE(me);
    return HT_OK;
}

PRIVATE int HTANSIReader_consumed (HTInputStream * me, size_t bytes)
{
    HTTRACE(PROT_TRACE, "ANSI read... consumed %d bytes\n" _ bytes);
    return HT_OK;
}

PRIVATE const HTInputStreamClass HTANSIReader =
{		
    "ANSIReader",
    HTANSIReader_flush,
    HTANSIReader_free,
    HTANSIReader_abort,
    HTANSIReader_read,
    HTANSIReader_close,
    HTANSIReader_consumed
};

PUBLIC HTInputStream * HTANSIReader_new (HTHost * host, HTChannel * ch,
					 void * param,
					 int mode)
{
    if (host && ch) {
	HTInputStream * me = HTChannel_input(ch);
	if (me == NULL) {
	    if ((me=(HTInputStream *) HT_CALLOC(1, sizeof(HTInputStream))) == NULL)
	    HT_OUTOFMEM("HTANSIReader_new");
	    me->isa = &HTANSIReader;
	    me->ch = ch;
	}
	me->host = host;
	return me;
    }
    return NULL;
}

/* ------------------------------------------------------------------------- */
/*				WRITE STREAM				     */
/* ------------------------------------------------------------------------- */

PRIVATE int HTANSIWriter_flush (HTOutputStream * me)
{
    return (fflush(me->fp) == EOF) ? HT_ERROR : HT_OK;
}

PRIVATE int HTANSIWriter_free (HTOutputStream * me)
{
    return HT_OK;
}

PRIVATE int HTANSIWriter_abort (HTOutputStream * me, HTList * e)
{
    HTTRACE(PROT_TRACE, "ANSI write.. ABORTING...\n");
    return HT_ERROR;
}

PRIVATE int HTANSIWriter_character (HTOutputStream * me, char c)
{
    return (fputc(c, me->fp) == EOF) ? HT_ERROR : HT_OK;
}

PRIVATE int HTANSIWriter_string (HTOutputStream * me, const char* s)
{
    if (*s)				             /* For vms :-( 10/04-94 */
	return (fputs(s, me->fp) == EOF) ? HT_ERROR : HT_OK;
    return HT_OK;
}

PRIVATE int HTANSIWriter_block (HTOutputStream * me, const char* s, int l)
{
    int status ;
    status = (fwrite(s, 1, l, me->fp) != l) ? HT_ERROR : HT_OK ;
    if (l > 1 && status == HT_OK) (void) HTANSIWriter_flush(me);
    return status;
}

/*
**	The difference between the close and the free method is that we don't
**	close the connection in the free method - we only call the free method
**	of the target stream. That way, we can keep the output stream as long 
**	as the channel itself.
*/
PRIVATE int HTANSIWriter_close (HTOutputStream * me)
{
    HTTRACE(PROT_TRACE, "ANSI write.. FREEING...\n");
    HT_FREE(me);
    return HT_OK;
}

PRIVATE const HTOutputStreamClass HTANSIWriter =
{		
    "ANSIWriter",
    HTANSIWriter_flush,
    HTANSIWriter_free,
    HTANSIWriter_abort,
    HTANSIWriter_character,
    HTANSIWriter_string,
    HTANSIWriter_block,
    HTANSIWriter_close,
};

PUBLIC HTOutputStream * HTANSIWriter_new (HTHost * host, HTChannel * ch,
					  void * param, int mode)
{
    if (host && ch) {
	HTOutputStream * me = HTChannel_output(ch);
	if (me == NULL) {
	    if ((me=(HTOutputStream *) HT_CALLOC(1, sizeof(HTOutputStream)))==NULL)
		HT_OUTOFMEM("HTANSIWriter_new");
	    me->isa = &HTANSIWriter;
	    me->ch = ch;
	    me->host = host;
	    me->fp = HTChannel_file(ch);
	}
	return me;
    }
    return NULL;
}
