/*
  !MidiKey
  Provides a keyboard note entry and nonitors channel activity.

  channel.c
  Controls the Channels window that displays and allows adjustment
  of the player's channel volume and program.

  3/8/22
*/

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

extern menu_t channel_menu;
extern const char * const gm_melodic[];
void midi_out_port(int status, int data1, int data2, int n);

/*
 * init_chan_window
 * ----------------
 * Set icons and data to reset values
 */
void init_chan_window(void)
{
  int chan;
  for(chan=0; chan<NUM_MIDI_CHANS; chan++)
  {
    chan_t *c = &midi.chan[chan];
    c->prg = 0;
    c->bank = 0;
    c->switches = 0;
    c->volume = 16383; // max
    c->expression = 16383; // max
    c->pitch = 8192; // mid

    const char *p;
    if(chan == (PERCUSSION_CHAN-1))
      p = "GM Drum Kit";
    else
      p = gm_melodic[c->prg]; // show GM instruments
    int icon = chan * CHN_ROW_ICONS;
    int win = ro.handle[WIN_CHANNELS];
    icon_text_change(itoa(c->bank), win, ICON_CHN_BNK_VAL + icon);
    icon_text_change(itoa(c->prg), win, ICON_CHN_PRG_VAL + icon);
    icon_text_change((char *)p, win, ICON_CHN_PRG_NAME + icon);
    icon_colour_change(0x17, win, ICON_CHN_PRG_NAME + icon);
    slider_display_value(c->volume, SLDR_CHN_VOL, WIN_CHANNELS, ICON_CHN_VOL_SLDR + icon);
    slider_display_value(c->expression, SLDR_CHN_VOL, WIN_CHANNELS, ICON_CHN_EXP_SLDR + icon);
    slider_display_value(c->pitch, SLDR_CHN_VOL, WIN_CHANNELS, ICON_CHN_PIT_SLDR + icon);
  }
}


/*
 * display_prog_info
 * -----------------
 * Updates a single channel
 */
static void display_prog_info(int chan, int prg)
{
  const char *p;
  if(chan == (PERCUSSION_CHAN-1))
    p = "GM Drum Kit";
  else
    p = gm_melodic[prg]; // show GM instruments
  int icon = chan * CHN_ROW_ICONS;
  icon_text_change(itoa(prg), ro.handle[WIN_CHANNELS], ICON_CHN_PRG_VAL + icon);
  icon_text_change((char *)p, ro.handle[WIN_CHANNELS], ICON_CHN_PRG_NAME + icon);

  midi_out_port(PROGRAM + chan, prg, 0, 2);
}

/*
 * display_bank_info
 * -----------------
 * Updates a single channel
 */
static void display_bank_info(int chan, int bank)
{
  int icon = chan * CHN_ROW_ICONS;
  icon_text_change(itoa(bank), ro.handle[WIN_CHANNELS], ICON_CHN_BNK_VAL + icon);

  midi_out_port(CONTROL + chan, BANK_lo, bank, 3);
}


/*
 * chn_slider_control
 * ------------------
 * blk = block from Wimp_GetPointerInfo
 * Returns True if dealt with here, else False
 */
int chn_slider_control(int *blk)
{
  if(ro.slider.window != ro.handle[WIN_CHANNELS])
    return 0;

  int chan = (ro.slider.icon - CHN_ROW_START) / CHN_ROW_ICONS;
  int icon = ro.slider.icon - (CHN_ROW_ICONS * chan);

  switch(icon)
  {
    case ICON_CHN_VOL_SLDR:
      // Channel volume display.
      {
        int v = midi.chan[chan].volume = slider_get_posn(blk, SLDR_CHN_VOL);
        slider_display_value(v, SLDR_CHN_VOL, WIN_CHANNELS, ro.slider.icon);

        midi_out_port(CONTROL + chan, VOLUME_lo, v & 0x7f, 3);
        midi_out_port(CONTROL + chan, VOLUME_hi, (v >> 7) & 0x7f, 3);
      }
      break;

    case ICON_CHN_EXP_SLDR:
      // Channel expression display.
      {
        int v = midi.chan[chan].expression = slider_get_posn(blk, SLDR_CHN_VOL);
        slider_display_value(v, SLDR_CHN_VOL, WIN_CHANNELS, ro.slider.icon);

        midi_out_port(CONTROL + chan, EXPRESSION_lo, v & 0x7f, 3);
        midi_out_port(CONTROL + chan, EXPRESSION_hi, (v >> 7) & 0x7f, 3);
      }
      break;

    case ICON_CHN_PIT_SLDR:
      // Channel pitch wheel display.
      {
        int v = midi.chan[chan].pitch = slider_get_posn(blk, SLDR_CHN_VOL);
        slider_display_value(v, SLDR_CHN_VOL, WIN_CHANNELS, ro.slider.icon);
        midi_out_port(PITCH_WHEEL + chan, v & 0x7f, (v >> 7) & 0x7f, 3);
      }
      break;

    default: return 0;
  }

  return 1;
}


/*
 * chn_mouse_click
 * ---------------
 * blk = block from Wimp_Poll
 * state = icon state, selected or not
 * Returns True if dealt with here, else False
 */
int chn_mouse_click(int *blk, int state)
{
  if(blk[3] != ro.handle[WIN_CHANNELS])
    return 0;

  int chan = (blk[4] - CHN_ROW_START) / CHN_ROW_ICONS;
  int icon = blk[4] - (CHN_ROW_ICONS * chan);
  int inc = 0;
  if(blk[2] == MOUSE_SELECT)
    inc = 1;
  else if (blk[2] == MOUSE_ADJUST)
    inc = -1;

  if(blk[2] == MOUSE_MENU)
  {
    if(blk[4] >= CHN_ROW_START)
    {
      int i;
      for(i=0; i<NUM_SWITCHES; i++)
        if(midi.chan[chan].switches & (1 << i))
          channel_menu.item[i].item_flags |= IT_TICKED;
        else
          channel_menu.item[i].item_flags &= ~IT_TICKED;
      sprintf(&channel_menu.text[8], "%d", chan + 1);
      open_menu(blk[0] - 64, blk[1], MENU_CHANNEL, (int)&channel_menu);
      midi.menu_chan = chan;
    }
  }
  else switch(icon)
  {
    case ICON_CHN_VOL_BACK: // channel volume
      blk[4]++;
    case ICON_CHN_VOL_SLDR:
      ro.slider.window = blk[3];
      ro.slider.icon = blk[4];
      icon_state_change(1, blk[3], blk[4]);
      break;

    case ICON_CHN_EXP_BACK: // channel expression
      blk[4]++;
    case ICON_CHN_EXP_SLDR:
      ro.slider.window = blk[3];
      ro.slider.icon = blk[4];
      icon_state_change(1, blk[3], blk[4]);
      break;

    case ICON_CHN_PIT_BACK: // channel pitch wheel
      blk[4]++;
    case ICON_CHN_PIT_SLDR:
      ro.slider.window = blk[3];
      ro.slider.icon = blk[4];
      icon_state_change(1, blk[3], blk[4]);
      break;

    case ICON_CHN_PRG_DEC:
      inc = -inc;
    case ICON_CHN_PRG_INC: // channel program
      {
        chan_t *c = &midi.chan[chan];
        c->prg = (c->prg + inc) & (MIDI_DATA_RANGE - 1);
        display_prog_info(chan, c->prg);
      }
      break;

    case ICON_CHN_BNK_DEC:
      inc = -inc;
    case ICON_CHN_BNK_INC: // channel bank Low
      {
        chan_t *c = &midi.chan[chan];
        c->bank = (c->bank + inc) & (MIDI_DATA_RANGE - 1);
        display_bank_info(chan, c->bank);
      }
      break;

    default: return 0;
  }

  return 1;
}


/*
 * chn_key_press
 * -------------
 * blk = block from Wimp_GetIconState
 * key = current character
 * p = pointer to icon text
 * Returns True if dealt with here, else False
 */
int chn_key_press(int *blk, int key, char *p)
{
  if(blk[0] != ro.handle[WIN_CHANNELS])
    return 0;

  if(key == '\r')
  {
    int chan = (blk[1] - CHN_ROW_START) / CHN_ROW_ICONS;
    int icon = blk[1] - (CHN_ROW_ICONS * chan);
    int n;
    chan_t *c = &midi.chan[chan];

    if(icon == ICON_CHN_PRG_VAL)
    {
      n = atoi(p);
      LIMIT(0, n, MIDI_DATA_RANGE - 1);
      c->prg = n;
      display_prog_info(chan, n);
      return 1;
    }
    else if(icon == ICON_CHN_BNK_VAL)
    {
      n = atoi(p);
      LIMIT(0, n, MIDI_DATA_RANGE - 1);
      c->bank = n;
      display_bank_info(chan, n);
      return 1;
    }
  }

  return 0;
}

