/*
 display.c
 ---------
 Displays the user instrument
 created  20/1/2024
*/

#include "synth.h"
#include "wimp.h"
#include "lib.h"
#include "main.h"
#include "common.h"
#include "editor.h"
#include "exp.h" // log conversion tables for trackbars (sliders)

const char * const note[] = {"C","C#","D","D#","E","F","F#","G","G#","A","A#","B"};

/*
 * TimestampToString
 * -----------------
 * Converts timestamp to a readable string.
 * We use a timestamp epoch of 1/1/2001.
 * The maths is correct for the whole 400 year Gregorian cycle and
 * hence forever, but a 32 bit timestamp overflows in the year 2137.
 */
#define DAY_ZERO_OFFSET  730792 // number of days from 1/3/0000 to 1/1/2001
char *TimestampToString(unsigned int t)
{
  unsigned int t1;
  t1 = t / 60;
  unsigned int sec = t - (t1 * 60);
  t = t1 / 60;
  unsigned int min = t1 - (t * 60);
  t1 = t / 24;
  unsigned int hour = t - (t1 * 24);

  t = t1 + DAY_ZERO_OFFSET; // relocate from 1/1/2001 to 1/3/0000
  unsigned int a = 4 * t / 146097;
  unsigned int b = t + a - (a / 4);
  unsigned int y = (4 * b - 1) / 1461;
  unsigned int d = b - 365 * y - (y / 4) + 122;
  unsigned int m = d * 100 / 3061;
  d = d - m * 3061 / 100;
  if(--m > 12) // 1/3/xxx0 to 1/1/xxx1 correction
  {
    m -= 12;
    y++;
  }

  static char s[24];
  sprintf(s, "%d/%d/%d %02d:%02d:%02d", d, m, y, hour, min, sec);
  return s;
}


/*
 * inv
 * ---
 * Conversion from linear values to log. Used for slider controls.
 */
static int inv(const unsigned short *tab, int in)
{
  int out = 0;

  while((tab[out] < in) && (out < (LEN-1)))
    out++;

  return out;
}


/*
 * trb_to_param
 * ------------
 * id = control id offset
 * Converts trackbar values to parameter values
 */
int trb_to_param(int slider, int n)
{
  switch(slider)
  {
    // envelopes, wave1, wave2, filter, and noise panes
    case SLDR_ENV_DY:     return e4k[n];     // envelope delay
    case SLDR_ENV_AR:     return e32k[n];    // envelope attack step
    case SLDR_ENV_AT:     return e32k[n];    // envelope attack target
    case SLDR_ENV_AH:     return e4k[n];     // envelope attack hold
    case SLDR_ENV_DR:     return e32k[n];    // envelope decay step
    case SLDR_ENV_DT:     return e32k[n];    // envelope decay target
    case SLDR_ENV_SR:     return e32k[n];    // envelope sustain step
    case SLDR_ENV_RR:     return e32k[n];    // 2 envelope release step
    // wave 1 and wave2 panes, additional sliders
    case SLDR_GLIDE:      return e32ki[n];   // glide time
    // filter pane, additional sliders
    case SLDR_RESONANCE:  return e1ki[n];    // filter resonance
    // general controls pane
    case SLDR_FM:         return e32k[n];    // fm depth
    case SLDR_MOD_RATE:   return e8k[n];     // modulation rate
    case SLDR_MOD_DEPTH:  return e32k[n];    // modulation depth
    case SLDR_GEN_RETRIG: return e1k[n];     // envelope retrigger
    case SLDR_GEN_GAIN:   return e4k20db[n]; // instrument gain
  }
  return n; // others are a direct (linear) relationship
}


/*
 * param_to_trb
 * ------------
 * id = control id offset
 * Converts parameter values to trackbar values
 */
static int param_to_trb(int slider, int n)
{
  switch(slider)
  {
    // envelopes, wave1, wave2, filter, and noise panes
    case SLDR_ENV_DY:     return inv(e4k, n);     // envelope delay
    case SLDR_ENV_AR:     return inv(e32k, n);    // envelope attack step
    case SLDR_ENV_AT:     return inv(e32k, n);    // envelope attack target
    case SLDR_ENV_AH:     return inv(e4k, n);     // envelope attack hold
    case SLDR_ENV_DR:     return inv(e32k, n);    // envelope decay step
    case SLDR_ENV_DT:     return inv(e32k, n);    // envelope decay target
    case SLDR_ENV_SR:     return inv(e32k, n);    // envelope sustain step
    case SLDR_ENV_RR:     return inv(e32k, n);    // 2 envelope release step
    // wave 1 and wave2 panes, additional sliders
    case SLDR_GLIDE:      return inv(e32ki, n);   // glide time
    // filter pane, additional sliders
    case SLDR_RESONANCE:  return inv(e1ki, n);    // filter resonance
    // general controls pane
    case SLDR_FM:         return inv(e32k, n);    // fm depth
    case SLDR_MOD_RATE:   return inv(e8k, n);     // modulation rate
    case SLDR_MOD_DEPTH:  return inv(e32k, n);    // modulation depth
    case SLDR_GEN_RETRIG: return inv(e1k, n);     // envelope retrigger
    case SLDR_GEN_GAIN:   return inv(e4k20db, n); // instrument gain
  }
  return n; // others are a direct (linear) relationship
}


/*
 * slider_change
 * -------------
 * used by the editor, updates a slider position and it's associated numerical value
 */
void slider_change(int n, int slider, int window, int icon)
{
  slider_display_value(param_to_trb(slider, n), slider, window, icon);
  icon_text_change(itoa(n), ro.handle[window], icon + 1);
}


/*
 * env_slider_change
 * -----------------
 * Displays the slider values for an envelope.
 */
static void env_slider_change(env_t *env, int win)
{
  slider_change(env->delay,         SLDR_ENV_DY, win, ICON_ENV_DY_SLDR);
  slider_change(env->attack_step,   SLDR_ENV_AR, win, ICON_ENV_AR_SLDR);
  slider_change(env->attack_target, SLDR_ENV_AT, win, ICON_ENV_AT_SLDR);
  slider_change(env->hold,          SLDR_ENV_AH, win, ICON_ENV_AH_SLDR);
  slider_change(env->decay_step,    SLDR_ENV_DR, win, ICON_ENV_DR_SLDR);
  slider_change(env->decay_target,  SLDR_ENV_DT, win, ICON_ENV_DT_SLDR);
  slider_change(env->sustain_step,  SLDR_ENV_SR, win, ICON_ENV_SR_SLDR);
  slider_change(env->release_step,  SLDR_ENV_RR, win, ICON_ENV_RR_SLDR);
}


/*
 * display_user
 * ------------
 * Updates the editor window with the syn.user instrument data.
 * This updates ALL the user and sound set displayed parameters.
 */
void display_user(void)
{
  ins_t *i = ro.user;
  int hi = ro.patch & 0xff;
  int lo = (ro.patch >> 8) & 0xff;
  int prg = (ro.patch >> 16) & 0xff;
  char s[20];

  if(hi == PERCUSSION_BANK)
  {
    memset(i, 0, sizeof(ins_t));
    strcpy(i->name, "Drum Kit not editable");
  }

  // editor window
  icon_text_change((ro.inst_num >= 0) ? itoa(ro.inst_num) : "", ro.handle[WIN_EDT], ICON_ED_INST_NUM);
  icon_text_change(ro.soundset_name, ro.handle[WIN_EDT], ICON_ED_SOUND_SET);
  icon_text_change(TimestampToString(ro.soundset_date), ro.handle[WIN_EDT], ICON_ED_SSET_DATE);
  icon_text_change(i->name, ro.handle[WIN_EDT], ICON_ED_INST_NAME);

  if(ro.patch_valid)
  {
    icon_text_change(itoa(ro.cur_ins), ro.handle[WIN_EDT], ICON_ED_PATCH);
    icon_text_change(ro.bank_name,     ro.handle[WIN_EDT], ICON_ED_BANK_NAME);
    icon_disabled_change(0, ro.handle[WIN_EDT], ICON_ED_BANK_NAME);
    icon_text_change(itoa(prg), ro.handle[WIN_EDT], ICON_ED_PRG_VAL);
    icon_text_change(itoa(hi),  ro.handle[WIN_EDT], ICON_ED_BHI_VAL);
    icon_text_change(itoa(lo),  ro.handle[WIN_EDT], ICON_ED_BLO_VAL);
  }
  else // instrument doesn't have a bank location (isn't being used)
  {
    icon_text_change("", ro.handle[WIN_EDT], ICON_ED_PATCH);
    icon_text_change("No Instrument reference", ro.handle[WIN_EDT], ICON_ED_BANK_NAME);
    icon_disabled_change(1, ro.handle[WIN_EDT], ICON_ED_BANK_NAME);
    icon_text_change("", ro.handle[WIN_EDT], ICON_ED_PRG_VAL);
    icon_text_change("", ro.handle[WIN_EDT], ICON_ED_BHI_VAL);
    icon_text_change("", ro.handle[WIN_EDT], ICON_ED_BLO_VAL);
  }

  if(hi <= PERCUSSION_BANK) // melodic edit, or non editable drum kit
  {
    icon_text_change("Program", ro.handle[WIN_EDT], ICON_ED_PRG_NAME);
    icon_text_change("Bank Lo", ro.handle[WIN_EDT], ICON_ED_BLO_NAME);
  }
  else // percussion edit
  {
    icon_text_change("Kit", ro.handle[WIN_EDT], ICON_ED_PRG_NAME);
    sprintf(s, "Key (%s%d)", note[lo % 12], lo / 12);
    icon_text_change(s, ro.handle[WIN_EDT], ICON_ED_BLO_NAME);
  }

  // wave 1 pane
  env_slider_change(&i->wave[0].env, WIN_ED_WAVE1);
  slider_change(i->wave[0].glide,             SLDR_GLIDE,  WIN_ED_WAVE1, ICON_WAV_GT_SLDR);
  if(test_menu.item[0].item_flags & IT_TICKED)
  {
    icon_text_change("Test", ro.handle[WIN_ED_WAVE1], ICON_WAV_NUM_NAME);
    icon_text_change("",     ro.handle[WIN_ED_WAVE1], ICON_WAV_NUM_VAL);
  }
  else
  {
    icon_text_change(ro.harm[i->wave[0].number].name, ro.handle[WIN_ED_WAVE1], ICON_WAV_NUM_NAME);
    icon_text_change(itoa(i->wave[0].number),        ro.handle[WIN_ED_WAVE1], ICON_WAV_NUM_VAL);
  }
  icon_text_change(itoa(i->wave[0].pitch),           ro.handle[WIN_ED_WAVE1], ICON_WAV_FPO_VAL);
  icon_text_change(itoa(i->wave[0].initial_pitch),   ro.handle[WIN_ED_WAVE1], ICON_WAV_IPO_VAL);
  icon_state_change((i->switches >> PITCH1_MOD) & 1, ro.handle[WIN_ED_WAVE1], ICON_WAV_PM);
  icon_state_change((i->switches >> AMP1_MOD) & 1,   ro.handle[WIN_ED_WAVE1], ICON_WAV_AUM);
  icon_state_change((i->switches >> AMP1_RING) & 1,  ro.handle[WIN_ED_WAVE1], ICON_WAV_ASM);
  icon_state_change((i->master_env == 1),  ro.handle[WIN_ED_WAVE1], ICON_WAV_MASTER);
  // wave 2 pane
  env_slider_change(&i->wave[1].env, WIN_ED_WAVE2);
  slider_change(i->wave[1].glide,             SLDR_GLIDE,  WIN_ED_WAVE2, ICON_WAV_GT_SLDR);
  if(test_menu.item[1].item_flags & IT_TICKED)
  {
    icon_text_change("Test", ro.handle[WIN_ED_WAVE2], ICON_WAV_NUM_NAME);
    icon_text_change("", ro.handle[WIN_ED_WAVE2], ICON_WAV_NUM_VAL);
  }
  else
  {
    icon_text_change(ro.harm[i->wave[1].number].name,   ro.handle[WIN_ED_WAVE2], ICON_WAV_NUM_NAME);
    icon_text_change(itoa(i->wave[1].number),          ro.handle[WIN_ED_WAVE2], ICON_WAV_NUM_VAL);
  }
  icon_text_change(itoa(i->wave[1].pitch),           ro.handle[WIN_ED_WAVE2], ICON_WAV_FPO_VAL);
  icon_text_change(itoa(i->wave[1].initial_pitch),   ro.handle[WIN_ED_WAVE2], ICON_WAV_IPO_VAL);
  icon_state_change((i->switches >> PITCH2_MOD) & 1, ro.handle[WIN_ED_WAVE2], ICON_WAV_PM);
  icon_state_change((i->switches >> AMP2_MOD) & 1,   ro.handle[WIN_ED_WAVE2], ICON_WAV_AUM);
  icon_state_change((i->switches >> AMP2_RING) & 1,  ro.handle[WIN_ED_WAVE2], ICON_WAV_ASM);
  icon_state_change((i->master_env == 2),  ro.handle[WIN_ED_WAVE2], ICON_WAV_MASTER);
  // filter pane
  env_slider_change(&i->filter_env, WIN_ED_FILTER);
  slider_change(i->filter_q,                SLDR_RESONANCE, WIN_ED_FILTER, ICON_FLT_RES_SLDR);
  icon_text_change(itoa(i->filter_fc),              ro.handle[WIN_ED_FILTER], ICON_FLT_CF_VAL);
  icon_state_change((i->switches >> LOWPASS) & 1,   ro.handle[WIN_ED_FILTER], ICON_FLT_LOW_PASS);
  icon_state_change((i->switches >> BANDPASS) & 1,  ro.handle[WIN_ED_FILTER], ICON_FLT_BAND_PASS);
  icon_state_change((i->switches >> HIGHPASS) & 1,  ro.handle[WIN_ED_FILTER], ICON_FLT_HIGH_PASS);
  icon_state_change((i->switches >> FILTER_RETRIGGER) & 1, ro.handle[WIN_ED_FILTER], ICON_FLT_RETRIG);
  icon_state_change((i->switches >> INV_FILTER_ENV) & 1,   ro.handle[WIN_ED_FILTER], ICON_FLT_INVERT);
  icon_state_change((i->switches >> FILTER_MOD) & 1,       ro.handle[WIN_ED_FILTER], ICON_FLT_PM);
  icon_state_change((i->switches >> FILTER_TRACK) & 1,     ro.handle[WIN_ED_FILTER], ICON_FLT_PT);
  icon_state_change((i->switches >> FILTERED_TONE) & 1,    ro.handle[WIN_ED_FILTER], ICON_FLT_WF);
  icon_state_change((i->switches >> FILTER_ENV_TRACK) & 1, ro.handle[WIN_ED_FILTER], ICON_FLT_ET);
  icon_state_change((i->master_env == 3),  ro.handle[WIN_ED_FILTER], ICON_FLT_MASTER);
  // noise pane
  env_slider_change(&i->noise_env, WIN_ED_NOISE);
  icon_state_change((i->switches >> NOISE_MOD) & 1,       ro.handle[WIN_ED_NOISE], ICON_NOISE_AM);
  icon_state_change((i->switches >> NOISE_RETRIGGER) & 1, ro.handle[WIN_ED_NOISE], ICON_NOISE_RETRIG);
  icon_state_change((i->switches >> NOISE_ENV_TRACK) & 1, ro.handle[WIN_ED_NOISE], ICON_NOISE_ET);
  icon_state_change((i->master_env == 4),  ro.handle[WIN_ED_NOISE], ICON_NOISE_MASTER);
  // general pane
  slider_change(i->fm_depth,  SLDR_FM,         WIN_ED_GENERAL, ICON_COM_FM_SLDR);
  slider_change(i->detune,    SLDR_DETUNE,     WIN_ED_GENERAL, ICON_COM_DT_SLDR);
  slider_change(i->mod_rate,  SLDR_MOD_RATE,   WIN_ED_GENERAL, ICON_MOD_RATE_SLDR);
  slider_change(i->mod_depth, SLDR_MOD_DEPTH,  WIN_ED_GENERAL, ICON_MOD_DPTH_SLDR);
  slider_change(i->retrig,    SLDR_GEN_RETRIG, WIN_ED_GENERAL, ICON_GEN_ERT_SLDR);
  slider_change(i->gain,      SLDR_GEN_GAIN,   WIN_ED_GENERAL, ICON_GEN_GAIN_SLDR);
  if(test_menu.item[2].item_flags & IT_TICKED)
  {
    icon_text_change("Test", ro.handle[WIN_ED_GENERAL], ICON_MOD_NUM_NAME);
    icon_text_change("",     ro.handle[WIN_ED_GENERAL], ICON_MOD_NUM_VAL);
  }
  else
  {
    icon_text_change(ro.harm[i->mod_wave].name, ro.handle[WIN_ED_GENERAL], ICON_MOD_NUM_NAME);
    icon_text_change(itoa(i->mod_wave),        ro.handle[WIN_ED_GENERAL], ICON_MOD_NUM_VAL);
  }
  icon_state_change((i->switches >> PITCH_TRACK) & 1,    ro.handle[WIN_ED_GENERAL], ICON_COM_PT);
  icon_state_change((i->switches >> WAVE_ENV_TRACK) & 1, ro.handle[WIN_ED_GENERAL], ICON_COM_ET);
  icon_state_change((i->switches >> WAVE_RETRIGGER) & 1, ro.handle[WIN_ED_GENERAL], ICON_COM_RETRIG);
  icon_state_change((i->switches >> TONE1_OUT) & 1,      ro.handle[WIN_ED_GENERAL], ICON_COM_W1_OUT);
  icon_state_change((i->switches >> TONE2_OUT) & 1,      ro.handle[WIN_ED_GENERAL], ICON_COM_W2_OUT);
  icon_state_change((i->switches >> TONE_RING_MOD) & 1,  ro.handle[WIN_ED_GENERAL], ICON_COM_WRM);
  icon_state_change((i->switches >> DEFINED_LENGTH) & 1, ro.handle[WIN_ED_GENERAL], ICON_GEN_DLE);
  icon_state_change((i->switches >> MONO_SCAN) & 1,      ro.handle[WIN_ED_GENERAL], ICON_GEN_MONO);

  // create instrument
  if(hi > PERCUSSION_BANK) // percussive
  {
    icon_state_change(1, ro.handle[WIN_CREATE], ICON_CA_PERCUSSION);
    icon_state_change(0, ro.handle[WIN_CREATE], ICON_CA_MELODIC);
    icon_text_change("Kit", ro.handle[WIN_CREATE], ICON_CA_PROG);
    icon_text_change("Key", ro.handle[WIN_CREATE], ICON_CA_BANK);
  }
  else if(hi < PERCUSSION_BANK)// melodic
  {
    icon_state_change(0, ro.handle[WIN_CREATE], ICON_CA_PERCUSSION);
    icon_state_change(1, ro.handle[WIN_CREATE], ICON_CA_MELODIC);
    icon_text_change("Program", ro.handle[WIN_CREATE], ICON_CA_PROG);
    icon_text_change("Bank", ro.handle[WIN_CREATE], ICON_CA_BANK);
  }
  icon_text_change(itoa(prg), ro.handle[WIN_CREATE], ICON_CA_PROG_VAL);
  icon_text_change(itoa(lo), ro.handle[WIN_CREATE], ICON_CA_BANK_VAL);
  ro.save_patch = ro.patch;
  kit_check();
}


