/* **  jazz - a midi sequencer for Linux
**
**  Copyright (C) 1994 Andreas Voss (andreas@avix.rhein-neckar.de)
**
**  Portions Copyright (C) 1995 Per Sigmond (Per.Sigmond@hia.no)
**
**  This program is free software; you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation; either version 2 of the License, or
**  (at your option) any later version.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with this program; if not, write to the Free Software
**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/* midinet.c using RPC and TCP protocol - Written by Per Sigmond, HiA			*/

#include <stdio.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#ifdef sun
#include <sys/filio.h>
#endif
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#ifndef VMS
#include <rpc/rpc.h>
#endif
#if defined(sun) && defined(__svr4__)
#include <rpc/clnt_soc.h>
#endif

#include "midi_p_clnt.c"
#include "midinet.h"

/* non_block_io():
   Input parameters:
   s: socket
   on: TRUE gives non-blocking socket, FALSE gives blocking socket
*/
void non_block_io( int s, int on ) {
	if ( ioctl( s, FIONBIO, &on ) < 0 ) {
		perror("ioctl");
		close(s);
		exit(1);
	}
	return;
}

static CLIENT *client = NULL;
static int tcp_sd = -1;

/* midinetconnect():						*/
/* Input parameters:                                            */
/* host - name of remote machine                                */
/* service - service name of corresponding TCP server program       */
/* Returns: descriptor of open active connected tcp socket	*/
/* Also initializes an RPC CLIENT structure */

int midinetconnect(char *hostname, char *service)
{
struct sockaddr_in remote_tcp_addr;
struct hostent *host;
struct protoent *pent;
struct servent *sent;
int i;

/* Set address family */
remote_tcp_addr.sin_family = AF_INET;

/* Get remote IP-address from symbolic hostname */
host = gethostbyname(hostname);
if(host != NULL){
	/* Copy remote RPC address-structure */
	memcpy( &remote_tcp_addr.sin_addr, host->h_addr, host->h_length);
}
else {
	/* Try decimal dotted address syntax */
	remote_tcp_addr.sin_addr.s_addr = inet_addr( hostname );
	if ( remote_tcp_addr.sin_addr.s_addr == -1 ) {
		/* That didn't work either... */
	 	fprintf(stderr,"Uknown host %s\n",hostname);
	   	exit(2);
	}
}

/* Get port number from symbolic service name */
sent = getservbyname(service,"tcp");
if (sent != NULL) {
	remote_tcp_addr.sin_port = sent->s_port;
}
else {
	/* Try numeric port syntax */
	remote_tcp_addr.sin_port = htons( (u_short) atoi( service ) );
	if ( remote_tcp_addr.sin_port == 0 ) {
		/* That didn't work either... */
		perror("unknown service");
		return(-1);
	}
}


/* Open RPC socket */
tcp_sd = socket( AF_INET, SOCK_STREAM, 0 );
if ( tcp_sd < 0 ) {
	perror("socket ");
     	exit(1);
}

/* Connect to remote RPC host */
if( connect(tcp_sd, (struct sockaddr*) &remote_tcp_addr,sizeof(remote_tcp_addr)) < 0){
	perror("connect tcp");
	close(tcp_sd);
     	return(-1);
}

/* Initialize client structure */
for (i = 0; i < 5; i++) {
	client = clnt_create( hostname, MIDINET_PROG, MIDINET_VERS, "tcp" );
	if (client) break;
	sleep(1);
}
if (!client) {
	perror("clnttcp_create");
	exit(1);
}

non_block_io( tcp_sd, 0 );

return(tcp_sd);
}

int write_ack_mpu( const void *buf, int len ) {

	midi_buf mbuf;
	int *resptr = NULL;
	int res = 0;

	mbuf.midi_buf_val = (char*) buf;
	mbuf.midi_buf_len = len;

	TIMEOUT.tv_sec = 25;
	TIMEOUT.tv_usec = 0;

	resptr = mpu_write_ack_1( &mbuf, client );
	if (!resptr) {
		perror("write_ack_mpu");
		exit(1);
	}
	res = *resptr;
	return( res );
}

int write_noack_mpu( const void *buf, int len ) {

	midi_buf mbuf;
	void *resptr;

	mbuf.midi_buf_val = (char*) buf;
	mbuf.midi_buf_len = len;

	TIMEOUT.tv_sec = 0;
	TIMEOUT.tv_usec = 0;

	resptr = mpu_write_noack_1( &mbuf, client );

	return( mbuf.midi_buf_len );
}

int get_recbuf_mpu( unsigned char **bufptr ) {

/* Command-bytes to communicate with midinetd reader-process */
#define STOP_PLAY_COMMAND 0xfb
#define START_OF_RECORD_BUFFER 0xfb

	volatile int ii;
	unsigned char cmd = STOP_PLAY_COMMAND; /* can be any value */
	int numbytes = 0;
	int i, j;
	unsigned char *iptr;
	unsigned char ch;


	/* Write command and tell midinetd to send record buffer */
	if ( write( tcp_sd, &cmd, 1 ) != 1 ) {
		perror("get_recbuf_mpu write socket");
		exit(1);
	}

	/* Make sure we are in blocking mode */
	non_block_io( tcp_sd, 0 );

	/* Wait for start mark (START_OF_RECORD_BUFFER) */
	ch = 0;
	for ( ii = 0; ii < 1000; ii++ ) {
		read( tcp_sd, &ch, 1 );
		if (ch == START_OF_RECORD_BUFFER)
			break;
	}
	if ( ch != START_OF_RECORD_BUFFER ) {
		/* Got to have that start mark */
		printf("get_recbuf_mpu: Did not receive START_OF_RECORD_BUFFER\n");
		return(0);
	}

	/* Read number of bytes in record buffer */
	i = 0;
	iptr = (unsigned char*) &numbytes;
	while( i < sizeof(int) ) {
		if (read( tcp_sd, iptr, 1) == 1) {
			i++;
			iptr++;
		}
	}
	numbytes = ntohl( numbytes );

	if (numbytes == 0) {
		/* No bytes in record buffer */
		return 0;
	}

	/* Allocate and read record buffer */
	*bufptr = (unsigned char*) malloc( numbytes );
	i = 0;
	while ( 1 ) {
		j = read( tcp_sd, (*bufptr) + i, numbytes - i );
		if (j < 0) j = 0;
		i = i + j;
		if (i >= numbytes) break;
	}

	return( numbytes );
}
