# include <kernel/kernel.h>
# include <kernel/user.h>
# if defined(SYS_NETWORKING) && defined(SYS_DATAGRAMS)
#  include <kernel/net.h>
# endif

inherit LIB_CONN;	/* basic connection object */


object driver;		/* driver object */
string buffer;		/* buffered input */

/*
 * NAME:	create()
 * DESCRIPTION:	initialize
 */
static void create(int clone)
{
    if (clone) {
	::create("binary");
	driver = find_object(DRIVER);
	buffer = "";
    }
}

/*
 * NAME:	open()
 * DESCRIPTION:	open the connection
 */
static int open()
{
    ::open(allocate(driver->query_tls_size()));
# ifdef SYS_DATAGRAMS
    return TRUE;
# else
    return FALSE;
# endif
}

/*
 * NAME:	close()
 * DESCRIPTION:	close the connection
 */
static void close(int dest)
{
    ::close(allocate(driver->query_tls_size()), dest);
}

/*
 * NAME:	receive_message()
 * DESCRIPTION:	forward a message to listeners
 */
static void receive_message(string str)
{
    int mode, len;
    string head, pre;
    mixed *tls;

    tls = allocate(driver->query_tls_size());
    catch {
	buffer += str;
    } : error("Binary connection buffer overflow");

    while (this_object() &&
	   (mode=query_mode()) != MODE_BLOCK && mode != MODE_DISCONNECT) {
	if (mode != MODE_RAW) {
	    if (sscanf(buffer, "%s\r\n%s", str, buffer) != 0 ||
		sscanf(buffer, "%s\n%s", str, buffer) != 0) {
		while (sscanf(str, "%s\b%s", head, str) != 0) {
		    while (sscanf(head, "%s\x7f%s", pre, head) != 0) {
			len = strlen(pre);
			if (len != 0) {
			    head = pre[0 .. len - 2] + head;
			}
		    }
		    len = strlen(head);
		    if (len != 0) {
			str = head[0 .. len - 2] + str;
		    }
		}
		while (sscanf(str, "%s\x7f%s", head, str) != 0) {
		    len = strlen(head);
		    if (len != 0) {
			str = head[0 .. len - 2] + str;
		    }
		}

		::receive_message(tls, str);
	    } else {
		break;
	    }
	} else {
	    if (strlen(buffer) != 0) {
		str = buffer;
		buffer = "";
		::receive_message(tls, str);
	    }
	    break;
	}
    }
}

/*
 * NAME:	set_mode()
 * DESCRIPTION:	set the connection mode
 */
void set_mode(int mode)
{
    string str;

    if (KERNEL() || SYSTEM()) {
	::set_mode(mode);
	if (mode == MODE_RAW && strlen(buffer) != 0) {
	    /* flush buffer */
	    str = buffer;
	    buffer = "";
	    ::receive_message(nil, str);
	}
    }
}

/*
 * NAME:	message()
 * DESCRIPTION:	send a message to the other side
 */
int message(string str)
{
    if (query_mode() < MODE_RAW) {
	str = implode(explode("\n" + str + "\n", "\n"), "\r\n");
    }
    return ::message(str);
}

/*
 * NAME:	message_done()
 * DESCRIPTION:	called when output is completed
 */
static void message_done()
{
    ::message_done(allocate(driver->query_tls_size()));
}

# ifdef SYS_DATAGRAMS
#  ifdef SYS_NETWORKING

object udpchannel;	/* UDP channel object */

/*
 * NAME:	set_udpchannel()
 * DESCRIPTION:	set the UDP channel for this connection
 */
void set_udpchannel(object udp, string host, int port)
{
    if (previous_program() == LIB_PORT) {
	udpchannel = udp;
	udp->add_connection(this_object(), host, port);
    }
}

/*
 * NAME:	receive_datagram()
 * DESCRIPTION:	receive a datagram
 */
void receive_datagram(mixed *tls, string str)
{
    if (previous_object() == udpchannel) {
	object user;

	user = query_user();
	if (user) {
	    user->receive_datagram(str);
	}
    }
}

/*
 * NAME:	datagram()
 * DESCRIPTION:	send a datagram on the UDP channel
 */
int datagram(string str)
{
    if (previous_object() == query_user() && udpchannel) {
	return udpchannel->datagram(str);
    }
}

#  else	/* !SYS_NETWORKING */

/*
 * NAME:	receive_datagram()
 * DESCRIPTION:	receive a datagram
 */
static void receive_datagram(string str)
{
    ::receive_datagram(allocate(driver->query_tls_size()), str);
}

#  endif /* !SYS_NETWORKING */
# endif	/* SYS_DATAGRAMS */
