/*
 * Copyright 2002 Niels Provos <provos@citi.umich.edu>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Niels Provos.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include <sys/types.h>
#include <sys/time.h>
#include <sys/queue.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>

#include <event.h>
#include <io.h>

struct io_filter *multi;

#define DIEMESSAGE	"DEAD.\n"
#define ENTERMESSAGE	"ENTER.\n"
#define NAMEMESSAGE	"Enter name: "

int
filter_append(void *arg, struct io_filter *filter, struct io_buffer *buf, 
	      int flags)
{
	struct io_buffer *mybuf = &filter->io_buf;
	char *name = arg;
	int len;

	if (buf->ready == 0 && !(flags & IOFLAG_FLUSH))
		return (IOFILT_NEEDMORE);

	if (flags & IOFLAG_FLUSH)
		len = strlen(name) + strlen(DIEMESSAGE);
	else
		len = buf->ready + strlen(name);

	if (io_buffer_extend(mybuf, len + 1) == -1)
		return (IOFILT_ERROR);

	if (flags & IOFLAG_FLUSH) {
		strcpy(mybuf->data, name);
		memcpy(mybuf->data + strlen(name), DIEMESSAGE,
		       strlen(DIEMESSAGE));
	} else {
		strcpy(mybuf->data, name);
		memcpy(mybuf->data + strlen(name), buf->data, buf->ready);
	}

	mybuf->ready = mybuf->off = len;

	return (IOFILT_DONE);
}

int
filter_filter(void *arg, struct io_filter *filter, struct io_buffer *buf, 
	      int flags)
{
	char *name = arg;
	struct io_buffer *mybuf = &filter->io_buf;

	if (buf->ready >= strlen(name) &&
	    !strncmp(buf->data, name, strlen(name))) {
		mybuf->ready = 0;
		return (IOFILT_NEEDMORE);
	}
	
	if (io_buffer_append(mybuf, buf) == -1)
		return (IOFILT_ERROR);

	mybuf->ready = mybuf->off;

	return (IOFILT_DONE);
}

int
filter_response(void *arg, struct io_filter *filter, struct io_buffer *buf, 
		int flags)
{
	struct io_duplex *dplx = arg;
	struct io_filter *first, *second;
	int len;
	char prompt[32];
	char enter[128];
	
	len = sizeof(prompt) - 4;
	if (buf->ready - 1 < len)
		len = buf->ready - 1;

	while (len > 0) {
		if (buf->data[len] != '\r' &&
		    buf->data[len] != '\n') {
			len++;
			break;
		}
		len--;
	}

	if (len == 0)
		return (IOFILT_ERROR);

	prompt[0] = '<';
	memcpy(prompt + 1, buf->data, len);
	strcpy(prompt + len + 1, "> ");

	first = io_new_filter(filter_append, strdup(prompt));
	second = io_new_filter(filter_filter, strdup(prompt));

	io_attach(dplx->io_src, first);
	io_attach(first, multi);
	io_attach(multi, second);
	io_attach(second, dplx->io_dst);

	io_terminate((struct io_obj *)filter, IORES_END);

	snprintf(enter, sizeof(enter), "%sENTER.\n", prompt);
	io_data_write((struct io_obj *)multi, enter, strlen(enter));

	return (IOFILT_NEEDMORE);
}

int
client_new(int fd)
{
	struct io_duplex *dplx;
	struct io_filter *filt;

	if ((dplx = io_new_socket(fd)) == NULL) {
		close (fd);
		return (-1);
	}

	io_data_write(dplx->io_dst, NAMEMESSAGE, strlen(NAMEMESSAGE));

	filt = io_new_filter(filter_response, dplx);
	io_attach(dplx->io_src, filt);

	return (0);
}

int
filter_printfd(void *arg, struct io_filter *filter, struct io_buffer *buf, 
	     int flags)
{
	struct io_buffer *mybuf = &filter->io_buf;
	int newfd;

	if (io_buffer_extend(mybuf, 256) == -1)
		return (IOFILT_ERROR);

	if (buf->ready < sizeof (int))
		return (IOFILT_NEEDMORE);

	newfd = *(int *)buf->data;

	snprintf(mybuf->data, 256, "New connection on fd %d\n", newfd);

	mybuf->ready = mybuf->off = strlen(mybuf->data);

	client_new(newfd);

	return (IOFILT_DONE);
}

int
filter_multiplex(void *arg, struct io_filter *filter, struct io_buffer *buf, 
		 int flags)
{
	struct io_buffer *mybuf = &filter->io_buf;

	if (io_buffer_append(mybuf, buf) == -1)
		return (IOFILT_ERROR);

	mybuf->ready = mybuf->off;

	return (IOFILT_DONE);
}

int
main(int argc, char **argv)
{
	struct io_obj *src, *dst;
	struct io_filter *filt, *dummy;

	event_init();

	dst = io_new_sink(fileno(stdout));
	multi = io_new_filter(filter_multiplex, NULL);
	dummy = io_new_filter(NULL, NULL);

	/* The multiplexer has to be a persistent object, it
	 * must not die when one of its sources dies
	 */
	io_attach(dummy, multi);

	/* Print out all chatter */
	io_attach(multi, dst);

	src = io_new_listener("0.0.0.0", 8080);
	filt = io_new_filter(filter_printfd, NULL);
	io_attach(src, filt);

	/* Print out new connections */
	io_attach(filt, dst);

	event_dispatch();

	exit(0);
}
