/*
	The functions contained here should only be called by functions
	from functions in original library source file yclientio.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <ctype.h>
#include <math.h>
		
#define _USE_BSD
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/stat.h> 
	 
#include <netdb.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>          /* For inet_ntoa() */
#include <fcntl.h>

#include "../include/Y2/Y.h"
#include "../include/Y2/Ylib.h"
#include "../include/Y2/Yclientnet.h"


extern void YShutdownSocket(int socket, int how, time_t timeout);
extern void YCloseSocketWait(int socket, time_t timeout);


static u_int64_t htonl64(u_int64_t x);

static void YNetPrintError(
	FILE *stream,
	YConnection *con,
	u_int16_t chunk_length,
	u_int16_t major_op_code,
	u_int16_t minor_op_code,
	const char *mesg
);
void YSetEventToBeDisconnect(YEvent *event);
int YSendData(int s, const char *buf, int len);

int YNetSendAudioChangePreset(
	YConnection *con,
	const char *audio_mode_name
);
int YNetSendAudioChangeValues(
	YConnection *con,
	int sample_size, 
	int channels,
	YDataLength sample_rate,
	int direction,
	int allow_fragmenting,
	int num_fragments,
	int fragment_size
);
int YNetParseAudioChange(YCNP_STD_INPUTS_PROTO);

int YNetSendCycleChange(YConnection *con, long cycle_us);
int YNetParseCycleChange(YCNP_STD_INPUTS_PROTO);

int YNetSendDisconnect(YConnection *con, int reason);
int YNetParseDisconnect(YCNP_STD_INPUTS_PROTO);

int YNetSendSetHost(
	YConnection *con,
	u_int16_t minor_op_code,
	YIPUnion *ip
);
int YNetParseSetHost(YCNP_STD_INPUTS_PROTO);

int YNetSendSetMixerChannel(
	YConnection *con,
	int mixer_code,
	Coefficient value1, Coefficient value2,
	Coefficient value3, Coefficient value4
);
int YNetSendGetMixerChannel(YConnection *con, int mixer_code);
int YNetParseMixerChannel(YCNP_STD_INPUTS_PROTO);

int YNetSendSoundPlay(
	YConnection *con,
	YID yid,
	const char *path,
	YDataPosition pos,
	YVolumeStruct *volume,
	int sample_rate,
	int repeats
);
int YNetParseSoundPlay(YCNP_STD_INPUTS_PROTO);

int YNetSendSoundKill(YConnection *con, YID yid);
int YNetParseSoundKill(YCNP_STD_INPUTS_PROTO);

int YNetSendGetSoundObjectAttributes(
	YConnection *con,
	const char *path
);
int YNetParseSoundObjectAttributes(YCNP_STD_INPUTS_PROTO);

int YNetSendShutdown(YConnection *con, int reason);
int YNetParseShutdown(YCNP_STD_INPUTS_PROTO);

int YNetSendSync(YConnection *con, long cycle_ahead_us);
int YNetParseSync(YCNP_STD_INPUTS_PROTO);

int YNetSendGetAudioStats(YConnection *con);
int YNetParseAudioStats(YCNP_STD_INPUTS_PROTO);

int YNetSendGetServerStats(YConnection *con);
int YNetParseServerStats(YCNP_STD_INPUTS_PROTO);

int YNetSendListAudioModes(YConnection *con);
int YNetParseListAudioModes(YCNP_STD_INPUTS_PROTO);

int YNetSendSetSoundObjectPlayValues(
	YConnection *con, YEventSoundPlay *value
);
int YNetSendGetSoundObjectPlayValues(YConnection *con, YID yid);
int YNetParseSoundObjectPlayValues(YCNP_STD_INPUTS_PROTO);

int YNetSendClientMessage(
	YConnection *con,
	Boolean notify_self,
	int format,
	int type,
	const char *message,
	int length
);
int YNetParseClientMessage(YCNP_STD_INPUTS_PROTO);

int YNetSendYSHMSoundOpen(YConnection *con);
int YNetParseYSHMSoundOpen(YCNP_STD_INPUTS_PROTO);

int YNetSendAudioCDPlayTrack(YConnection *con, int track_number);
int YNetSendAudioCDStop(YConnection *con);
int YNetSendAudioCDEject(YConnection *con);
int YNetParseAudioCD(YCNP_STD_INPUTS_PROTO);

int YNetSendListAudioCDTracks(YConnection *con);
int YNetParseListAudioCDTracks(YCNP_STD_INPUTS_PROTO);


static int YNetParse(
	YConnection *con,
	YEvent *event,
	const u_int8_t *buf,
	u_int32_t chunk_length,
	u_int16_t major_op_code,
	u_int16_t minor_op_code
);
int YNetRecv(YConnection *con);


#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define ABSOLUTE(x)     (((x) < 0) ? ((x) * -1) : (x))


static u_int64_t htonl64(u_int64_t x)
{
	return(htonl(x));
#if 0
	u_int8_t *px = (u_int8_t *)&x, *py;
	py[7] = px[0];
	py[6] = px[1];
	py[5] = px[2];
	py[4] = px[3];
	py[3] = px[4];
	py[2] = px[5];
	py[1] = px[6];
	py[0] = px[7];
	return(*(u_int64_t *)py);
#endif
}

/*
 *	Prints YIFF network error to stream.
 */
static void YNetPrintError(
	FILE *stream, YConnection *con,
	u_int16_t chunk_length,
	u_int16_t major_op_code, u_int16_t minor_op_code,
	const char *mesg
)
{
	if(stream == NULL)
	    return;

	fprintf(
	    stream,
	    "Y client protocol error on connection: 0x%.8x\n",
	    (u_int32_t)con
	);
	fprintf( 
	    stream,
	    "    Major OP Code: %i\n",
	    major_op_code
	);
	fprintf(
	    stream,
	    "    Minor OP Code: %i\n",
	    minor_op_code
	);
	fprintf(
	    stream,
	    "    Segment Size: %i bytes\n",
	    chunk_length
	);

	if(mesg != NULL)
	    fprintf(
		stream,
		"    Remarks: %s\n",
		mesg
	    );
}

/*
 *	Turns event into a disconnect event.
 */
void YSetEventToBeDisconnect(YEvent *event)
{
	if(event == NULL)
	    return;

	/* Explicitly change event type to YDisconnect */
	event->type = YDisconnect;

	/* Set disconnect reason code */
	event->disconnect.reason = 0;
}


/*
 *	Sends binary data in buf to connection s.
 */
int YSendData(int s, const char *buf, int len)
{
	struct timeval timeout;
	fd_set writefds;

	int bytes_written;


	if(s < 0)
	    return(-1);

	timeout.tv_sec = 0;
	timeout.tv_usec = 0;
	FD_ZERO(&writefds);
	FD_SET(s, &writefds);
	select(s + 1, NULL, &writefds, NULL, &timeout);

	if(!FD_ISSET(s, &writefds))
	    return(-1);

	bytes_written = send(s, buf, len, 0);
	if(bytes_written < 0)
	    return(-1);
	else
	    return(bytes_written);
}


/*
 *	Sends audio mode change preset.
 */
int YNetSendAudioChangePreset(
	YConnection *con,
	const char *audio_mode_name
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <...audiomodename...>
	 */
	const YDataLength this_seg_len = 8 + YAudioNameMax;
	YDataLength actual_seg_len;
	int name_len;
	char buf[this_seg_len];


	if(audio_mode_name == NULL)
	    return(-1);

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioChange);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioChangePreset);

	name_len = MIN(strlen(audio_mode_name), YAudioNameMax);
	strncpy(
	    &buf[8],
	    audio_mode_name,
	    name_len
	);
	/* Do not null terminate string */

	/* Since this is a variable length segment we need to
	 * recalculate the segment length after the formatting
	 * above and update the segment header's chunk size.
	 */
	actual_seg_len = 8 + name_len;
	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

	return(YSendData(con->fd, buf, actual_seg_len));
}
/*      
 *      Sends audio mode change values.
 */     
int YNetSendAudioChangeValues(
	YConnection *con,
	int sample_size,
	int channels,
	YDataLength sample_rate,
	int direction,
	int allow_fragmenting,
	int num_fragments,
	int fragment_size
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u16_samplesize> <u16_channels> <u32_samplerate>
	 * <u8_direction>
	 * <u8_allowfrags> <u32_numfrags> <u32_fragsize>
	 */
	const YDataLength this_seg_len = 8 + 8 + 1 + 9;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioChange);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioChangeValues);

	*(u_int16_t *)(&buf[8]) = htons((u_int16_t)sample_size);
	*(u_int16_t *)(&buf[10]) = htons((u_int16_t)channels);
	*(u_int32_t *)(&buf[12]) = htonl((u_int32_t)sample_rate);

	*(u_int8_t *)(&buf[16]) = (u_int8_t)direction;

	*(u_int8_t *)(&buf[17]) = (u_int8_t)allow_fragmenting;
	*(u_int32_t *)(&buf[18]) = htonl((u_int32_t)num_fragments);
	*(u_int32_t *)(&buf[22]) = htonl((u_int32_t)fragment_size);

	return(YSendData(con->fd, buf, this_seg_len)); 
}
/*
 *	Parses audio mode change.
 */
int YNetParseAudioChange(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YAudioChangeValues)
	{
	    /* Parse Audio mode values */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u16_samplesize> <u16_channels> <u32_samplerate>
	     * <u8_direction>
	     * <u8_allowfrags> <u32_numfrags> <u32_fragsize>
	     */
	    const YDataLength this_seg_len = 8 + 8 + 1 + 9;
	    YEventAudioChange *audio = &event->audio;

	    if(chunk_length < this_seg_len)
		return(-1);

	    audio->preset = False;

	    *audio->mode_name = '\0';

	    audio->sample_size = ntohs(*(u_int16_t *)(&buf[8]));
	    audio->channels = ntohs(*(u_int16_t *)(&buf[10]));
	    audio->sample_rate = ntohl(*(u_int32_t *)(&buf[12]));

	    audio->direction = buf[16];

	    audio->allow_fragmenting = buf[17];
	    audio->num_fragments = ntohl(*(u_int32_t *)(&buf[18]));
	    audio->fragment_size_bytes = ntohl(*(u_int32_t *)(&buf[22]));
	}
	else if(minor_op_code == YAudioChangePreset)
	{
	    /* Parse change preset Audio mode */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <...audiomodename...>
	     */
	    const YDataLength base_seg_len = 8;
	    int mode_name_len;
	    YEventAudioChange *audio = &event->audio;

	    if(chunk_length < base_seg_len)
		return(-1);

	    mode_name_len = (YDataLength)chunk_length - (YDataLength)base_seg_len;
	    if(mode_name_len >= YAudioNameMax)
		mode_name_len = YAudioNameMax - 1;

	    if(mode_name_len > 0)
	    {
		strncpy(
		    audio->mode_name,
		    (char *)&buf[8],
		    mode_name_len
		);
		audio->mode_name[mode_name_len] = '\0';
	    }
	    else
	    {
		audio->mode_name[0] = '\0';
	    }

	    /* Clear rest of event members */
	    audio->preset = True;

	    audio->sample_size = 0;
	    audio->channels = 0;
	    audio->sample_rate = 0;

	    audio->direction = 0;

	    audio->allow_fragmenting = 0;
	    audio->num_fragments = 0;
	    audio->fragment_size_bytes = 0;
	}

	return(0);
}

/*
 *      Sends cycle change.
 */
int YNetSendCycleChange(YConnection *con, long cycle_us)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_cycleus>
	 */
	const YDataLength this_seg_len = 8 + 4;
	char buf[this_seg_len]; 

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YCycleChange);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);
	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)cycle_us);

	return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *	Parses cycle change.
 */
int YNetParseCycleChange(YCNP_STD_INPUTS_PROTO)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_cycleus>
	 */
	const YDataLength this_seg_len = 8 + 4;
	YEventCycleChange *cycle = &event->cycle;

	if(chunk_length < this_seg_len)
	    return(-1);

	cycle->cycle_us = ntohl(*(u_int32_t *)(&buf[8]));

	return(0);
}


/*
 *	Sends disconnect.
 */
int YNetSendDisconnect(YConnection *con, int reason)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u16_reason>
	 */
	const YDataLength this_seg_len = 8 + 2;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YDisconnect);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);
	*(u_int16_t *)(&buf[8]) = htons((u_int16_t)reason);

	return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *	Parses disconnect.
 */
int YNetParseDisconnect(YCNP_STD_INPUTS_PROTO)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u16_reason>
	 */
	const YDataLength this_seg_len = 8 + 2;
	YEventDisconnect *disconnect = &event->disconnect;

	if(chunk_length < this_seg_len)
	    return(-1);

	disconnect->reason = ntohs(*(u_int16_t *)(&buf[8]));

	return(0);
}

/*
 *	Sends a set host.
 */
int YNetSendSetHost(
	YConnection *con,
	u_int16_t minor_op_code,
	YIPUnion *ip
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u8_ipc1> <u8_ipc2> <u8_ipc3> <u8_ipc4>
	 */
	const YDataLength this_seg_len = 8 + 4;

	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSetHost);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)minor_op_code);
 
	*(u_int8_t *)(&buf[8]) = (u_int8_t)ip->charaddr[0];
	*(u_int8_t *)(&buf[9]) = (u_int8_t)ip->charaddr[1];
	*(u_int8_t *)(&buf[10]) = (u_int8_t)ip->charaddr[2];
	*(u_int8_t *)(&buf[11]) = (u_int8_t)ip->charaddr[3];

	return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *	Handles set host.
 */
int YNetParseSetHost(YCNP_STD_INPUTS_PROTO)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u8_ipc1> <u8_ipc2> <u8_ipc3> <u8_ipc4>
	 */
	const YDataLength this_seg_len = 8 + 4;
	YEventHost *host = &event->host;

	if(chunk_length < this_seg_len)
	    return(-1);

	host->op = ((minor_op_code == YSetHostAdd) ? 1 : 0);

	host->ip.charaddr[0] = (u_int8_t)buf[8];
	host->ip.charaddr[1] = (u_int8_t)buf[9];
	host->ip.charaddr[2] = (u_int8_t)buf[10];
	host->ip.charaddr[3] = (u_int8_t)buf[11];

	return(0);
}

/*
 *	Sends a mixer channel set.
 */
int YNetSendSetMixerChannel(
	YConnection *con,
	int mixer_code,
	Coefficient value1, Coefficient value2,
	Coefficient value3, Coefficient value4
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u16_mixercode>
	 * <u32_value1> <u32_value2>
	 * <u32_value3> <u32_value4>
	 */
	const YDataLength this_seg_len = 8 + 2 + 8 + 8;

	char buf[this_seg_len];
	u_int32_t ival[4];


	ival[0] = (u_int32_t)rint(value1 * (Coefficient)((u_int32_t)-1));
	ival[1] = (u_int32_t)rint(value2 * (Coefficient)((u_int32_t)-1));
	ival[2] = (u_int32_t)rint(value3 * (Coefficient)((u_int32_t)-1));
	ival[3] = (u_int32_t)rint(value4 * (Coefficient)((u_int32_t)-1));

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YMixerChannel);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YMixerChannelSet);

	*(u_int16_t *)(&buf[8]) = htons((u_int16_t)mixer_code);

	*(u_int32_t *)(&buf[10]) = htonl((u_int32_t)ival[0]);
	*(u_int32_t *)(&buf[14]) = htonl((u_int32_t)ival[1]);
	*(u_int32_t *)(&buf[18]) = htonl((u_int32_t)ival[2]);
	*(u_int32_t *)(&buf[22]) = htonl((u_int32_t)ival[3]);

	return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *      Sends a mixer channel get.
 */
int YNetSendGetMixerChannel(YConnection *con, int mixer_code)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u16_mixercode>
	 */
	const YDataLength this_seg_len = 8 + 2;

	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YMixerChannel);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YMixerChannelGet);

	*(u_int16_t *)(&buf[8]) = htons((u_int16_t)mixer_code);

	return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *	Handles mixer device set.
 */
int YNetParseMixerChannel(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YMixerChannelGet)
	{
	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u16_mixercode>
	     */
	    const YDataLength this_seg_len = 8 + 2;
  
	    if(chunk_length < this_seg_len)
		return(-1);
	}
	else if(minor_op_code == YMixerChannelSet)
	{
	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u16_mixercode>
	     * <u32_value1> <u32_value2>
	     * <u32_value3> <u32_value4>
	     */
	    const YDataLength this_seg_len = 8 + 2 + 8 + 8;
	    YEventMixer *mixer = &event->mixer;

	    if(chunk_length < this_seg_len)
		return(-1);

	    mixer->code = ntohs(*(u_int16_t *)(&buf[8]));

	    /* Get mixer values, check availibility with defination */
	    if(YMixerValues > 0)
		mixer->value[0] =
		    (Coefficient)ntohl(*(u_int32_t *)(&buf[10])) /
		    (Coefficient)((u_int32_t)-1);
	    if(YMixerValues > 1)
		mixer->value[1] =
		    (Coefficient)ntohl(*(u_int32_t *)(&buf[14])) /
		    (Coefficient)((u_int32_t)-1);
	    if(YMixerValues > 2)
		mixer->value[2] =
		    (Coefficient)ntohl(*(u_int32_t *)(&buf[18])) /
		    (Coefficient)((u_int32_t)-1);
	    if(YMixerValues > 3)
		mixer->value[3] =
		    (Coefficient)ntohl(*(u_int32_t *)(&buf[22])) /
		    (Coefficient)((u_int32_t)-1);
	}

	return(0);
}

/*
 *      Sends a sound play.
 */
int YNetSendSoundPlay(
	YConnection *con,
	YID yid,
	const char *path,
	YDataPosition pos,
	YVolumeStruct *volume,
	int sample_rate,
	int repeats
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_yid> <u32_pos>
	 * <u16_volume_left> <u16_volume_right>
	 * <u16_volume_back_left> <u16_volume_back_right>
	 * <u32_sample_rate> <u32_repeats>
	 * <...path...>
	 */
	const YDataLength this_seg_len = 8 + 8 + 4 + 4 + 8 + YPathMax;
	char buf[this_seg_len];
	YDataLength path_len, actual_seg_len;

	if(repeats < 0)
	    repeats = 0;

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectPlay);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);

	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)yid);
	*(u_int32_t *)(&buf[12]) = htonl((u_int32_t)pos);

	*(u_int16_t *)(&buf[16]) = htons((u_int16_t)volume->left);
	*(u_int16_t *)(&buf[18]) = htons((u_int16_t)volume->right);

	*(u_int16_t *)(&buf[20]) = htons((u_int16_t)volume->back_left);
	*(u_int16_t *)(&buf[22]) = htons((u_int16_t)volume->back_right);

	*(u_int32_t *)(&buf[24]) = htonl((u_int32_t)sample_rate);
	*(u_int32_t *)(&buf[28]) = htonl((u_int32_t)repeats);

	path_len = MIN(strlen(path), YPathMax);
	strncpy(
	    &buf[32],
	    path,
	    path_len
	);
	/* Do not null terminate string */

	/* Since this is a variable length segment we need to
	 * recalculate the segment length after the formatting
	 * above and update the segment header's chunk size.
	 */
	actual_seg_len = 8 + 8 + 4 + 4 + 8 + path_len;
	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

	return(YSendData(con->fd, buf, actual_seg_len));
}
/*
 *	Handles sound play.
 */
int YNetParseSoundPlay(YCNP_STD_INPUTS_PROTO)
{
	if(True)	/* No minor op codes */
	{
	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u32_flags> <u32_yid>
	     * <u32_position> <u32_length>
	     * <u32_repeats> <u32_total_repeats>
	     * <u16_volume_left> <u16_volume_right>
	     * <u16_volume_back_left> <u16_volume_back_right>
	     * <u32_sample_rate>
	     */
	    const YDataLength this_seg_len = 8 + 8 + 8 + 8 + 4 + 4 + 4;
	    YEventSoundPlay *play = &event->play;

	    if(chunk_length < this_seg_len)
		return(-1);

#define BUF_TO_COEFFICIENT(x)   ( \
  (Coefficient)(x) / (Coefficient)((u_int16_t)-1) \
)
	    play->flags = (unsigned long)ntohl(*(u_int32_t *)(&buf[8]));
	    play->yid = (YID)ntohl(*(u_int32_t *)(&buf[12]));
	    play->position = (YDataPosition)ntohl(*(u_int32_t *)(&buf[16]));
	    play->length = (YDataLength)ntohl(*(u_int32_t *)(&buf[20]));
	    play->repeats = ntohl(*(u_int32_t *)(&buf[24]));
	    play->total_repeats = ntohl(*(u_int32_t *)(&buf[28]));
	    play->left_volume = BUF_TO_COEFFICIENT(
		ntohs(*(u_int16_t *)(&buf[32]))
	    );
	    play->right_volume = BUF_TO_COEFFICIENT(
		ntohs(*(u_int16_t *)(&buf[34]))
	    );
	    play->back_left_volume = BUF_TO_COEFFICIENT(
		ntohs(*(u_int16_t *)(&buf[36]))
	    );
	    play->back_right_volume = BUF_TO_COEFFICIENT(
		ntohs(*(u_int16_t *)(&buf[38]))
	    );
	    play->sample_rate = ntohl(*(u_int32_t *)(&buf[40]));
#undef BUF_TO_COEFFICIENT
	}

	return(0);
}

/*
 *	Sends a sound kill.
 */
int YNetSendSoundKill(YConnection *con, YID yid)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_yid>
	 */
	const YDataLength this_seg_len = 8 + 4;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectKill);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);
	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)yid);

	return(YSendData(con->fd, buf, this_seg_len));
}       
/*
 *	Parses sound kill.
 */
int YNetParseSoundKill(YCNP_STD_INPUTS_PROTO)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_yid>
	 */
	const YDataLength this_seg_len = 8 + 4;
	YEventSoundKill *kill = &event->kill;

	if(chunk_length < this_seg_len)
	    return(-1);

	kill->yid = ntohl(*(u_int32_t *)(&buf[8]));

	return(0);
}

/*
 *	Sends get sound object attributes.
 */
int YNetSendGetSoundObjectAttributes(
	YConnection *con, const char *path
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <...path...>
	 */
	const YDataLength this_seg_len = 8 + YPathMax;
	YDataLength actual_seg_len;
	YDataLength path_len;
	char buf[this_seg_len];

	if(path == NULL)
	    return(-1);

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectAttributes);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSoundObjectAttributesGet);

	path_len = MIN(strlen(path), YPathMax);
	strncpy(
	    &buf[8],
	    path,
	    path_len
	);
	/* Do not null terminate string */

	/* Since this is a variable length segment we need to
	 * recalculate the segment length after the formatting
	 * above and update the segment header's chunk size.
	 */
	actual_seg_len = 8 + path_len;
	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

	return(YSendData(con->fd, buf, actual_seg_len));
}
/*
 *	Handles sound object attributes.
 */
int YNetParseSoundObjectAttributes(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YSoundObjectAttributesGet)
	{

	}
	else if(minor_op_code == YSoundObjectAttributesSet)
	{
	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u16_format>
	     * <u16_samplesize> <u16_channels> <u32_samplerate>
	     * <u32_length>
	     * <...path...>
	     */
	    const YDataLength base_seg_len = 8 + 2 + 8 + 4;
	    YDataLength path_len;
	    YEventSoundObjectAttributes *soa = &event->sndobjattributes;

	    if(chunk_length < base_seg_len)
		return(-1);

	    soa->format = ntohs(*(u_int16_t *)(&buf[8]));
	    soa->sample_size = ntohs(*(u_int16_t *)(&buf[10]));
	    soa->channels = ntohs(*(u_int16_t *)(&buf[12]));
	    soa->sample_rate = ntohl(*(u_int32_t *)(&buf[14]));
	    soa->length = ntohl(*(u_int32_t *)(&buf[18]));

	    path_len = (YDataLength)chunk_length - (YDataLength)base_seg_len;
	    if(path_len >= YPathMax)
		path_len = YPathMax - 1;

	    if(path_len > 0)
	    {
	        strncpy(
		    soa->path,
		    (char *)(&buf[22]),
		    path_len
	        );
		soa->path[path_len] = '\0';
	    }
	    else
	    {
		*soa->path = '\0';
	    }
	}

	return(0);
}

/*
 *      Sends shutdown.
 */
int YNetSendShutdown(YConnection *con, int reason)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u16_reason>
	 */
	const YDataLength this_seg_len = 8 + 2;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YShutdown);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);
	*(u_int16_t *)(&buf[8]) = htons((u_int16_t)reason);

	return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *      Parses shutdown.
 */
int YNetParseShutdown(YCNP_STD_INPUTS_PROTO)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u16_reason>
	 */
	const YDataLength this_seg_len = 8 + 2;
	YEventShutdown *shutdown = &event->shutdown;

	if(chunk_length < this_seg_len)
	    return(-1);

	shutdown->reason = ntohs(*(u_int16_t *)(&buf[8]));

	return(0);
}       


/*
 *      Sends sync.
 */
int YNetSendSync(YConnection *con, long cycle_ahead_us)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_cycleaheadus>
	 */
	const YDataLength this_seg_len = 8 + 4;
	char buf[this_seg_len];

	/* Sanitize cycle_ahead_us */
	if(cycle_ahead_us < 0)
	    cycle_ahead_us = 0;

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSync);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)0);
	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)cycle_ahead_us);

	return(YSendData(con->fd, buf, this_seg_len));
}
/*        
 *      Parses sync.
 */
int YNetParseSync(YCNP_STD_INPUTS_PROTO)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_cycleaheadus>
	 */
	const YDataLength this_seg_len = 8 + 4;
	YEventSync *sync = &event->sync;

	if(chunk_length < this_seg_len)
	    return(-1);

	sync->cycle_ahead_us = ntohl(*(u_int32_t *)(&buf[8]));

	return(0);
}

/*
 *	Sends get audio stats.
 */
int YNetSendGetAudioStats(YConnection *con)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 */
	const YDataLength this_seg_len = 8;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioStats);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioStatsGet);

	return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *	Handles set audio stats.
 */
int YNetParseAudioStats(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YAudioStatsSet)
	{
	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u8_cycleset>
	     * <u32_cycleus> <u32_compensatedcycleus>  
	     * <u32_writeaheadus> <u32_cumulativelatencyus>
	     * <u16_samplesize> <u16_channels> <u32_samplerate>
	     * <u32_bytespersec>
	     * <u8_allowfragments> <u32_numfragments> <u32_fragmentsize>  
	     * <u8_flipstereo>
	     * <u8_direction>
	     * <...audio_mode_name...>
	     */
	    const YDataLength base_seg_len = 8 + 1 + 8 + 8 + 8 + 4 + 9 +
					     1 + 1;
/*	    const YDataLength this_seg_len = 8 + 1 + 8 + 8 + 8 + 4 + 9 +
					     1 + 1 + YAudioNameMax; */
	    YDataLength name_len;
	    YEventAudioStats *as = &event->audiostats;
	    if(chunk_length < base_seg_len)
		return(-1);

	    as->cycle_set = (int)buf[8];

	    as->cycle_us = ntohl(*(u_int32_t *)(&buf[9]));
	    as->compensated_cycle_us = ntohl(*(u_int32_t *)(&buf[13]));

	    as->write_ahead_us = ntohl(*(u_int32_t *)(&buf[17]));
	    as->cumulative_latency_us = ntohl(*(u_int32_t *)(&buf[21]));

	    as->sample_size = ntohs(*(u_int16_t *)(&buf[25]));
	    as->channels = ntohs(*(u_int16_t *)(&buf[27]));
	    as->sample_rate = ntohl(*(u_int32_t *)(&buf[29]));
	    as->bytes_per_sec = ntohl(*(u_int32_t *)(&buf[33]));

	    as->allow_fragments = ((buf[37]) ? True : False);
	    as->num_fragments = ntohl(*(u_int32_t *)(&buf[38]));
	    as->fragment_size = ntohl(*(u_int32_t *)(&buf[42]));

	    as->flip_stereo = ((buf[46]) ? True : False);
	    as->direction = (int)buf[47];

	    name_len = (YDataLength)chunk_length - (YDataLength)base_seg_len;
	    if(name_len >= YAudioNameMax)
		name_len = YAudioNameMax - 1;
	    if(name_len > 0)
	    {
		strncpy(
		    as->mode_name,
		    (char *)(&buf[48]),
		    name_len
		);
		as->mode_name[name_len] = '\0';
	    }
	    else
	    {
		*as->mode_name = '\0';
	    }

	}

	return(0);
}

/*
 *      Send get server stats.   
 */
int YNetSendGetServerStats(YConnection *con)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 */
	const YDataLength this_seg_len = 8;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YServerStats);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YServerStatsGet);

	return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *      Parses server stats.
 */
int YNetParseServerStats(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YServerStatsGet)
	{
	    /* Parse server stats get */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     */
	    const YDataLength this_seg_len = 8;

	    if(chunk_length < this_seg_len)
		return(-1);
	}
	else if(minor_op_code == YServerStatsSet)
	{
	    /* Parse server stats set */      

	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u32_protocolvermaj> <u32_protocolvermin>
	     * <u16_load>
	     * <u64_start_time>
	     * <...vendor_name, server_name, sound_name...>
	     */
	    const YDataLength base_seg_len = 8 + 8 + 2 + 8;
	    YEventServerStats *sstat = &event->serverstats;

	    if(chunk_length < base_seg_len)
		return(-1);

	    sstat->protocol_version_major =
		ntohl(*(u_int32_t *)(&buf[8]));
	    sstat->protocol_version_minor =
		ntohl(*(u_int32_t *)(&buf[12]));

	    sstat->cycle_load =
		(double)ntohs(*(u_int16_t *)(&buf[16])) /
		(double)((u_int16_t)-1);

	    sstat->start_time = (u_int64_t)htonl64(*(u_int64_t *)(&buf[18]));

	    /* Begin parsing strings */
	    if(chunk_length > base_seg_len)
	    {
		const char	*s = (const char *)&buf[base_seg_len],
				*s_end = (const char *)&buf[chunk_length];

		/* Vendor name */
		char c, *ts = sstat->vendor_name;
		int i = 0, tlen = YVendorNameMax;
		while((s < s_end) && (i < tlen))
		{
		    ts[i] = c = *s;
		    if(c == '\0')
			break;
		    s++; i++;
		}
		if(i < tlen)
		    ts[i] = '\0';
		else
		    ts[tlen - 1] = '\0';

		/* Server name */
		ts = sstat->server_name;
		i = 0; tlen = YServerNameMax;
		s++;
		while((s < s_end) && (i < tlen))
		{
		    ts[i] = c = *s;
		    if(c == '\0')
			break;
		    s++; i++;
		}
		if(i < tlen)
		    ts[i] = '\0';
		else
		    ts[tlen - 1] = '\0';

		/* Sound name */
		ts = sstat->sound_name;
		i = 0; tlen = YSoundNameMax;
		s++;
		while((s < s_end) && (i < tlen))
		{
		    ts[i] = c = *s;
		    if(c == '\0')
			break;
		    s++; i++;
		}
		if(i < tlen)
		    ts[i] = '\0';
		else
		    ts[tlen - 1] = '\0';
	    }

	    /* More items to be added in future */

	}


	return(0);
}

/*
 *	Send list audio modes.
 */
int YNetSendListAudioModes(YConnection *con)   
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 */
	const YDataLength this_seg_len = 8;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YListAudioModes);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YListAudioModesGet);

	return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *      Parses list audio modes.
 */
int YNetParseListAudioModes(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YListAudioModesGet)
	{
	    /* Parse list audio modes get */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     */
	    const YDataLength this_seg_len = 8;

	    if(chunk_length < this_seg_len)
		return(-1);
	}
	else if(minor_op_code == YListAudioModesSet)
	{
	    /* Parse list audio modes set */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u32_samplerate> <u32_channels>
	     * <u32_samplesize> <u32_fragmentsizebytes>
	     * <u8_direction> <u8_allowfragmenting> <u32_numfragments>
	     * <...audiomodename...>
	     */
	    const YDataLength base_seg_len = 8 + 8 + 8 + 6;
	    int name_len;
	    YEventAudioChange *audio = &event->audio;

	    if(chunk_length < base_seg_len)
		return(-1);

	    /* We use the YEventAudioChange audio event for convience
	     * in the fetching and organization of these values.
	     */

	    name_len = (YDataLength)chunk_length - (YDataLength)base_seg_len;
	    if(name_len >= YAudioNameMax)
		name_len = YAudioNameMax - 1;

	    if(name_len > 0)
	    {
		strncpy(
		    audio->mode_name,
		    (char *)&buf[30],
		    name_len
		);
		audio->mode_name[name_len] = '\0';
	    }
	    else
	    {
		*audio->mode_name = '\0';
	    }

	    audio->preset = True;

	    audio->sample_rate = ntohl(*(u_int32_t *)(&buf[8]));
	    audio->channels = ntohl(*(u_int32_t *)(&buf[12]));
	    audio->sample_size = ntohl(*(u_int32_t *)(&buf[16]));
	    audio->fragment_size_bytes = ntohl(*(u_int32_t *)(&buf[20]));

	    audio->direction = (u_int8_t)buf[24];
	    audio->allow_fragmenting = (u_int8_t)buf[25];
	    audio->num_fragments = ntohl(*(u_int32_t *)(&buf[26]));
	}

	return(0);
}

/*
 *	Sends set sound object play values.
 */
int YNetSendSetSoundObjectPlayValues(
	YConnection *con, YEventSoundPlay *value
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_flags> <u32_yid>
	 * <u32_pos> <u32_totalrepeats>
	 * <u16_volume_left> <u16_volume_right>
	 * <u16_volume_back_left> <u16_volume_back_right>
	 * <u32_sample_rate>
	 */
	const YDataLength this_seg_len = 8 + 8 + 8 + 4 + 4 + 4;
	char buf[this_seg_len];

#define COEFFICIENT_TO_BUF16(x)	(u_int16_t)( \
 (Coefficient)(x) * (Coefficient)((u_int16_t)-1) \
)
	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectPlayValues);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSoundObjectPlayValuesSet);

	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)value->flags);
	*(u_int32_t *)(&buf[12]) = htonl((u_int32_t)value->yid);

	*(u_int32_t *)(&buf[16]) = htonl((u_int32_t)value->position);
	*(u_int32_t *)(&buf[20]) = htonl((u_int32_t)value->total_repeats);

	*(u_int16_t *)(&buf[24]) = htons(COEFFICIENT_TO_BUF16(
	    value->left_volume
	));
	*(u_int16_t *)(&buf[26]) = htons(COEFFICIENT_TO_BUF16(
	    value->right_volume
	));
	*(u_int16_t *)(&buf[28]) = htons(COEFFICIENT_TO_BUF16(
	    value->back_left_volume
	));
	*(u_int16_t *)(&buf[30]) = htons(COEFFICIENT_TO_BUF16(
	    value->back_right_volume
	));
	*(u_int32_t *)(&buf[32]) = htonl((u_int32_t)value->sample_rate);
#undef COEFFICIENT_TO_BUF16

	return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *      Sends get sound object play values.
 */
int YNetSendGetSoundObjectPlayValues(YConnection *con, YID yid)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_yid>
	 */
	const YDataLength this_seg_len = 8 + 4;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSoundObjectPlayValues);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSoundObjectPlayValuesGet);

	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)yid);

	return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *	Parses a sound object play values.
 */
int YNetParseSoundObjectPlayValues(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YSoundObjectPlayValuesGet)
	{

	}
	else if(minor_op_code == YSoundObjectPlayValuesSet)
	{
	    /* Parse sound object play values set */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u32_flags> <u32_yid>
	     * <u32_position> <u32_length>
	     * <u32_repeats> <u32_total_repeats>
	     * <u16_volume_left> <u16_volume_right>
	     * <u16_volume_back_left> <u16_volume_back_right>
	     * <u32_sample_rate>
	     */
	    const YDataLength this_seg_len = 8 + 8 + 8 + 8 + 4 + 4 + 4;
	    YEventSoundPlay *play = &event->play;

	    if(chunk_length < this_seg_len)
		return(-1);

#define BUF_TO_COEFFICIENT(x)	( \
  (Coefficient)(x) / (Coefficient)((u_int16_t)-1) \
)
	    play->flags = (unsigned long)ntohl(*(u_int32_t *)(&buf[8]));
	    play->yid = (YID)ntohl(*(u_int32_t *)(&buf[12]));
	    play->position = (YDataPosition)ntohl(*(u_int32_t *)(&buf[16]));
	    play->length = (YDataLength)ntohl(*(u_int32_t *)(&buf[20]));
	    play->repeats = ntohl(*(u_int32_t *)(&buf[24]));
	    play->total_repeats = ntohl(*(u_int32_t *)(&buf[28]));
	    play->left_volume = BUF_TO_COEFFICIENT(
		ntohs(*(u_int16_t *)(&buf[32]))
	    );
	    play->right_volume = BUF_TO_COEFFICIENT(
		ntohs(*(u_int16_t *)(&buf[34]))
	    );
	    play->back_left_volume = BUF_TO_COEFFICIENT(
		ntohs(*(u_int16_t *)(&buf[36]))
	    );
	    play->back_right_volume = BUF_TO_COEFFICIENT(
		ntohs(*(u_int16_t *)(&buf[38]))
	    );
	    play->sample_rate = ntohl(*(u_int32_t *)(&buf[40]));
#undef BUF_TO_COEFFICIENT
	}

	return(0);
}

/*
 *	Sends a client message.
 */
int YNetSendClientMessage(
	YConnection *con,
	Boolean notify_self,
	int format,
	int type,
	const char *message,
	int length
)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_format> <u32_type>
	 * <u8_notify_self>
	 * <...message...>
	 */
	const YDataLength this_seg_len = 8 + 8 + 1 + YClientMessageMessageMax;
	YDataLength actual_seg_len = 8 + 8 + 1;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YClientMessage);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YClientMessageSend);

	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)format);
	*(u_int32_t *)(&buf[12]) = htonl((u_int32_t)type);
	*(u_int8_t *)(&buf[16]) = (u_int8_t)notify_self;

	if((message != NULL) && (length > 0))
	{
	    int len = MIN(length, YClientMessageMessageMax);
	    memcpy(&buf[17], message, len);
	    actual_seg_len += len;
	}

	/* Since this is a variable length segment we need to
	 * recalculate the segment length after the formatting
	 * above and update the segment header's chunk size.
	 */
	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)actual_seg_len);

	return(YSendData(con->fd, buf, actual_seg_len));
}
/*
 *      Parses a client message.
 */
int YNetParseClientMessage(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YClientMessageReceive)
	{
	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u32_format> <u32_type>
	     * <...message...>
	     */
	    const YDataLength base_seg_len = 8 + 8;
	    YEventClientMessage *clientmessage = &event->clientmessage; 
	    YDataLength len;
	    if(chunk_length < base_seg_len)
		return(-1);

	    clientmessage->format = ntohl(*(u_int32_t *)(&buf[8]));
	    clientmessage->type = ntohl(*(u_int32_t *)(&buf[12]));

	    clientmessage->message[0] = '\0';
	    clientmessage->message[YClientMessageMessageMax - 1] = '\0';
	    clientmessage->length = len = MIN(
		chunk_length - base_seg_len,
		YClientMessageMessageMax
	    );
	    if(len > 0)
		memcpy(
		    clientmessage->message,
		    &buf[16],
		    len
		);
	}

	return(0);
}

/*
 *	Sends a YSHM sound open.
 */
int YNetSendYSHMSoundOpen(YConnection *con)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 */
	const YDataLength this_seg_len = 8;

	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YSHMSound);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YSHMSoundOpen);

	return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *      Parses a YSHM sound open.
 */
int YNetParseYSHMSoundOpen(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YSHMSoundOpen)
	{
	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u32_shm_id>
	     */
	    const YDataLength this_seg_len = 8 + 4;
	    YEventYSHMSound *yshmsound = &event->yshmsound;

	    if(chunk_length < this_seg_len)
		return(-1);

	    yshmsound->minor_op_code = minor_op_code;
	    yshmsound->shm_id = ntohl(*(u_int32_t *)(&buf[8]));
	}
	else if(minor_op_code == YSHMSoundClose)
	{
	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u32_shm_id>
	     */
	    const YDataLength this_seg_len = 8 + 4;
	    YEventYSHMSound *yshmsound = &event->yshmsound;

	    if(chunk_length < this_seg_len)
		return(-1);

	    yshmsound->minor_op_code = minor_op_code;
	    yshmsound->shm_id = ntohl(*(u_int32_t *)(&buf[8]));
	}

	return(0);
}

/*
 *      Send audio CD play track.
 */
int YNetSendAudioCDPlayTrack(YConnection *con, int track_number)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 * <u32_track_number>
	 */
	const YDataLength this_seg_len = 8 + 4;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioCD);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioCDPlayTrack);

	*(u_int32_t *)(&buf[8]) = htonl((u_int32_t)track_number);

	return(YSendData(con->fd, buf, this_seg_len));
}

/*
 *	Send audio CD stop.
 */
int YNetSendAudioCDStop(YConnection *con)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 */
	const YDataLength this_seg_len = 8;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioCD);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioCDStop);

	return(YSendData(con->fd, buf, this_seg_len));
}

/*
 *	Send audio CD eject.
 */
int YNetSendAudioCDEject(YConnection *con)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 */
	const YDataLength this_seg_len = 8;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioCD);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioCDEject);

	return(YSendData(con->fd, buf, this_seg_len));
}

/*
 *	Parse audio CD (play track, stop, and eject).
 */
int YNetParseAudioCD(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YAudioCDStatsGet)
	{

	}
	else if(minor_op_code == YAudioCDStatsSet)
	{

	}
	else if(minor_op_code == YAudioCDPlayTrack)
	{
	    /* Parse audio CD play track */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u32_track_number>
	     */
	    const YDataLength base_seg_len = 8 + 4;
	    YEventAudioCDStats *acds = &event->audiocdstats;

	    if(chunk_length < base_seg_len)
		return(-1);

	    acds->minor_op_code = minor_op_code;
	    acds->track_number = ntohl(*(u_int32_t *)(&buf[8]));
	    acds->track_start_time = 0;
	    acds->track_length = 0;
	}
	else if(minor_op_code == YAudioCDStop)
	{
	    /* Parse audio CD stop */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     */
	    const YDataLength base_seg_len = 8;
	    YEventAudioCDStats *acds = &event->audiocdstats;

	    if(chunk_length < base_seg_len)
		return(-1);

	    acds->minor_op_code = minor_op_code;
	    acds->track_number = 0;
	    acds->track_start_time = 0;
	    acds->track_length = 0;
	}
	else if(minor_op_code == YAudioCDEject)
	{
	    /* Parse audio CD eject */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     */
	    const YDataLength base_seg_len = 8;
	    YEventAudioCDStats *acds = &event->audiocdstats;

	    if(chunk_length < base_seg_len)
		return(-1);

	    acds->minor_op_code = minor_op_code;
	    acds->track_number = 0;
	    acds->track_start_time = 0;
	    acds->track_length = 0;
	}

	return(0);
}

/*
 *	Send list audio CD tracks.
 */
int YNetSendListAudioCDTracks(YConnection *con)
{
	/* <u32_cs> <u16_majop> <u16_minop>
	 */
	const YDataLength this_seg_len = 8;
	char buf[this_seg_len];

	*(u_int32_t *)(&buf[0]) = htonl((u_int32_t)this_seg_len);
	*(u_int16_t *)(&buf[4]) = htons((u_int16_t)YAudioCDTracksList);
	*(u_int16_t *)(&buf[6]) = htons((u_int16_t)YAudioCDTracksListGet);

	return(YSendData(con->fd, buf, this_seg_len));
}
/*
 *      Parses list audio CD tracks.
 */
int YNetParseListAudioCDTracks(YCNP_STD_INPUTS_PROTO)
{
	if(minor_op_code == YAudioCDTracksListGet)
	{

	}
	else if(minor_op_code == YAudioCDTracksListSet)
	{
	    /* Parse list audio CD track set */

	    /* <u32_cs> <u16_majop> <u16_minop>
	     * <u32_track_number>
	     * <u64_track_start_time>
	     * <u64_track_length>
	     * <...track_name...>
	     */
	    const YDataLength base_seg_len = 8 + 4 + 8 + 8;
 	    YEventAudioCDStats *acds = &event->audiocdstats;
	    int len;

	    if(chunk_length < base_seg_len)
		return(-1);

	    acds->minor_op_code = minor_op_code;
	    acds->track_number = ntohl(*(u_int32_t *)(&buf[8]));
	    acds->track_start_time = ntohl(*(u_int64_t *)(&buf[12]));
	    acds->track_length = ntohl(*(u_int64_t *)(&buf[20]));

	    len = MIN(chunk_length - base_seg_len, YAudioCDTrackNameMax - 1);
	    if(len > 0)
		strncpy(
		    acds->track_name,
		    (const char *)(&buf[28]),
		    len
		);
	    acds->track_name[len] = '\0';
 	}

	return(0);
}

/*
 *	Parses the buffer buf recieved from the server, this function
 *	should be called from YNetRecv().
 */
static int YNetParse(  
	YConnection *con,
	YEvent *event,
	const u_int8_t *buf,
	u_int32_t chunk_length, 
	u_int16_t major_op_code,
	u_int16_t minor_op_code
)
{
	int status;

	event->type = major_op_code;

	switch(major_op_code)
	{
	  case YAudioChange:
	    status = YNetParseAudioChange(YCNP_STD_INPUTS);
	    break;

	  case YCycleChange:
	    status = YNetParseCycleChange(YCNP_STD_INPUTS);
	    break;

	  case YDisconnect:
	    status = YNetParseDisconnect(YCNP_STD_INPUTS);
	    break;

	  case YSetHost:
	    status = YNetParseSetHost(YCNP_STD_INPUTS);
	    break;

	  case YListHosts:
/* Work on this later */
	    break;

	  case YMixerChannel:
	    status = YNetParseMixerChannel(YCNP_STD_INPUTS);
	    break;

	  case YListMixers:
/* Work on this later */
	    break;

	  case YSoundObjectPlay:
	    status = YNetParseSoundPlay(YCNP_STD_INPUTS);
	    break;

	  case YSoundObjectKill:
	    status = YNetParseSoundKill(YCNP_STD_INPUTS);
	    break;

	  case YSoundObjectAttributes:
	    status = YNetParseSoundObjectAttributes(YCNP_STD_INPUTS);
	    break;

	  case YShutdown:
	    status = YNetParseShutdown(YCNP_STD_INPUTS);
	    break;

	  case YSync:
	    status = YNetParseSync(YCNP_STD_INPUTS);
	    break;

	  case YAudioStats:
	    status = YNetParseAudioStats(YCNP_STD_INPUTS);
	    break;

	  case YServerStats:
	    status = YNetParseServerStats(YCNP_STD_INPUTS);
	    break;

	  case YListAudioModes:
	    status = YNetParseListAudioModes(YCNP_STD_INPUTS);
	    break;

	  case YSoundObjectPlayValues:
	    status = YNetParseSoundObjectPlayValues(YCNP_STD_INPUTS);
	    break;

	  case YClientMessage:
	    status = YNetParseClientMessage(YCNP_STD_INPUTS);
	    break;

	  case YSHMSound:
	    status = YNetParseYSHMSoundOpen(YCNP_STD_INPUTS);
	    break;

	  case YAudioCD:
	    status = YNetParseAudioCD(YCNP_STD_INPUTS);
	    break;

	  case YAudioCDTracksList:
	    status = YNetParseListAudioCDTracks(YCNP_STD_INPUTS);
	    break;


	  default:
	    /* Unknown op code, so set event type to 0 */
	    event->type = 0;
	    break;
	}


	return(0);
}

/*
 *	This function is called by YGetNextEvent().
 *
 *	Returns the number of segments handled or -1 on fatal error.
 *	Fatal error implies the connection is no longer usable.
 *
 *	This function may close the connection and set the con->fd
 *	to -1 if a YDisconnect or YShutdown event is recieved.
 */
int YNetRecv(YConnection *con)
{
	/* Maximum events to fetch per recieve, further events are
	 * discarded.
	 */
	const int max_event_fetch_loops = 10000;

	int i, n, loops, status;
	struct timeval timeout;
	fd_set readfds;

	u_int8_t *buf_ptr;
	int bytes_read, segments_handled;

	u_int32_t chunk_length;
	u_int16_t major_op_code, minor_op_code;

	YEvent *event_ptr;

	if(con->buf == NULL)
	    return(-1);

	if(con->buf_cont < 0)
	    con->buf_cont = 0;

	/* Make sure that con->buf_len is greater than con->buf_cont */
	if((con->buf_len - con->buf_cont) <= 0)
	{
	    /* Buffer overflowed and unable to parse, so reset it
	     * and disconnect connection since there is no way to
	     * seek the next chunk position anymore.
	     */
	    con->buf_cont = 0;

	    fprintf(
		stderr,
 "YNetRecv(): Connection 0x%.8x: Contents overflowed buffer length %ld.\n",
		(u_int32_t)con,
		con->buf_len
	    );

	    /* Force disconnect */
	    if(con->fd > -1)
	    {
		YShutdownSocket(con->fd, SHUT_RDWR, 30l);
		YCloseSocketWait(con->fd, 30l);
		con->fd = -1;
	    }

	    return(-1);
	}

	/* Check if there are any data to recieve */
	timeout.tv_sec = 0;
	timeout.tv_usec = 0;
	FD_ZERO(&readfds);
	FD_SET(con->fd, &readfds);
	status = select(con->fd + 1, &readfds, NULL, NULL, &timeout);
	if(status == -1)
	    perror("select");

	if(!FD_ISSET(con->fd, &readfds))
	    return(0);

	buf_ptr = con->buf + con->buf_cont;
	bytes_read = recv(
	    con->fd,
	    buf_ptr,
	    con->buf_len - con->buf_cont,
	    MSG_NOSIGNAL
	);
	if(bytes_read == 0)
	{
	    /* Shutdown and close the socket */
	    if(con->fd > -1)
	    {
		YShutdownSocket(con->fd, SHUT_RDWR, 30l);
		close(con->fd);
		con->fd = -1;
	    }

	    return(-1);
	}
	else if(bytes_read < 0)
	{
	    /* Handle error */
	    const int error_code = errno;
	    switch(error_code)
	    {
	      /* These errors are okay, return 0 indicating no data
	       * read but also no error
	       */
	      case EWOULDBLOCK:
#if (EAGAIN != EWOULDBLOCK)
	      case EAGAIN:
#endif
	      case EINTR:
		return(0);
		break;

	      /* All other errors, force disconnect and return -1 */
	      default:
		/* Force disconnect */
		if(con->fd > -1)
		{
		    YShutdownSocket(con->fd, SHUT_RDWR, 30l);
		    YCloseSocketWait(con->fd, 30l);
		    con->fd = -1;
		}
		return(-1);
		break;
	    }
	}

	/* Increment byte count indicating contents in buffer */
	con->buf_cont += bytes_read;
	/* Sanitize buf_cont to the number of bytes allocated in the
	 * buffer. This will be checked on the next loop if they
	 * are equal and indicate an overflow if that happens.
	 */
	if(con->buf_cont > con->buf_len)
	    con->buf_cont = con->buf_len;


	/* Begin handling each segment, handle no more than
	 * max_event_fetch_loops from the recieved buffer.
	 */
	segments_handled = 0;
	for(loops = 0; loops < max_event_fetch_loops; loops++)
	{
	    /* Check if we did not get the first 8 bytes (needed for the
	     * chunk size, major op code, and minor op code).
	     */
	    if(con->buf_cont < 8)
		break;

	    /*   All chunks start off with the same format, that being:
	     *   <4 bytes, chunk length> <2 bytes, major op code>
	     *   <2 bytes, minor op code>
	     */
	    chunk_length = (u_int32_t)MAX(
		(long)ntohl(*((u_int32_t *)(&con->buf[0]))), (long)0
	    );
	    major_op_code = ntohs(*((u_int16_t *)(&con->buf[4])));
	    minor_op_code = ntohs(*((u_int16_t *)(&con->buf[6])));

	    /* Recieved data indicated chunk length less than 8 bytes? */
	    if(chunk_length < 8)
	    {
		/* This should never happen, so consider this a fatal
		 * error. Report it and force disconnect.
		 */
		YNetPrintError(
		    stderr,
		    con,
		    chunk_length,
		    major_op_code,
		    minor_op_code,
"Recieved a segment with header specified chunk length less than 8 bytes"
		);
		if(con->fd > -1)
		{
		    YShutdownSocket(con->fd, SHUT_RDWR, 30l);
		    YCloseSocketWait(con->fd, 30l);
		    con->fd = -1;
		}
		return(-1);
	    }

	    /* Did we not get the entire chunk? In other words does the
	     * buffer contents value suggest that we do not have the
	     * entire chunk?
	     */
	    if((YDataLength)chunk_length > (YDataLength)con->buf_cont)
		break;


	    /* Increment total number of queued events */
	    if(con->total_queued_events < 0)
		con->total_queued_events = 0;
	    con->total_queued_events++;
	    if(con->total_queued_events > YQueuedEventsMax)
	    {
		/* Queued events overflowed */

		con->total_queued_events = YQueuedEventsMax;

		/* Need to force close connection for this */
		if(con->fd > -1)
		{
                    YShutdownSocket(con->fd, SHUT_RDWR, 30l);
                    YCloseSocketWait(con->fd, 30l);
		    con->fd = -1;
		}

		fprintf(stderr,
 "YNetRecv(): Connection 0x%.8x: Limit of %i queued events exceeded.\n",
		    (u_int32_t)con,
		    YQueuedEventsMax
		);

		return(-1);
	    }
	    /* Allocate a new queued event */
	    con->queued_event = (YEvent *)realloc(
		con->queued_event,
		con->total_queued_events * sizeof(YEvent)
	    );
	    if(con->queued_event == NULL)
	    {
		/* Memory allocation error */

		con->total_queued_events = 0;

		/* Need to force close connection for this */
		if(con->fd > -1)
		{
                    YShutdownSocket(con->fd, SHUT_RDWR, 30l);
                    YCloseSocketWait(con->fd, 30l);
		    con->fd = -1;
		}

		return(-1);
	    }
	    /* Get pointer to newly allocated queued event segment */
	    event_ptr = &(con->queued_event[
		con->total_queued_events - 1
	    ]);


	    /* Parse recieved buffer segement and put data into event
	     * structure event_ptr.
	     */
	    YNetParse(
		con,
		event_ptr,
		con->buf,
		chunk_length,
		major_op_code,
		minor_op_code
	    );
	    segments_handled++;

	    /* Shift the buffer */
	    for(i = 0, n = chunk_length;
		n < con->buf_cont;
		i++, n++
	    )
		con->buf[i] = con->buf[n];

	    /* Decrease buffer contents */
	    con->buf_cont = (YDataLength)con->buf_cont -
		(YDataLength)chunk_length;
	    if(con->buf_cont < 0)
		con->buf_cont = 0;


	    /* Post event handling; check special case if the event we
	     * just parsed is either YDisconnect or YShutdown
	     */
	    if((event_ptr->type == YDisconnect) ||
	       (event_ptr->type == YShutdown)
	    )
	    {
		/* Need to force close connection when we recieve either
		 * of these events.
		 */
		if(con->fd > -1)
		{
                    YShutdownSocket(con->fd, SHUT_RDWR, 30l);
                    YCloseSocketWait(con->fd, 30l);
		    con->fd = -1;
		}
	    }
	}


	/* Return number of segments handled which implies number of
	 * events that were handled
	 */
	return(segments_handled);
}
