
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <signal.h>

#include "miditypes.h"
#include "seq.h"
#include "midiapi.h"

static int interruptsong = 0;

void signal_handler(int sig) {
  switch(sig) {
    case SIGINT: {
      char string[1024];
      printf("Please select (a)bort all (n)next song or enter to continue\n");
      scanf("%c", string);
      switch(string[0]) {
        case 'a':
        case 'A':
          printf("Aborting all songs.\n");
          interruptsong = 2;
          break;

        case 'n':
        case 'N':
          printf("Skipping to next song...\n");
          interruptsong = 1;
          break;
      }
    }
    break;
  }


 if (signal(sig, SIG_IGN) != SIG_IGN)
    signal(sig, signal_handler);

}

void signal_setup(void) {
  signal(SIGINT,   signal_handler);
}


void playsequence(MidiSequence * seq) {
  int i;
  int activetracks;
  void * voidhandle;

  voidhandle = MidiOpen();
  MidiPanic(voidhandle); /* all notes off, center all controllers */

  interruptsong = 0;
  signal_setup();

  MidiSetTempo(voidhandle, seq->timebase.tempo, seq->timebase.ppqn);
 
  /* setup tracks for first events */
  activetracks = seq->numtracks;
  for (i=0; i<seq->numtracks; i++) {
    if (seq->track[i].evlist == NULL) {
      seq->track[i].enabled = 0;
    }
    else {
      seq->track[i].enabled = 1;
      seq->track[i].portnum = 0;
      seq->track[i].cur = *(seq->track[i].evlist);
    }
  }

  while (activetracks > 0 && interruptsong == 0) {
    int besttrack = 0;
    long besttime = 0x7fffffff;

    /* choose soonest event, and play from that track */
    for (i=0; i<seq->numtracks; i++) {
      if (seq->track[i].enabled) {
        if (seq->track[i].cur.stamp < besttime) {
          besttime = seq->track[i].cur.stamp;
          besttrack = i;
        } 
      }
    }  

    playevent(voidhandle, besttrack, seq);

    /* Recalculate the number of active tracks */
    activetracks = 0;
    for (i=0; i<seq->numtracks; i++) {
      if (seq->track[i].enabled)
        activetracks++;
    }
  }

  MidiPanic(voidhandle); /* all notes off, center all controllers */
  MidiClose(voidhandle);

  if (interruptsong == 2)
    exit(-1); /* quit the whole program */
}


void playevent(void * voidhandle, int track, MidiSequence * seq) {
  int i;
  long stamp;

  stamp = seq->track[track].cur.stamp;
 
  /* output event to MIDI interface, or handle seq event */
  if (seq->track[track].cur.msglen)
    midiout(voidhandle, &seq->track[track].cur);
  else 
    seqevent(voidhandle, &seq->track[track].cur, seq);   
 
  for (i=0; i<seq->numtracks; i++) {
    if (seq->track[i].enabled) 
      seq->track[i].cur.stamp -= stamp;
  } 

  if (seq->track[track].cur.next != NULL) 
    seq->track[track].cur = *seq->track[track].cur.next; 
  else 
    seq->track[track].enabled = 0;
}


void midiprintout(MidiEvent * ev) {
  int i;

  printf("Time: %d ", (int) ev->stamp);
  printf("Event: %02x ", ev->msg[0]);
  if (ev->msglen > 0) {
    printf("data: ");  
    for (i=1; i<ev->msglen; i++) {
      printf("%02x ", ev->msg[i]);  
    }
  } 
  printf("\n");
}


void midiout(void * voidhandle, MidiEvent * ev) {
  MidiWrite(voidhandle, 0, ev->msg, ev->stamp, ev->msglen);
}


void seqevent(void * voidhandle, MidiEvent * ev, MidiSequence * seq) {
  MidiEvent space;

  space.msg[0] = 0xf8;
  space.msglen = 1;
  space.stamp = ev->stamp;
  midiout(voidhandle, &space);  

  switch (ev->seqevent) {
    case 0x51: /* Tempo */
      { unsigned int tempo;
        tempo = (ev->seqdata[0] << 16) |
                (ev->seqdata[1] << 8) | 
                ev->seqdata[2];

#ifdef DEBUG
        printf("Tempo change: %d  ", tempo);
#endif 

        tempo = 60000000 / tempo;

#ifdef DEBUG
        printf(" bpm: %d\n", tempo);
#endif

        MidiSetTempo(voidhandle, tempo, seq->timebase.ppqn);
      }
      return;
    default: 
      break;
  }
}

