#include <pctcp/types.h>
#include <pctcp/pctcp.h>
#include <pctcp/netinfo.h>
#include "funcdef.h"
#include "../defs.h"
#include "../funcs.h"

typedef unsigned long u_long;
typedef unsigned short u_short;

int _near neterrno;
int _near netsuberrno;

char _near shared_buf[4096];

static int _fastcall func_sendto(int, int, struct net_addr _far *, int);
static int _fastcall func_info(int, struct net_info _far *);
static int _fastcall func_accept(int, struct net_addr _far *);
static int _fastcall func_recv(int, int, struct net_addr _far *);

int
rpc_entry(buf, size)
short _far *buf;
int size;
{
	u_long x, y;

	switch ((char)buf[0]) {
	    case FUNC_INIT:
		dfputs("PC/TCP transport layer\r\n");
		buf[0] = locate_sys_vector();		/* Status */
		buf[2] = (u_short)shared_buf;
		buf[3] = (u_short)((u_long)(char far *)shared_buf >> 16);
		buf[4] = sizeof shared_buf;
		return 10;
	    case FUNC_SHUTDOWN:
		net_abortall();
		net_releaseall();
		return 0;
	    case FUNC_SOCKET:
		buf[0] = net_socket(buf[1], (struct net_addr _far *)&buf[2]);
		if (buf[0] < 0)
		    break;
		return sizeof (struct net_addr) + 2 * sizeof (short);
	    case FUNC_INFO:
		buf[0] = func_info(buf[1], (struct net_info _far *)&buf[1]);
		if (buf[0] < 0)
		    break;
		return sizeof (struct net_info) + sizeof (short);
	    case FUNC_ACCEPT:
		buf[0] = func_accept(buf[1], (struct net_addr _far *)&buf[1]);
		if (buf[0] < 0)
		    break;
		return sizeof (struct net_addr) + sizeof (short);
	    case FUNC_DRAIN:
		buf[0] = _net_read(buf[1], shared_buf, sizeof shared_buf,
			0, NET_FLG_DRAIN);
		break;
	    case FUNC_RELEASE:
		buf[0] = net_release(buf[1]);
		break;
	    case FUNC_EOF:
		buf[0] = net_eof(buf[1]);
		break;
	    case FUNC_READ:
		buf[0] = _net_read(buf[1], shared_buf, buf[2], 0, 0);
		break;
	    case FUNC_WRITE:
		buf[0] = _net_write(buf[1], shared_buf, buf[2], 0);
		break;
	    case FUNC_RECV:
		buf[0] = func_recv(buf[1], buf[2], (struct net_addr _far *)&buf[1]);
		if (buf[0] < 0)
		    break;
		return sizeof (struct net_addr) + sizeof (short);
	    case FUNC_SENDTO:
		buf[0] = func_sendto(buf[1], buf[2],
			(struct net_addr _far *)&buf[4], buf[3]);
		break;
	    case FUNC_SELECT:
		/* PC/PCP wants nfds to be the highest index you
		 * are looking for.  Its maximum value is 31.
		 * The standard implementation is to make it the
		 * number of bits you are looking for.
		x = 0;
		y = 0;
		/* TODO: check for error */
		buf[0] = _net_select(buf[1] - 1, &x, &y);
		if (buf[0] < 0)
		    break;
		*(u_long _far *)&buf[2] &= x;
		*(u_long _far *)&buf[4] &= y;
		return 12;
	}
	if (buf[0] < 0) {
	    buf[1] = 0;
	    buf[2] = convert_error(neterrno, netsuberrno);
	    return 6;
	}
	return 2;
}

static int _fastcall
func_sendto(nd, len, addr, flags)
int nd, len, flags;
struct net_addr _far *addr;
{
    struct addr tcp_addr;

    tcp_addr.fhost = addr->host;
    tcp_addr.lsocket = 0;
    tcp_addr.fsocket = addr->socket;
    tcp_addr.protocol = 0;
    return _net_writeto(nd, shared_buf, len, &tcp_addr, flags);
}

static int _fastcall
func_info(nd, ni)
int nd;
struct net_info _far *ni;
{
    struct netinfo tcp_ni;

    if (net_info(nd, &tcp_ni) < 0)
	return -1;
    ni->ip_address = tcp_ni.ip_address;
    ni->ip_broadcast = (tcp_ni.ip_address & tcp_ni.ip_subnet) |
			(0xffffffffL & ~tcp_ni.ip_subnet);
    return 0;
}

static int _fastcall
func_accept(nd, addr)
int nd;
struct net_addr _far *addr;
{
    struct addr tcp_addr;

    if (get_peer(nd, &tcp_addr) < 0)
	return -1;
    addr->host = tcp_addr.fhost;
    addr->socket = tcp_addr.fsocket;
    return nd;
}

static int _fastcall
func_recv(fd, len, addr)
int fd, len;
struct net_addr _far *addr;
{
    int ret;
    struct addr tcp_addr;

    ret = _net_read(fd, shared_buf, len, &tcp_addr, 0);
    if (ret >= 0) {
	addr->host = tcp_addr.fhost;
	addr->socket = tcp_addr.fsocket;
    }
    return ret;
}
