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

#define MIDIIO_PRIVATE
#include "midiio.h"

/* The name of the POSIX message queue used for sending midi events */
#define MIDIQ "/midiport"  

typedef struct {
  long mtype;
  unsigned char msg[4];
  struct timespec delay;
  int msglen;
} MidiIOEvent;

typedef struct {
  mqd_t midiq;
  int bpm;
  int ppqn;
  int nsecpertick;
} MidiHandle;      


void * MidiOpen() {
  MidiHandle * handle;

  handle = (MidiHandle *) malloc(sizeof(MidiHandle));
  
  if ((handle->midiq = mq_open(MIDIQ, O_WRONLY)) == (mqd_t) -1) {
    perror("msgq: ");
    return NULL;
  }
 
  return (void *) handle;  
}


void MidiWrite(void * voidhandle, unsigned char msg[4], int stamp, int msglen) {
  MidiHandle * handle = voidhandle;
  MidiIOEvent ev;

  memcpy(&ev.msg, msg, 4);

  MidiTimeToRealTime(voidhandle, stamp, &ev.delay);

  ev.msglen = msglen;

  if (mq_send(handle->midiq, (char *) &ev, sizeof(ev), 1) == (ssize_t) -1)
    printf("error sending message!\n");
}


void MidiPanic(void * voidhandle) {
  MidiHandle * handle = voidhandle;
  MidiIOEvent ev;

  ev.msg[0] = 0xFF;
  ev.msglen = 1;
  ev.delay.tv_sec = 0;
  ev.delay.tv_nsec = 0;

  if (mq_send(handle->midiq, (char *) &ev, sizeof(ev), 2) == (ssize_t) -1)
    printf("error sending message!\n");
}


void MidiClose(void * voidhandle) {
  MidiHandle * handle = voidhandle;
  mq_close(handle->midiq);  
}


void MidiTimeToRealTime(void * voidhandle, long stamp, struct timespec *t) {
  MidiHandle * handle = voidhandle;

#if 0
  t->tv_sec = 0;
  t->tv_nsec = stamp * handle->nsecpertick;
#else
  t->tv_sec = 0;
  t->tv_nsec = 0;
  { long i;
    for (i=0; i<stamp; i++) {
      t->tv_nsec += handle->nsecpertick;
      if (t->tv_nsec >= 1000000000) {
        t->tv_sec += 1;
        t->tv_nsec -= 1000000000;
      }
    }
  }
#endif

}

void MidiSetTempo(void * voidhandle, int bpm, int ppqn) {
  MidiHandle * handle = voidhandle;

  handle->bpm = bpm;
  handle->ppqn = ppqn;

  handle->nsecpertick = 6000000000 / (bpm * ppqn);
  handle->nsecpertick *= 10; 

  printf("Tempo Change - bpm: %d PPQN: %d nsecpertick: %d\n", bpm, ppqn, 
    handle->nsecpertick);
}

