/*
 *  player.c - Player process handling.
 *
 *  (C) 1994 Mikael Nordqvist (d91mn@efd.lth.se, mech@df.lth.se)
 */

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <sys/ultrasound.h>
#include <limits.h>
#include <sys/time.h>

#include "mod.h"
#include "message.h"

SEQ_DECLAREBUF();
extern int seqfd, gus_dev;
extern struct mod_info M;
extern struct options opt;

extern int player2main_synced[2];
extern int main2player[2];

extern int songpos, linepos, speed, tempo;
extern char song_end;
extern double mod_time;

/* Used when keypresses changes pos */
static unsigned char song_jump, song_jump_pos, song_jump_speed,
                     song_jump_tempo;
static char aborting;

void play_module(void)
{
    info("Hinote: %d Lonote: %d\n", opt.high_note, opt.low_note);
    info("Playing module '%s'", M.name);
    
    if(opt.start_pos >= M.songlength)
	opt.start_pos=0;
    
    /* Make sure everything gets correctly initialized on the 1st iteration */
    total_reset(opt.speed, opt.tempo, opt.start_pos);
    aborting=0;

    while(!aborting) {
	if(song_jump) {
	    total_reset(song_jump_speed, song_jump_tempo, song_jump_pos);
	    song_jump=0;
	    drain_pipes(1); /* Drain all pipes */
	    send_ack();     /* Tell main we are ready to restart playing */
	}

	play_tick();
	handle_IO(); /* Flush events and check for messages */
    }
}


void handle_IO(void)
{
    struct mod_message mess;
    fd_set rset, wset;
    int nr, maxfd;
    char idling;

    maxfd=MAX(seqfd, main2player[PIPE_READ])+1;
    idling=0;

    do {
	FD_ZERO(&rset);
	FD_ZERO(&wset);
	if(_seqbufptr)
	    FD_SET(seqfd, &wset);
	FD_SET(main2player[PIPE_READ], &rset);
	if((nr=select(maxfd, &rset, &wset, 0, 0)) > 0) {
	    /* Write what we can */
	    if(FD_ISSET(seqfd, &wset)) {
		while(_seqbufptr) {
		    if((nr=write(seqfd, _seqbuf, _seqbufptr)) == -1) {
			if(errno == EAGAIN)
			    break; /* Queue full */
			else
			    error("write(seqfd) failed (errno=%d)\n", errno);
		    }
		    
		    if(nr < _seqbufptr) {
			memmove(_seqbuf, &(_seqbuf[nr]),
				_seqbufptr-nr);
			_seqbufptr-=nr;
		    }
		    else
			_seqbufptr=0;
		}
	    }
	    
	    /* Check for messages */
	    if(FD_ISSET(main2player[PIPE_READ], &rset)) {
		safe_read(main2player[PIPE_READ], &mess, sizeof(mess));
		switch(mess.type) {
		  case MESSAGE_STOP:
		    idling=1;
		    total_reset(6, 125, 0);
		    song_jump=0;
		    drain_pipes(1);
		    send_ack();
		    break;
		  case MESSAGE_EXIT:
		    aborting=1;
		    idling=song_end=_seqbufptr=0;
		    break;
		  case MESSAGE_JUMPPATTERN:
		    song_jump=1;
		    song_jump_pos=mess.a1.songpos;
		    safe_read(main2player[PIPE_READ], &mess, sizeof(mess));
		    safe_read(main2player[PIPE_READ],
			      (struct mod_message *)&opt.active_voices,
			      sizeof(opt.active_voices));
		    song_jump_speed=mess.a1.speed;
		    song_jump_tempo=mess.a2.tempo;
		    idling=song_end=_seqbufptr=0;
		    break;
		  case MESSAGE_RESTART:
		    safe_read(main2player[PIPE_READ],
			      (struct mod_message *)&opt.active_voices,
			      sizeof(opt.active_voices));
		    song_jump=1;
		    song_jump_pos=opt.start_pos;
		    song_jump_speed=opt.speed;
		    song_jump_tempo=opt.tempo;
		    idling=song_end=_seqbufptr=0;
		    break;
		  case MESSAGE_NOP:
		    debug("Player got NOP-message\n");
		    break;
		  default:
		    error("Unknown message rcvd by player.\n");
		}
	    }
	}
    }
    while(_seqbufptr || song_end || idling);
}


void send_line(void)
{
    struct mod_message mess;
    
    mess.type=MESSAGE_PRINTLINE;
    mess.a1.songpos=songpos;
    mess.a2.line=linepos;
    safe_write(player2main_synced[PIPE_WRITE], &mess, sizeof(mess));
    SEQ_ECHO_BACK(MESSAGE_PRINTLINE);
}

void send_speed(void)
{
    struct mod_message mess;
    
    mess.type=MESSAGE_PRINTSPEED;
    mess.a1.speed=speed;
    mess.a2.tempo=tempo;
    safe_write(player2main_synced[PIPE_WRITE], &mess, sizeof(mess));
    SEQ_ECHO_BACK(MESSAGE_PRINTSPEED);
}
