/***************************************************************************
*
* FunktrackerGOLD - By Jason Nunn
*
* Using Curses (ANSI C) NON-preemptive Processing
*
* Snail: 32 Rothdale Road, Moil, Darwin, NT, 0810, Australia
*
* ================================================
*
* Playback (visual) tracking code
*
***************************************************************************/
#include <ncurses.h>
#pragma pack(1)
#include "everything.h"   /*version info, byte sex*/
#include "funktracker.h"  /*funktracker structures*/
#pragma pack()
#include "fickle.h"

/*protos dsp_mixxer.c*/
extern tchmix chmix[MAXIMUM_CHANNELS];
extern int hz_rate;
extern int no_active_channels;

/*protos funktracker.c*/
#pragma pack(1)
extern tfunk_info funk_info;
extern tfunk_hr *funk_hr_ptr;
extern tfunk_chan funk_chan[MAXIMUM_CHANNELS];
extern void funk_tracker(void);
#pragma pack()

/*protos funkmisc.c*/
extern char *ferr_messages[];
extern void funk_init_for_play(void);
extern void (*virtualmixxer)(void);

/*protos funkgold_main.c*/
extern int ch,maxy;
extern char edit_mode;
extern char nibble_display[];
extern char *note_display[];
extern void display_topbar(void);
extern void update_screen(void);
extern void clear_area(char y1,char x1,char y2,char x2);
extern void byte2str(char *str,unsigned char value);
extern void word2str(char *str,unsigned long value);
extern void dword2str(char *str,unsigned long value);
extern int hex2char(char *str);
extern long hex2long(char *str);
extern void ferr_message(char *mess_str);

/*protos funkgold_se.c*/
extern unsigned char se_pos_real;
extern unsigned char se_pos_hl;

/*protos funkgold_pe.c*/
extern void pe_dis_seqpat_no_bg(void);
extern void pe_display_trak(
  unsigned char pat_no,unsigned char chan_real,unsigned char pos_real);
extern void pe_display_ind(unsigned char pat_no,unsigned char pos_real);
extern void pe_display_bg(void);
extern void pe_display_all(void);

/*locals*/
int trac_pat_real_old,trac_pat_real,trac_pat_hl,trac_pat_old,
    trac_pattern_ofs_old,trac_sequence_ofs_old,trac_len;

char *cmd_dis_labels[] =
{
  "FPort Up","FPort Dn","FPorta  ","Vibrato ","VibFanin","VibFanot",
  "VSlid Up","VSlid Dn","VPorta  ","VReverb ","Tremola ","Arpeggio",
  "Sam Offs","Volume  ","Cmd O   "
};

char *cmdo_dis_labels[] =
{
  "CmdO0   ","Vol Cut!","Note dly","Arpg Spd","FnPrt Up","FnPrt Dn",
  "FnSld Up","FnSld Dn","VolCrest","Feedback","M Volume","EchDelay",
  "EchDecay","Retrig  ","Balance ","Tempo   "
};

char *cmdo0_dis_labels[] =
{
  "Vib Sine","Vib Tri ","Vib Sqr ","Vib Saw ","Vib Rnd ","Trm Sine",
  "Tri Tri ","Tri Sqr ","Tri Saw ","Tri Rnd ","Hlt Note","Hlt Vol ",
  "Hlt All ","????????","pan Left","pan rght"
};

int hz_counter;
int clock_sec;
int clock_min;

/***************************************************************************
*
***************************************************************************/
void print_watch(void)
{
  char tmpstr[4];

  attroff(A_REVERSE);
  move(1,74);
  if(clock_min < 10)
    sprintf(tmpstr,"0%d:",clock_min);
  else
    sprintf(tmpstr,"%d:",clock_min);
  addstr(tmpstr);
  if(clock_sec < 10)
    sprintf(tmpstr,"0%d",clock_sec);
  else
    sprintf(tmpstr,"%d",clock_sec);
  addstr(tmpstr);
  update_screen();
}

void stop_watch(void)
{
  if(hz_counter > hz_rate)
  {
    hz_counter = 0;
    clock_sec++;
    if(clock_sec == 60)
    {
      clock_sec = 0;
      clock_min++;
      if(clock_min > 99) clock_min = 0;
    }
    print_watch();
  }
  else
    hz_counter++;
}

/***************************************************************************
*
***************************************************************************/
void fast_track(int seq_no)
{
  register int chan_no;

  while(funk_info.sequence_ofs != seq_no)
  {
    funk_tracker();
    stop_watch();
  }
  for(chan_no = 0;chan_no < no_active_channels;chan_no++)
    chmix[chan_no].funkctrl = 0;
}

/***************************************************************************
* miwi 27/05/96
***************************************************************************/
void trac_dis_seqpatno(void)
{
  char tmpstr[] = "  ";
  register int amaxy = maxy - 4;

  move(amaxy,9);
  byte2str(tmpstr,funk_info.sequence_ofs_display);
  addstr(tmpstr);
  move(amaxy,25);
  byte2str(tmpstr,funk_hr_ptr->order_list[funk_info.sequence_ofs_display]);
  addstr(tmpstr);
}

/***************************************************************************
*0         1         2         3         4         5         6         7
*012345678901234567890123456789012345678901234567890123456789012345678901234567
* Name                Volume             Ctrl Sys     Note Sys     Vol Sys
* xxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxx   xxxxxxxx XX  xxxxxxxx XX  xxxxxxxx XX
***************************************************************************/
void trac_pty_dis(void)
{
  register int y;

  display_topbar();
  for(y = 0;y < (no_active_channels + 1);y++)
  {
    move(y + 2, 0); addch(' ');
    move(y + 2,20); addch(' ');
    move(y + 2,37); addstr("   ");
    move(y + 2,51); addstr("  ");
    move(y + 2,64); addstr("  ");
    move(y + 2,77); addstr("  ");
  }
  clear_area((no_active_channels + 3),0,(no_active_channels + 3),78);
  move(2,1);
  addstr("Name               ");
  move(2,21);
  addstr("Volume          ");
  move(2,40);
  addstr("Ctrl System");
  move(2,53);
  addstr("Note System");
  move(2,66);
  addstr("Vol System ");
  pe_dis_seqpat_no_bg();
  trac_dis_seqpatno();
  update_screen();
}

void trac_pty_discmd(int cmd,int value)
{
  char tmp[] = " 00";

  if(cmd == 0xf)
    addstr("         00");
  else
  {
    if(cmd == 0xe)
    {
      register int cmdo = value >> 4;
      register int cmdov = value & 0xf;
      if(cmdo == 0)
      {
        addstr(cmdo0_dis_labels[cmdov]);
        value = 0;
      }
      else
      {
        addstr(cmdo_dis_labels[cmdo]);
        value = cmdov;
      }
    }
    else
      addstr(cmd_dis_labels[cmd]);

    tmp[1] = nibble_display[value >> 4];
    tmp[2] = nibble_display[value & 0xf];
    addstr(tmp);
  }
}

void trac_pty(void)
{
  register int x;
  char vol_bar[17];

  for(x = 0;x < no_active_channels;x++)
  {
    register int a,adjv = funk_chan[x].rvolume >> 4;

    move(3 + x,1);
    addnstr(funk_hr_ptr->funk_sb[funk_chan[x].sample].sname,19);
    move(3 + x,21);
    if(adjv == 0)
      addstr("                ");
    else
    {
      for(a = 0;a <= 16;a++)
        if(a < (adjv + 1))
          vol_bar[a] = FC_TRAC_VOLBAR;
        else
          vol_bar[a] = ' ';
      addnstr(vol_bar,16);
    }
    move(3 + x,40);
    trac_pty_discmd(funk_chan[x].command,funk_chan[x].com_val);
    move(3 + x,53);
    trac_pty_discmd(funk_chan[x].note_command,funk_chan[x].note_com_val);
    move(3 + x,66);
    trac_pty_discmd(funk_chan[x].volume_command,funk_chan[x].volume_com_val);
  }
  trac_dis_seqpatno();
  update_screen();
}

/***************************************************************************
*0         1         2         3         4         5         6         7
*012345678901234567890123456789012345678901234567890123456789012345678901234567
* Cmd Sm PSVVRACFDD|Cmd Not Ifrq Adjt Port Real Frq Rel Port|Cmd Bl VL Ad PT RV
*               ^-new echo commands
***************************************************************************/
void trac_reg_dis(void)
{
  register int y;

  display_topbar();
  for(y = 0;y < (no_active_channels + 1);y++)
  {
    move(y + 2, 0); addch('|');
    move(y + 2, 4); addch(' ');
    move(y + 2, 7); addch(' ');
    move(y + 2,18); addch('|');
    move(y + 2,22); addch(' ');
    move(y + 2,26); addch(' ');
    move(y + 2,31); addch(' ');
    move(y + 2,36); addch(' ');
    move(y + 2,41); addch(' ');
    move(y + 2,46); addch(' ');
    move(y + 2,50); addch(' ');
    move(y + 2,54); addch(' ');
    move(y + 2,59); addch('|');
    move(y + 2,63); addch(' ');
    move(y + 2,66); addch(' ');
    move(y + 2,69); addch(' ');
    move(y + 2,72); addch(' ');
    move(y + 2,75); addch(' ');
    move(y + 2,78); addch('|');
  }
  clear_area((no_active_channels + 3),0,(no_active_channels + 3),78);
  move(2,1);
  addstr("Cmd");
  move(2,5);
  addstr("Sm");
  move(2,8);
  addstr("PSVVRACFDD");
  move(2,19);
  addstr("Cmd");
  move(2,23);
  addstr("Not");
  move(2,27);
  addstr("Ifrq");
  move(2,32);
  addstr("Adjt");
  move(2,37);
  addstr("Port");
  move(2,42);
  addstr("Real Frq");
  move(2,51);
  addstr("Rel Port");
  move(2,60);
  addstr("Cmd");
  move(2,64);
  addstr("Bl");
  move(2,67);
  addstr("VL");
  move(2,70);
  addstr("Ad");
  move(2,73);
  addstr("PT");
  move(2,76);
  addstr("RV");
  attroff(A_REVERSE);
  move(1,1);
  addstr("Control System");
  move(1,19);
  addstr("Note System");
  move(1,60);
  addstr("Volume System");
  pe_dis_seqpat_no_bg();
  trac_dis_seqpatno();
  update_screen();
}

/***************************************************************************
*
***************************************************************************/
void dis_reg_com(int com,int val)
{
  char tmp[4];

  if(com != 0xf)
  {
    tmp[0] = 'a' + com;
    tmp[1] = nibble_display[val >> 4];
    tmp[2] = nibble_display[val & 0xf];
    tmp[3] = 0;
    addstr(tmp);
  }
  else
  {
    addstr("---");
  }
}

void trac_reg(void)
{
  register int x;
  char bstr[] = "00";
  char wstr[] = "0000";
  char dstr[] = "00000000";
  char tmpstr[11];

  for(x = 0;x < no_active_channels;x++)
  {
    move(3 + x,1);
    dis_reg_com(funk_chan[x].command,funk_chan[x].com_val);

    byte2str(bstr,funk_chan[x].sample);
    move(3 + x,5);
    addstr(bstr);

    tmpstr[0] = nibble_display[funk_chan[x].port_type];
    tmpstr[1] = nibble_display[funk_chan[x].sample_ofs_parm];
    tmpstr[2] = nibble_display[funk_chan[x].vib_waveform];
    tmpstr[3] = nibble_display[funk_chan[x].vol_vib_waveform];
    tmpstr[4] = nibble_display[funk_chan[x].retrig_limit];
    tmpstr[5] = nibble_display[funk_chan[x].arp_speed];
    tmpstr[6] = nibble_display[chmix[x].funkctrl];
    tmpstr[7] = nibble_display[chmix[x].echo_feedback];
    tmpstr[8] = nibble_display[chmix[x].echo_delay];
    tmpstr[9] = nibble_display[chmix[x].echo_decay];
    tmpstr[10] = 0;
    move(3 + x,8);
    addstr(tmpstr);

    move(3 + x,19);
    dis_reg_com(funk_chan[x].note_command,funk_chan[x].note_com_val);

    move(3 + x,23);
    addstr(note_display[funk_chan[x].note]);

    word2str(wstr,funk_chan[x].ifreq);
    move(3 + x,27);
    addstr(wstr);

    word2str(wstr,funk_chan[x].ifreq_vibrato);
    move(3 + x,32);
    addstr(wstr);

    word2str(wstr,funk_chan[x].ifreq_portdest);
    move(3 + x,37);
    addstr(wstr);

    dword2str(dstr,funk_chan[x].rfreq);
    move(3 + x,42);
    addstr(dstr);

    dword2str(dstr,funk_chan[x].rfreq_portdest);
    move(3 + x,51);
    addstr(dstr);

    move(3 + x,60);
    dis_reg_com(funk_chan[x].volume_command,funk_chan[x].volume_com_val);

    byte2str(bstr,funk_chan[x].balance);
    move(3 + x,64);
    addstr(bstr);

    byte2str(bstr,funk_chan[x].volume);
    move(3 + x,67);
    addstr(bstr);

    byte2str(bstr,funk_chan[x].volume_vibrato);
    move(3 + x,70);
    addstr(bstr);

    byte2str(bstr,funk_chan[x].volume_portdest);
    move(3 + x,73);
    addstr(bstr);

    byte2str(bstr,funk_chan[x].rvolume);
    move(3 + x,76);
    addstr(bstr);
  }
  trac_dis_seqpatno();
  update_screen();
}

/***************************************************************************
*
***************************************************************************/
void trac_pattern(void)
{
  register unsigned char x;

  if(funk_info.pattern_ofs_display != trac_pattern_ofs_old)
  {
    trac_pattern_ofs_old = funk_info.pattern_ofs_display;
    trac_pat_real = funk_info.pattern_ofs_display / trac_len;
    trac_pat_hl = funk_info.pattern_ofs_display % trac_len;
    if((trac_pat_real != trac_pat_real_old) ||
       (funk_info.sequence_ofs_display != trac_sequence_ofs_old))
    {
      trac_pat_real_old = trac_pat_real;
      trac_sequence_ofs_old = funk_info.sequence_ofs_display;
      x = trac_pat_real * trac_len;
/*funk_info.pattern_ofs_display & 0xf0*/
      pe_display_trak(funk_hr_ptr->order_list[funk_info.sequence_ofs_display],0,x);
      pe_display_ind(funk_hr_ptr->order_list[funk_info.sequence_ofs_display],x);
      trac_dis_seqpatno();
    }
    attron(A_REVERSE);
    move(3 + trac_pat_hl,3);
    addch(' ');
    update_screen();
  }
}

/***************************************************************************
* miwi 27/05/96
***************************************************************************/
void play_song(int play_type)
{
  register int amaxy = maxy - 2;

  if(funk_hr_ptr->order_list[se_pos_real + se_pos_hl] == 0xff)
    ferr_message("No. You can't play up to a black sequence entry.");
  else
  {
    switch(play_type)
    {
      case 0:
        pe_display_bg();
        pe_display_all();
        break;
      case 1:
        trac_reg_dis();
        break;
      case 2:
        trac_pty_dis();
        break;
    }
    hz_counter = 0;
    clock_sec = 0;
    clock_min = 0;
    print_watch();
    funk_init_for_play();
    funk_info.trek_status = PLAY;
    move(amaxy,0);
    addstr("Forward Tracking, please wait...");
    update_screen();
    fast_track(se_pos_real + se_pos_hl);
    clear_area(amaxy,0,amaxy,79);
    update_screen();
    trac_len = maxy - 8;
    trac_pat_real_old = 1;
    trac_pat_real = 0;
    trac_pat_hl = 0;
    trac_pat_old = 0xff;
    trac_pattern_ofs_old = 0xff;
    trac_sequence_ofs_old = 0xff;
    nodelay(stdscr,TRUE);
    ch = ERR;
    while(funk_info.trek_status == PLAY)
    {
      virtualmixxer();
      switch(play_type)
      {
        case 0:
          trac_pattern();
          break;
        case 1:
          trac_reg();
          break;
        case 2:
          trac_pty();
          break;
      }
      stop_watch();
      ch = wgetch(stdscr);
      switch(ch)
      {
        case FC_TRAC_PAT:
          pe_display_bg();
          pe_display_all();
          play_type = 0;
          print_watch();
          break;
        case FC_TRAC_DGB:
          trac_reg_dis();
          play_type = 1;
          print_watch();
          break;
        case FC_TRAC_PTY:
          trac_pty_dis();
          play_type = 2;
          print_watch();
          break;
        case ERR:
          break;
        default:
          funk_info.trek_status = STOP;
          break;
      }
    }
    nodelay(stdscr,FALSE);
  }
  ch = 0;
}

