#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/file.h>
#include "lwp/lwp.h"
#include "pdb.h"
#include "network.h"
/*
char	_sendbuf[BUFSIZ];
char	*_sendbuf_pos = _sendbuf; */

extern  char *msglist[]; 

int
dosend(char *str, char *buf, char **curpos, int sock)
{
	int	rc = 1;

	while (*str) {
		**curpos = *str;
		(*curpos)++, str++;
		if (*curpos >= &buf[BUFSIZ]) {
			rc = dodeliver(buf, curpos, sock);
			if (!rc)
				break;
		}
	}
	return rc;
}

int
dodeliver(char *buf, char **curpos, int sock)
{
	int	len = *curpos - buf;
	int	success;

	if (*curpos == buf)
		return;
	success = send_data(buf, len, sock);
	*curpos = buf;

	return success;
}

report_stack(char *msg)
{
	int	ssize, sused;

	LWP_StackUsed(LWP_ActiveProcess, &ssize, &sused);
	printf("(%s: stacksize=%d bytes, %d used)\n", msg, ssize, sused);
}

extern int	in_server_thread();

/*
 * Since we can only pass 1 pointer as a parm to a new thread,
 * and we have 3 pieces of info we want to pass it, we have to
 * create 1 structure with the 3 pieces of info in it, and then
 * pass our one pointer as a pointer to this structure.
 */

typedef struct ioparms {
	char	*iobuf;
	int	iosock;
	int	iolen;
	int	ioflags;
} IOPARMS;

#define	SIO_STANDALONE	0x1	/* Thread is on it's own, so kill itself */
				/* when done. */

/*
 * Create an I/O Parameter Block so we can pass multiple args to
 * threads.
 */

IOPARMS *
iopb(char *iobuf, int len, int sock, int flags)			
{
	IOPARMS	*iop = (IOPARMS *) domalloc(sizeof(IOPARMS));

	iop->iobuf = iobuf;
	iop->iolen = len;
	iop->iosock = sock;
	iop->ioflags = flags;

	return iop;
}

/*
 * DONT CALL THIS DIRECTLY.  Use sendblock() or sendblock_spawn().
 */

int
_sendblock_code(IOPARMS *iop)
{
	static struct timeval maxwait = { 3*60L, 0L }; /* secs, microsecs */

	int             wrote, written, rc;
	fd_set		wait_sock;
	char		*iobuf;
	extern int	errno;

	int		len	= iop->iolen;	/* Save some parms */
	int		sock	= iop->iosock;
	int		flags	= iop->ioflags;

	iobuf = domalloc(len);			/* Save the iobuf */
	bcopy(iop->iobuf, iobuf, len);
	free(iop);				/* Free parm area */
	
	written = 0;
	for (;;) {

	/*        printf("Trying to write %4d bytes to sock #%d...",
		       len - written, sock);  */
		wrote = write(sock, &iobuf[written], len - written);

		if (wrote != -1) {
			/* printf("Wrote %4d\n", wrote); */
			written += wrote;
			if (written >= len)
				break;
			continue;
		}


		if (errno != EWOULDBLOCK) {
			perror("write");		/* Error */
			break;
		}

		/*
		 * Okay, we've blocked, sleep till we can write more,
		 * or until our alarm tells us to give up (select's timeout).
		 */

		/* puts("[block]"); */
		FD_ZERO(&wait_sock);
		FD_SET(sock, &wait_sock);

		/*
		 * We now go to sleep, waiting for select to tell us
		 * when the socket we're trying to write to is ready
		 * to be written to again.
		 */

		SleepThread();
		rc = IOMGR_Select(sock, 0, &wait_sock, 0, &maxwait);
		WakeThread();

		/*
		 * Something finally happend.  Either sock is ready for
		 * more, we got tired of waiting (timeout), or an error
		 * happend.
		 */

		printf("Connection #%d %s", sock, rc ? "is ready.\n" : "");
		if (rc == 1)
			continue;		/* Ready for more */
		if (rc == -1)
			perror("IOMGR_Select");
		else if (!rc) {
			printf("has timed out (%d secs).\n", maxwait.tv_sec);
			sock_timeout(sock); 
		}
		break;
	}
	free(iobuf);
	if (written < len)
		printf("SendBlock: sent %4d bytes to sock #%d (%d requested)\n",
		       written, sock, len);

	if (flags & SIO_STANDALONE) {
		puts("Sendblock EndThread.");
/*		EndThread(sock); */
	}

	return written == len ? 1 : 0;
}


/*
 * sendblock_spawn creates a standalone thread who's job is to get the data
 * there eventually (_sendblock_code).  After either delivery or timeout,
 * the thread terminates.
 */

void
sendblock_spawn(char *iobuf, int len, int sock)
{
	/* printf("Spawning a sendblock([%.16s], %d, #%d)\n", iobuf, len, sock); */

	/* MakeThread(_sendblock_code,
		(char *) iopb(iobuf, len, sock, SIO_STANDALONE),
		3072); */
}


/*
 * This is just a parmeter intereface to _sendblock_code.  Should only be
 * called from an already active non-server thread.
 */
 

int
sendblock(char *iobuf, int len, int sock)
{
	
	return _sendblock_code(  iopb(iobuf, len, sock, 0x0)  );
}

/*
 * send_data tries to write a buffer.  If all isn't written, or a block
 * occurs, we spawn sendblock, which will wait around and try and finish the
 * job if it can.  This return the number of bytes that were actually
 * written, or -1 for error.  If we spawn a process, some number of bytes
 * that were actually written the first try will be returned (likely to be
 * 0).
 *
 * Calling send_data ensures that either 1) Your data will get there
 * eventually, or 2) It closes the connection because it didn't wake
 * up before our timeout.
 */

int
send_data(char iobuf[], int len, int sock)
{
	int             wrote;
	extern int	errno;

	/* printf("Trying to whump %4d bytes to sock #%d...", len, sock); */
	wrote = write(sock, iobuf, len);

	if (wrote == len) {
		/* printf("Wrote %4d\n", wrote); */
		return wrote;
	}

	if (wrote == -1 && errno != EWOULDBLOCK) {
		perror("write");
		return 0;
	}

	if (wrote < 0)
		puts("[block]"), wrote = 0;
	else
		printf("Wrote %4d\n", wrote);

	/*
	 * We've blocked.  Spawn a dude who'll hang around
	 * to send this...
	 */

/*	if (!in_server_thread()) */
		return sendblock(&iobuf[wrote], len - wrote, sock);
/* changed because of each conn having its own lwp */
/*	sendblock_spawn(&iobuf[wrote], len - wrote, sock); */

	/*
	 * Server continues about business, no return code.
	 */
}


/*
 * Send a certian portian of a given file to a given socket.  Task is done
 * as an almost standalone LWP.  The proc that makes us is responsible for
 * destroying us.
 */


void
nio_send_file(int sock, char *filename, long startpoint, long requested)
{
	long            fd, toread, red, sent, i,done;
	long            total_sent,should_send;
	char		iobuf[BUFSIZ + EOLM_LEN];
	struct stat	st;

	if ((fd = open(filename, O_RDONLY, 0644)) < 0) {
		char	*iobptr = iobuf;

		dosend("This file cannot be accessed at this time.\n.\r\n", iobuf, &iobptr, sock);
		dodeliver(iobuf, &iobptr, sock);
		return;
	}
	fstat(fd, &st);
	printf(" FileTime: %.24s\n", ctime(&st.st_mtime));
	printf(" FileSize: %ld bytes\n", st.st_size);
	printf("  Request: %ld bytes\n", requested);

	lseek(fd, startpoint, L_SET);

	total_sent = 0;
        done = FALSE;
	should_send = min(st.st_size - startpoint + EOLM_LEN,requested + EOLM_LEN);
	if (should_send < 0)
	  should_send = 4;
	sprintf(iobuf,"%ld Total Characters :%ld sent:This document was last modified on %s\n",st.st_size,should_send,
		ctime(&st.st_mtime));
		red = strlen(iobuf);
	sent = sendblock(iobuf,red ,sock);
	for (i = 0; ; i++) {
		if (requested - total_sent < BUFSIZ)
			toread = requested - total_sent;
		else
			toread = BUFSIZ;
		red = read(fd, iobuf, toread);
		if (toread < BUFSIZ || red < toread)
			strcpy(&iobuf[red], EOLM), red += EOLM_LEN,done = TRUE;
		sent = sendblock(iobuf, red, sock);
		if (sent)
		  total_sent += red;
		if ((sent==FALSE) || total_sent >= requested + EOLM_LEN ||done)
			break;
		if (i % 2)
			LWP_DispatchProcess();
	}
	close(fd);
	printf("SENDFILE #%d done, %d bytes requested, %d sent, %ld should_send.\n",
	       sock, requested, total_sent,should_send);

/*
	if (total_sent != requested + EOLM_LEN
	    && startpoint + total_sent != st.st_size + EOLM_LEN)
		printf("CONNECTION #%d IS PROBABLY DEAD.\n", sock);
*/

}


