/*
  midi_io.c
  9/7/22

  MIDI input/output interface.

*/

#include "wimp.h"
#include "lib.h"
#include "main.h"

#define HEIGHT 112 // sprite is 128 high with 16 pixels for legend

#define GREY    44
#define LILAC  187

static int activity[16];

/*
 * update_activity
 * ---------------
 * Runs every 100ms. Displays the number of events per channel
 * since last time.
 */
static void update_activity(void)
{
  int i;
  static unsigned char prev[16];

  for(i=0; i<16; i++)
  {
    int x0 = (i * 16) + 1;
    int x1 = x0 + 14;
    int y0 = HEIGHT;
    int y1 = y0 - (activity[i] * 4);
    activity[i] = 0;
    if(y1 < 0)
      y1 = 0;
    rectangle(ro.display, x0, y0, x1, y1, LILAC);
    if(y1 > prev[i])
      rectangle(ro.display, x0, y1, x1, prev[i], GREY);
    prev[i] = y1;
  }
  icon_state_change(0, ro.win_data[WIN_MAIN].win_handle, ICON_DISPLAY);
}


/*
 * midi_rx_command
 * ---------------
 * Called for every received midi event. Increments an activity count
 * for each channel for all channel voice messages.
 */
static void midi_rx_command(unsigned int cmd)
{
  int status = cmd & 0xf0;

  if((status >= 0x80) && (status <= 0xe0))
    activity[cmd & 0xf]++;
}


/*
  MIDI Support Interface
  ----------------------
*/

// MIDISupport SWI numbers
#define MIDISupport_RemoveDriver    0x4EE81
#define MIDISupport_CreateDriver    0x4EE83
#define MIDISupport_Send            0x4EE84
#define MIDISupport_Receive         0x4EE85

// Driver flags
#define CAN_SEND             1
#define CAN_RECEIVE          2
#define REQUIRES_BUFFERING   4
#define CAN_RECEIVE_COMMAND  8

static int midi_support_handle;

/*
 * create_driver
 * -------------
 * Instructs Midi Support to create a driver for us.
 */
void create_driver(void)
{
  _kernel_swi_regs regs;

  regs.r[0] = CAN_RECEIVE | REQUIRES_BUFFERING | CAN_RECEIVE_COMMAND;
  regs.r[1] = (int)"Channel-Activity";
  regs.r[2] = 1; // version
  regs.r[3] = (int)"29 Oct 2025";
  _kernel_swi(MIDISupport_CreateDriver, &regs, &regs);
  midi_support_handle = regs.r[0];
}

/*
 * remove_driver
 * -------------
 * Commands Midi Support to remove (delete) our driver.
 */
void remove_driver(void)
{
  _kernel_swi_regs regs;

  regs.r[1] = midi_support_handle;
  _kernel_swi(MIDISupport_RemoveDriver, &regs, &regs);
}

/*
 * midi_out_port
 * -------------
 * Sends a midi message to Midi Support.
 */
void midi_out_port(int status, int data1, int data2, int n)
{
  _kernel_swi_regs regs;

   regs.r[2] = status |
               (data1 << 8) |
               (data2 << 16) |
               (n << 24);

   regs.r[0] = 1; // send a command
   regs.r[1] = midi_support_handle;
   _kernel_swi(MIDISupport_Send, &regs, &regs);
}


/*
 * midi_in_port
 * ------------
 * Runs every 100ms. Requests midi messages from Midi Support until
 * no more returned.
 */
void midi_in_port(void)
{
  _kernel_swi_regs regs;

  do
  {
    regs.r[0] = 1; // receive byte or command
    regs.r[1] = midi_support_handle;
    regs.r[2] = 2; // receive command
    if(_kernel_swi(MIDISupport_Receive, &regs, &regs) != NULL)
      return;
    if(regs.r[0])
      midi_rx_command(regs.r[0]); // interpret message
  }
  while(regs.r[0]);
  update_activity();
}

