#define DEB(stmt)	/* stmt	*/	/* For debug printing */
#define DEB2(stmt)	/* stmt */		/* For debug printing */
#define DEB3(stmt)	stmt 		/* For debug printing */
#define DEB_ALWAYS(stmt)	stmt	/* Interesting things */

#define _INCLUDE_POSIX_SOURCE 1
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/errno.h>
#include <sys/soundcard.h>

#include "mlib.h"

SEQ_DEFINEBUF(1024);
SEQ_PM_DEFINES;

int seqfd;
mlib_track *tracks[1024];
int ntrks = 0;
int dev = 3;

void
seqbuf_dump ()
{
  if (_seqbufptr)
    if (write (seqfd, _seqbuf, _seqbufptr) == -1)
      {
	perror ("write /dev/sequencer2");
	exit (-1);
      }
  _seqbufptr = 0;
}

void
player()
{
		int i, track;
		int prev_time = 0;
	
		int ptrs[1024] = {0};
		unsigned char *ptr;
		unsigned char *event;
		int time;

#if 1
		for (track = 0;track<ntrks;track++)
		for (i=0;i<128;i++)
		{
			if (tracks[track]->pgm_map[i] != -1)
			  {
			    if (PM_LOAD_PATCH(dev, 0, i)>=0)
			    {
			       tracks[track]->pgm_map[i] =  
			         _pm_info.data.data8[1];
			       printf("PGM: %d -> %d\n", i,
			       		tracks[track]->pgm_map[i]);
			    } 
			  else
			    {
			       if (errno == ENOSPC)
			       	{
					perror("/dev/sequencer");
					exit(1);
				}
			       tracks[track]->pgm_map[i] = i;
			    }
			  }

		      if (tracks[track]->pgm_map[i] == -1)
			       tracks[track]->pgm_map[i] = i; 
		}

		SEQ_START_TIMER();
		while (1)
		{
			int best = -1, best_time = 0x7fffffff;

			for (i=0;i<ntrks;i++)
			if (ptrs[i] < tracks[i]->len)
			  {
				ptr = &(tracks[i]->events[ptrs[i]*12]);
				event = &ptr[4];
				time = *(int*)ptr;

				if (time < best_time)
				{
					best = i;
					best_time = time;
				}
			  }

			if (best == -1) return;

			ptr = &(tracks[best]->events[ptrs[best]*12]);
			event = &ptr[4];
			time = *(int*)ptr;
			ptrs[best]++;

			if (event[0] < 128)
			  {
			  }
			else
			  {
			        int j;

			  	if (time > prev_time)
			  	  {
			  	  	SEQ_WAIT_TIME(time);
			  	  	prev_time = time;
			  	  }

			  	 if ((event[0] & 0xf0) == 0x90)
			  	  {
			  	    event[1] = dev;

			  	    if (event[0] == EV_CHN_COMMON &&
			  	       event[2] == MIDI_PGM_CHANGE)
			  	      {
			  	      	event[4] = tracks[best]->pgm_map[event[4]];
			  	      }
			  	  }

			  	 _SEQ_NEEDBUF(8);
			  	 memcpy(&_seqbuf[_seqbufptr], event, 8);
			  	 _SEQ_ADVBUF(8);

			  }
		}

#endif
}

int main(int argc, char *argv[])
{
	mlib_desc *mdesc;
	int was_last;
	int tmp, argp=1;

	if (argc < 2)
	{
		fprintf(stderr, "Usage: %s [options] midifile\n", argv[0]);
		exit(-1);
	}

	if (isdigit(argv[argp][0]))
		dev = atoi(argv[argp++]);

	if ((seqfd=open("/dev/music", O_RDWR, 0))==-1)
	  {
	  	perror("/dev/music");
	  	exit(-1);
	  }

	tmp=dev;
	ioctl(seqfd, SNDCTL_FM_4OP_ENABLE, &tmp);

	tmp=dev;
	ioctl(seqfd, SNDCTL_SEQ_RESETSAMPLES, &tmp);

	if ((mdesc = mlib_open(argv[argp]))==NULL)
	{
		fprintf(stderr, "Can't open MIDI file %s: %s\n",
			argv[argp], mlib_errmsg());
		exit(-1);
	}

	tmp = mdesc->hdr.division;
	if (ioctl(seqfd, SNDCTL_TMR_TIMEBASE, &tmp)==-1)
	{
		perror("Set timebase");
		exit(-1);
	}

	tmp = 100;
	if (ioctl(seqfd, SNDCTL_TMR_TEMPO, &tmp)==-1)
	{
		perror("Set tempo");
		exit(-1);
	}

	ntrks = 0;

	while ((tracks[ntrks]=mlib_loadtrack(mdesc, &was_last))!=NULL)
	{
		int i;

		DEB2(printf("Loaded track %03d: len = %d events, flags = %08x\n",
			mdesc->curr_trk, tracks[ntrks]->len, tracks[ntrks]->flags));
		ntrks++;
	}

	if (!was_last)
	{
	   fprintf(stderr, "%s: %s\n", argv[1], mlib_errmsg());
	   exit(-1);
	}

	tmp = (int)mdesc->timesig;
	/* printf("Setting timesig to %08x\n", tmp); */
	if (ioctl(seqfd, SNDCTL_TMR_METRONOME, &tmp)==-1)
	{
/*		perror("Set metronome"); */
	}

	player();
	SEQ_DELTA_TIME(
		mdesc->hdr.division*4);
	SEQ_DUMPBUF();

	mlib_close(mdesc);
	exit(0);
}
