#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include "kernel.h"
// oslib
#include "os.h"
//
#include "proto.h"
#include "midi.h"
#include "midiport.h"
#include "events.h"

// this source file handles the creating and removal of MIDI ports


static _kernel_oserror err_reserved_bits_set = { 1, "Reserved bits set" };
static _kernel_oserror err_unknown_reason = { 1, "Unknown reason code" };
static _kernel_oserror err_unknown_port = { 1, "Unknown port" };
static _kernel_oserror err_failed_to_create = { 1, "Failed to create MIDI port" };


static MIDIPORT *ports[MAXPORT];

static _kernel_oserror *remove_midiport(int porti);
static _kernel_oserror *validate_port(int porti);
static _kernel_oserror *create_midiport(int freq, int *porti);



void midiport_initialise() {

  int i;

  for (i = 1; i < MAXPORT; i++)    ports[i] = NULL;
}



void midiport_kill() {

  int i;

  for (i = 1; i < MAXPORT; i++)
    if (ports[i])   remove_midiport(i);
}



_kernel_oserror *midiport(int reason, int arg1, int arg2, int arg3, int *rval0, int *rval1) {

  _kernel_oserror *err;

  switch (reason & 255) {
  case 0: // create port
          // arg1 = freq
          // arg2 = features
          //        bits  0..3  base-channel
          //        bits  4..8  tuning
          //        bits  9..14 max polyphony
    {
      int porti, base, tuning, poly;

      err = create_midiport(arg1, &porti);
      if (err)                    return err;
      *rval0 = porti;
      if (arg2 == 0) {
        base   = 0;
        tuning = 0;
        poly   = 24;
      } else {
        base   = arg2 & 15;
        tuning = (arg2 << 23)>>27;
        poly   = (arg2 >> 9) & 63;
      }
      ports[porti]->basechannel = base;
      ports[porti]->tuning = tuning;
      ports[porti]->polyphony = poly;
    }
    break;
  case 1: // delete port
    {
      int porti;

      if (reason & 0xffff0000)    return &err_reserved_bits_set;
      porti = (reason>>8) & 255;
      return remove_midiport(porti);
    }
    break;
  case 2: // tx bytes
    {
      int porti, available;

      if (reason & 0xffff0000)    return &err_reserved_bits_set;
      porti = (reason>>8) & 255;
      err = validate_port(porti);
      if (err)                    return err;

      midi_rx_bytes(ports[porti], (unsigned char *)arg1, arg2, &available);
      *rval0 = available;
    }
    break;
  case 3: // tx command
    {
      int porti, available;
      unsigned char buffer[8];

      if (reason & 0xffff0000)    return &err_reserved_bits_set;
      porti = (reason>>8) & 255;
      err = validate_port(porti);
      if (err)                    return err;

      ((int *)buffer)[0] = arg1;
      ((int *)buffer)[1] = arg2;

      midi_rx_bytes(ports[porti], buffer+1, (*buffer)&7, &available);
      *rval0 = available;
    }
    break;
  case 4: // tx small command
    {
      int porti, available;
      unsigned char buffer[4];

      if (reason & 0xffff0000)    return &err_reserved_bits_set;
      porti = (reason>>8) & 255;
      err = validate_port(porti);
      if (err)                    return err;

      ((int *)buffer)[0] = arg1;

      midi_rx_bytes(ports[porti], buffer+1, (*buffer)&3, &available);
      *rval0 = available;
    }
    break;
  case 5: // read samples
    {
      int porti;

      if (reason & 0xffff0000)    return &err_reserved_bits_set;
      porti = (reason>>8) & 255;
      err = validate_port(porti);
      if (err)                    return err;
      if (arg3 & 0xffffff83)      return &err_reserved_bits_set;

      midi_read_samples((signed short *)arg1, arg2, arg3, ports[porti]);
    }
    break;
  case 6: // read info
    {
      int porti;

      if (reason & 0xffff0000)    return &err_reserved_bits_set;
      porti = (reason>>8) & 255;
      err = validate_port(porti);
      if (err)                    return err;
      // not supported
    }
    break;
  default:
    return &err_unknown_reason;
    break;
  }

  return NULL;
}

// --------------------------------------------------------------------------

_kernel_oserror *create_midiport(int freq, int *prti) {

  MIDIPORT *port;
  int porti, i;

  porti = -1;
  for (i = 1; (i < MAXPORT) && (porti == -1); i++)
    if (!ports[i])   porti = i;
  if (porti == -1)  return &err_failed_to_create;

  if (freq < 5000)   freq = 5000;
  if (freq > 50000)  freq = 50000;
  port = malloc(sizeof(MIDIPORT));
  if (!port)        return &err_failed_to_create;

  ports[porti] = port;
  reset_port_structure(port);
  port->frequency = freq;

  *prti = porti;
  generate_event(HBP10GM_EVENT_PORT_CREATED, (int)port, 0, 0);
  return NULL;
}


_kernel_oserror *validate_port(int porti) {

  if ((porti <= 0) || (porti >= MAXPORT))   return &err_unknown_port;
  if (!ports[porti])                        return &err_unknown_port;

  return NULL;
}


_kernel_oserror *remove_midiport(int porti) {

  _kernel_oserror *err;


  err = validate_port(porti);
  if (err)   return err;

  generate_event(HBP10GM_EVENT_PORT_DELETED, (int)ports[porti], 0, 0);

  free(ports[porti]);
  ports[porti] = NULL;

  return NULL;
}
