#include "global.h"
#include "mbuf.h"
#include "socket.h"
#include "usock.h"

int
so_los(up,protocol)
struct usock *up;
int protocol;
{
	up->cb.local = (struct loc *) callocw(1,sizeof(struct loc));
	up->cb.local->peer = up;	/* connect to self */
	up->type = TYPE_LOCAL_STREAM;
	up->cb.local->hiwat = LOCSFLOW;
	return 0;
}
int
so_lod(up,protocol)
struct usock *up;
int protocol;
{
	up->cb.local = (struct loc *) callocw(1,sizeof(struct loc));
	up->cb.local->peer = up;	/* connect to self */
	up->type = TYPE_LOCAL_DGRAM;
	up->cb.local->hiwat = LOCDFLOW;
	return 0;
}
int
so_lo_recv(up,bpp,from,fromlen)
struct usock *up;
struct mbuf **bpp;
char *from;
int *fromlen;
{
	int s;

	while(up->cb.local != NULLLOC && up->cb.local->q == NULLBUF
	  && up->cb.local->peer != NULLUSOCK){
		if(up->noblock){
			errno = EWOULDBLOCK;
			return -1;
		} else if((errno = pwait(up)) != 0){
			return -1;
		}
	}
	if(up->cb.local == NULLLOC){
		errno = EBADF;
		return -1;
	}
	if(up->cb.local->q == NULLBUF &&
	   up->cb.local->peer == NULLUSOCK){
		errno = ENOTCONN;
		return -1;
	}
	/* For datagram sockets, this will return the
	 * first packet on the queue. For stream sockets,
	 * this will return everything.
	 */
	*bpp = dequeue(&up->cb.local->q);
	if(up->cb.local->q == NULLBUF && (up->cb.local->flags & LOC_SHUTDOWN)){
		s = up - Usock + SOCKBASE;
		close_s(s);
	}
	psignal(up,0);
	return len_p(*bpp);
}
int	
so_los_send(up,bp,to)
struct usock *up;
struct mbuf *bp;
char *to;
{
	if(up->cb.local->peer == NULLUSOCK){
		free_p(bp);
		errno = ENOTCONN;
		return -1;
	}
	append(&up->cb.local->peer->cb.local->q,bp);
	psignal(up->cb.local->peer,0);
	/* If high water mark has been reached, block */
	while(up->cb.local->peer != NULLUSOCK &&
	      len_p(up->cb.local->peer->cb.local->q) >=
	      up->cb.local->peer->cb.local->hiwat){
		if(up->noblock){
			errno = EWOULDBLOCK;
			return -1;
		} else if((errno = pwait(up->cb.local->peer)) != 0){
			return -1;
		}
	}
	if(up->cb.local->peer == NULLUSOCK){
		errno = ENOTCONN;
		return -1;
	}
	return 0;
}
int	
so_lod_send(up,bp,to)
struct usock *up;
struct mbuf *bp;
char *to;
{
	if(up->cb.local->peer == NULLUSOCK){
		free_p(bp);
		errno = ENOTCONN;
		return -1;
	}
	enqueue(&up->cb.local->peer->cb.local->q,bp);
	psignal(up->cb.local->peer,0);
	/* If high water mark has been reached, block */
	while(up->cb.local->peer != NULLUSOCK &&
	      len_q(up->cb.local->peer->cb.local->q) >=
	      up->cb.local->peer->cb.local->hiwat){
		if(up->noblock){
			errno = EWOULDBLOCK;
			return -1;
		} else if((errno = pwait(up->cb.local->peer)) != 0){
			return -1;
		}
	}
	if(up->cb.local->peer == NULLUSOCK){
		errno = ENOTCONN;
		return -1;
	}
	return 0;
}
int
so_lod_qlen(up,rtx)
struct usock *up;
int rtx;
{
	int len;

	switch(rtx){
	case 0:
		len = len_q(up->cb.local->q);
		break;
	case 1:
		if(up->cb.local->peer != NULLUSOCK)
			len = len_q(up->cb.local->peer->cb.local->q);
		break;
	}
	return len;
}
int
so_los_qlen(up,rtx)
struct usock *up;
int rtx;
{
	int len;

	switch(rtx){
	case 0:
		len = len_p(up->cb.local->q) + len_p(up->ibuf);
		break;
	case 1:
		if(up->cb.local->peer != NULLUSOCK)
			len = len_p(up->cb.local->peer->cb.local->q)
			      + len_p(up->obuf);
		break;
	}
	return len;
}
int
so_loc_shut(up,how)
struct usock *up;
int how;
{
	int s;

	s = up - Usock + SOCKBASE;

	if(up->cb.local->q == NULLBUF)
		close_s(s);
	else
		up->cb.local->flags = LOC_SHUTDOWN;
	return 0;
}
int
so_loc_close(up)
struct usock *up;
{
	if(up->cb.local->peer != NULLUSOCK){
		up->cb.local->peer->cb.local->peer = NULLUSOCK;
		psignal(up->cb.local->peer,0);
	}
	free_q(&up->cb.local->q);
	free(up->cb.local);
	return 0;
}
char *
lopsocket(p)
struct sockaddr *p;
{
	return "";
}
so_loc_stat(up)
struct usock *up;
{
	int s;

	s = up - Usock + SOCKBASE;

	tprintf("Inqlen: %d packets\n",socklen(s,0));
	tprintf("Outqlen: %d packets\n",socklen(s,1));
	return 0;
}
