#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/stat.h>

#ifndef INADDR_NONE
#define INADDR_NONE -1
#endif

#include "tuner.h"

static do_stationcall();

char *dft_nets[] = { DFT_NETS, 0 };

char radio_host[64];		/* Host where radio runs */

int transmitter_port = CTLPRT_XMIT;	/* Default control port for transmitters */
int radio_port = CTLPRT_RADIO;		/* Default control port for radios */
struct sockaddr_in radio_addr;	/* Address of our radio */
struct sockaddr_in bcast_addrs[MAXNET];	/* Addresses of transmitters */
int num_bcast_addrs = 0;

struct station_info {
    char name[MAXNAME];		/* station name */
    int port;			/* UDP broadcast port */
    int is_playing;		/* 1 if currently transmitting */
    char logfile[MAXNAME];	/* Name of cdlog file */
    int since;			/* Play time in minutes of current CD */
    char cdname[MAXNAME];	/* Current CD name */
};

struct station_info stations[MAXSTATION];

char *station_names[MAXSTATION];	/* Hack. Points into stations array */
int num_stations;

char cur_name[MAXNAME];
char cur_play[MAXNAME];
char cur_time[MAXNAME];
int cur_mute;
int cur_port;
int origtune = -1;

int sock;			/* Our socket */
int sc_sock;			/* Stationcall socket */

char plbuf[MAXPLBUF][MAXNAME];
char *playlist[MAXPLBUF];
int num_playlist;

init_io() {
    struct hostent *hent;
    int i;

    for( i=0; i<MAXSTATION; i++)
      station_names[i] = stations[i].name;
    /*XXX Process args */
    sock = opensock();
    sc_sock = openscsock();

    /*
    ** Divine address struct for radio station we're controlling.
    */
    if ( radio_host[0] == 0 ) {
	if ( gethostname(radio_host, sizeof(radio_host)) < 0 ) {
	    perror("gethostname");
	    exit(1);
	}
    }
    if ( (hent=gethostbyname(radio_host)) == 0 ) {
#ifndef sun
	herror(radio_host);
#else
	fprintf(stderr, "%s: Unknown host name\n", radio_host);
#endif
	exit(1);
    }
    radio_addr.sin_family = AF_INET;
    radio_addr.sin_port = htons(radio_port);
    memcpy(&radio_addr.sin_addr, hent->h_addr, hent->h_length);
    /*
    ** Create list of broadcast addresses for locating stations.
    */
    if ( num_bcast_addrs == 0 ) {
	/*
	** No addresses in arguments. Do the defaults.
	*/
	char **p;

	for( p=dft_nets; *p; p++ )
	  add_bcast_addr(*p);
    }
    /*
    ** Obtain station info and current station.
    */
    get_stations();
    get_curinfo();
    origstation = cur_tune;
    show_tuner(station_names, num_stations);
    show_tune(cur_tune, cur_name, cur_play, cur_time, cur_mute);
    show_playlist(playlist, num_playlist,1);
    watch_socket(sc_sock, do_stationcall);
}

get_stations() {
    int i;
    char *s;
    struct station_info *p;

    /*
    ** Clear old stations.
    */
    num_stations = 0;
    /*
    ** Broadcast info request.
    */
    for(i=0; i<num_bcast_addrs; i++)
      sendsock(sock, &bcast_addrs[i], "radio:s");
    /*
    ** Receive replies.
    */
    while ( (s=recvsock(sock)) ) {
	p = stations + num_stations;
	if ( parse_stationcall(p, s) )
	  num_stations++;
    }
}

static
do_stationcall(d1, d2, d3)
    int d1, d2, d3;
{
    char *s;
    struct station_info sistr;
    int i;

    if ( (s=recvsock(sc_sock)) == NULL ) {
	fprintf(stderr, "tuner: stationcall event without data?\n");
	return;
    }
    if ( !parse_stationcall(&sistr, s) )
      return;
    for ( i=0; i<num_stations; i++)
      if ( stations[i].port == sistr.port ) {
	  stations[i] = sistr;
	  if ( sistr.port == cur_port ) {
	      if( get_curinfo() )
		show_playlist(playlist, num_playlist,1);
	      show_tune(cur_tune, cur_name, cur_play, cur_time, cur_mute);
	  }
	  return;
      }
    stations[num_stations] = sistr;
    num_stations++;
    show_tuner(station_names, num_stations);
}
    
parse_stationcall(p, s)
    struct station_info *p;
    char *s;
{
    int port;
    char name[MAXNAME], logname[MAXNAME], cdname[MAXNAME];
    int is_playing, since;
    int i;

    is_playing = 1;
    since = -1;
    cdname[0] = '\0';
    logname[0] = '\0';
    i = sscanf(s, "radio:S:%[^:]:%d:%d:%[^:]:%d:%[^\n]", name, &port,
	       &is_playing, logname, &since, cdname);
    if ( i >= 2 ) {
	strncpy(p->name, name, MAXNAME);
	p->port = port;
	p->is_playing = is_playing;
	strncpy(p->logfile, logname, MAXNAME);
	p->since = since;
	strncpy(p->cdname, cdname, MAXNAME);
	return 1;
    }
    fprintf(stderr, "Ill-formatted station call(%d): '%s'\n",i,  s);
    return 0;
}

add_bcast_addr(addr)
    char *addr;
{
    struct sockaddr_in *sp;
    unsigned long iaddr;

    sp = &bcast_addrs[num_bcast_addrs++];
    if ( (iaddr=inet_addr(addr)) == INADDR_NONE) {
	fprintf(stderr, "Ill-formatted address %s\n", addr);
	exit(1);
    }
	
    sp->sin_family = AF_INET;
    sp->sin_addr.s_addr = iaddr;
    sp->sin_port = htons(transmitter_port);
}

get_curinfo() {
    char *s;
    int n;
    int port, muteoff;
    int i;
    FILE *fp;
    char buf[128];
    int t;

    sendsock(sock, &radio_addr, "radio:i");
    num_playlist = 0;
    if ( s = recvsock(sock) ) {
	/*
	** Parsing is funny, because older radio programs didn't have the
	** mute feature (so only send a single number, the port).
	*/
	n = sscanf(s, "radio:I:%d:%d", &muteoff, &port);
	if ( n == 0 ) {
	    fprintf(stderr, "Funny reply from radio program: %s\n", s);
	    return 1;
	}
	if ( n == 1 ) {
	    port = muteoff;
	    muteoff = 1;
	}
	cur_mute = !muteoff;
	cur_port = port;
	/*
	** Try to find the corresponding station
	*/
	for(i=0; i<num_stations; i++) {
	    if ( cur_port == stations[i].port ) {
		cur_tune = i+1;
		strcpy(cur_name, stations[i].name);
		strcpy(cur_play, stations[i].cdname);
		t = stations[i].since / 60;
		if ( t == 0 )
		  cur_time[0] = '\0';
		else if ( t < 60 )
		  sprintf(cur_time, "%d minutes", t);
		else
		  sprintf(cur_time, "%d hours", t/60);
		if ( !stations[i].is_playing )
		  strcat(cur_time, "(Silent)");
		if ( stations[i].logfile[0] )
		  return get_playlist(stations[i].logfile);
		return 1;
	    }
	}
	if ( i == num_stations ) {
	    /* Couldn't find the station number */
	    cur_tune = 0;
	    sprintf(cur_name, "Unknown (port %d)", cur_port);
	    strcpy(cur_play, "?");
	    strcpy(cur_time, "?");
	    return 1;
	}
    }
    fprintf(stderr, "Warning: radio program not responding\n");
    strcpy(cur_name, "?");
    strcpy(cur_time, "?");
    strcpy(cur_play, "?");
    cur_tune = 0;
    return 1;
}

void
do_curinfo()
{
    get_curinfo();
    show_tune(cur_tune, cur_name, cur_play, cur_time, cur_mute);
}

void
do_tune(station)
    int station;
{
    char buf[32];

    if ( station == 0 ) return;
    sprintf(buf, "radio:t:%d", stations[station-1].port);
    sendsock(sock, &radio_addr, buf);
    get_curinfo();
    show_tune(cur_tune, cur_name, cur_play, cur_time, cur_mute);
    show_playlist(playlist, num_playlist,1);
}

/*
** do_mute - Mute radio
*/
void
do_mute(onoff)
    int onoff;
{
    sendsock(sock, &radio_addr, onoff?"radio:0":"radio:1");
}

/*
** get last MAXPLBUF entries of playlist file.
*/
get_playlist(file)
    char *file;
{
    FILE *fp;
    int i, j;
    static char fnbuf[MAXNAME];
    struct stat sb;
    static int mtime, saved_num_playlist;

    if ( strncmp(file, fnbuf, MAXNAME) == 0 ) {
	if ( stat(file, &sb) >= 0 && sb.st_mtime == mtime ) {
	    /* Yes, cached copy still ok */
	    num_playlist = saved_num_playlist;
	    return 0;
	}
    }

    if ( (fp=fopen(file, "r")) == 0 )
      return;

    strncpy(fnbuf, file, MAXNAME);
    stat(file, &sb);
    mtime = sb.st_mtime;

    i = 0;
    while( (fgets(plbuf[i%MAXPLBUF], MAXNAME, fp)) ) i++;
    if ( i == 0 ) {
	num_playlist = 0;
	saved_num_playlist = 0;
	return;
    }
    i--;

    if ( i < MAXPLBUF ) {
	num_playlist = i+1;
	while ( i >= 0 ) {
	    playlist[i] = plbuf[i];
	    i--;
	}
    }
    else {
	for ( j=0; j<MAXPLBUF; j++) playlist[j] = plbuf[(i+j+1) % MAXPLBUF];
	num_playlist = MAXPLBUF;
    }
    saved_num_playlist = num_playlist;
    fclose(fp);
    return 1;
}

/*
** print_stations prints the names of all stations.
*/
void
print_stations()
{
    int i;

    for(i=0; i<num_stations; i++) {
	printf("%s\n", stations[i].name);
    }
}

/*
** print_curstation prints info on the current station.
*/
void
print_curstation()
{
    printf("%s%s, playing %s since %s\n", cur_name, cur_mute?" (mute)":"",
	  cur_play, cur_time);
}

/*
** do_tune_by_name tunes to the station with the given name.
*/
int
do_tune_by_name(name)
    char *name;
{
    int i;
    
    for( i=0; i<num_stations; i++ )
      if (strcmp(name, station_names[i]) == 0 ) {
	  do_tune(i+1);
	  return 1;
      }
    return 0;
}
