/***************************************************************************
*
* FunktrackerGOLD - By Jason Nunn
*
* Using Curses (ANSI C) NON-preemptive Processing
*
* Snail: 32 Rothdale Road, Moil, Darwin, NT, 0810, Australia
*
* =========================================================
*
* Sample Editor
*
***************************************************************************/
#include <ncurses.h>
#include <unistd.h>
#include <malloc.h>
#pragma pack(1)
#include "everything.h"
#include "funktracker.h"
#include "funkmisc.h"
#pragma pack()
#include "funkgold.h"

/*protos funktracker.c*/
#pragma pack(1)
extern tfunk_hr *funk_hr_ptr;
extern void *funk_sam_ptrs[64];
#pragma pack()

/*protos funkmisc.c*/
extern int ferr_val;
extern char *ferr_messages[];
extern void clear_sam_entry(tfunk_sb *funk_sb);
extern void load_sample(int sam_no,char *filename,double resamf);

/*protos funkgold_main.c*/
extern int ch,maxy;
extern char song_directory[1024];
extern char sample_directory[1024];
extern char song_filename[256];
extern char sam_filename[256];
extern unsigned char sam_fpos_real;
extern unsigned char sam_fpos_hl;
extern unsigned char sam_fpos_hl_old;

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 dword2str(char *str,unsigned long value);
extern int hex2nib(char *str);
extern int hex2char(char *str);
extern long hex2long(char *str);
extern void get_string(char y, char x,char *str,int maxlen);
extern void ferr_message(char *mess_str);
extern void play_slot(int note_value);
extern void main_editor_dis_bg(void);
extern void save_song(int song_type);
extern int quit_confirm(void);

/*protos funkgold_pe.c*/
extern void display_oct_no(void);
extern void dec_oct_no(void);
extern void inc_oct_no(void);
extern unsigned char pe_octave_no;
extern int mus_kb_input(void);

/*protos funkgold_trac.c*/
extern void play_song(int play_type);

/*protos for filefinder*/
extern int get_sam_file_name(
  char *directory,
  unsigned char *fpos_real,
  unsigned char *fpos_hl,
  unsigned char *fpos_hl_old,
  char *filename);

/*locals*/
double resample_factor = 1;
unsigned char sm_pos_real;
unsigned char sm_pos_hl;
unsigned char sm_pos_hl_old;
unsigned char sm_sd_option;

/***************************************************************************
****************************************************************************
****************************************************************************
*
* Sample editor
*
****************************************************************************
****************************************************************************
***************************************************************************/

/***************************************************************************
* miwi 27/05/96
***************************************************************************/
void sm_display_bg(void)
{
  register int y;
  register int amaxy = maxy - 5;

  display_topbar();
  move(1,0);
  attroff(A_REVERSE);
  addstr("Sample Editor");
  attron(A_REVERSE);
  clear_area(2,0,2,79);
  clear_area(amaxy,0,amaxy,79);
  for(y = 0;y < (maxy - 8);y++)
  {
    move(y + 3,0);
    addch(' ');
    move(y + 3,3);
    addch(' ');
    move(y + 3,23);
    addstr("  ");
    move(y + 3,33);
    addstr("  ");
    move(y + 3,43);
    addstr("  ");
    move(y + 3,47);
    addstr("  ");
    move(y + 3,51);
    addstr("  ");
    move(y + 3,59);
    addstr("  ");
    move(y + 3,62);
    addstr("  ");
    move(y + 3,67);
    addstr("  ");
    move(y + 3,72);
    addstr("  ");
    move(y + 3,75);
    addstr("  ");
    move(y + 3,78);
    addstr("  ");
  }
  move(2,1);
  addstr("No");
  move(2,4);
  addstr("Sample Name");
  move(2,25);
  addstr("Length");
  move(2,35);
  addstr("Start");
  move(2,45);
  addstr("Vl");
  move(2,49);
  addstr("Bl");
  move(2,53);
  addstr("Port");
  move(2,61);
  addch('S');
  move(2,64);
  addstr("Vib");
  move(2,69);
  addstr("Trm");
  move(2,74);
  addch('R');
  move(2,77);
  addch('A');

  attroff(A_REVERSE);
  y = strlen(song_filename);
  if(y > 56) y = 56;
  move(1,80 - (6 + y));
  addstr("SONG: ");
  addnstr(song_filename,56);
  amaxy = maxy - 4;
  move(amaxy,0);
  addstr("Last Note:");
  move(amaxy,30);
  addstr("Octave:");
}

/***************************************************************************
*
***************************************************************************/
void sm_hl_bar(unsigned char pl,unsigned char x)
{
  if((sm_pos_hl == pl) && (sm_sd_option == x))
    attron(A_REVERSE);
  else
    attroff(A_REVERSE);
}

void sm_dis_samentry(unsigned char hlpos)
{
  char bstr[] = "  ";
  char dstr[] = "        ";
  char *wf_str[] = {"Sin","Tri","Sqr","Saw","Rdm"};
  register int sam_no = sm_pos_real + hlpos;
 
  attroff(A_REVERSE);
  move(hlpos + 3,1);
  byte2str(bstr,sam_no);
  addstr(bstr);

  sm_hl_bar(hlpos,0);
  move(hlpos + 3,4);
  addnstr(funk_hr_ptr->funk_sb[sam_no].sname,19);

  attroff(A_REVERSE);
  move(hlpos + 3,25);
  dword2str(dstr,funk_hr_ptr->funk_sb[sam_no].length);
  addstr(dstr);

  sm_hl_bar(hlpos,1);
  move(hlpos + 3,35);
  dword2str(dstr,funk_hr_ptr->funk_sb[sam_no].start);
  addstr(dstr);

  sm_hl_bar(hlpos,2);
  move(hlpos + 3,45);
  byte2str(bstr,funk_hr_ptr->funk_sb[sam_no].volume);
  addstr(bstr);

  sm_hl_bar(hlpos,3);
  move(hlpos + 3,49);
  byte2str(bstr,funk_hr_ptr->funk_sb[sam_no].balance);
  addstr(bstr);

  sm_hl_bar(hlpos,4);
  move(hlpos + 3,53);
  if((funk_hr_ptr->funk_sb[sam_no].pt_and_sop >> 4))
    addstr("Linear");
  else
    addstr("Curved");

  sm_hl_bar(hlpos,5);
  move(hlpos + 3,61);
  addch(nibble_display[funk_hr_ptr->funk_sb[sam_no].pt_and_sop & 0xf]);

  sm_hl_bar(hlpos,6);
  move(hlpos + 3,64);
  addstr(wf_str[funk_hr_ptr->funk_sb[sam_no].vv_waveform >> 4]);

  sm_hl_bar(hlpos,7);
  move(hlpos + 3,69);
  addstr(wf_str[funk_hr_ptr->funk_sb[sam_no].vv_waveform & 0xf]);

  sm_hl_bar(hlpos,8);
  move(hlpos + 3,74);
  addch(nibble_display[funk_hr_ptr->funk_sb[sam_no].rl_and_as >> 4]);

  sm_hl_bar(hlpos,9);
  move(hlpos + 3,77);
  addch(nibble_display[funk_hr_ptr->funk_sb[sam_no].rl_and_as & 0xf]);
}

/***************************************************************************
* miwi 27/05/96
***************************************************************************/
void sm_display_all(void)
{
  register int sam_no;

  for(sam_no = 0;sam_no < (maxy - 8);sam_no++)
    sm_dis_samentry(sam_no);
  display_oct_no();
  sm_pos_hl_old = sm_pos_hl;
}

void sm_move_hl(void)
{
  sm_dis_samentry(sm_pos_hl_old);
  sm_dis_samentry(sm_pos_hl);
  sm_pos_hl_old = sm_pos_hl;
}

/***************************************************************************
* miwi 27/05/96
***************************************************************************/
void sm_cursor_up(void)
{
  if(sm_pos_hl > 0)
  {
    sm_pos_hl--;
    sm_move_hl();
    sm_dis_samentry(sm_pos_hl);
    update_screen();
  }
  else
  {
    if(sm_pos_real > 0) sm_pos_real--;
    sm_display_all();
    update_screen();
  }
}

void sm_cursor_down(void)
{
  if(sm_pos_hl < (maxy - 9))
  {
    sm_pos_hl++;
    sm_move_hl();
    sm_dis_samentry(sm_pos_hl);
    update_screen();
  }
  else
  {
    if(sm_pos_real < ((64 + 8) - maxy)) sm_pos_real++;
    sm_display_all();
    update_screen();
  }
}

/***************************************************************************
*
***************************************************************************/
void sm_edit_sd(void)
{
  register long x;
  char str[20] = "";
  register int sam_no = sm_pos_real + sm_pos_hl;

  switch(sm_sd_option)
  {
    case 0:
      get_string(3 + sm_pos_hl,4,str,19);
      for(x = 0;x < 19;x++)
      if(str[x])
        funk_hr_ptr->funk_sb[sam_no].sname[x] = str[x];
      else
        funk_hr_ptr->funk_sb[sam_no].sname[x] = 32;
      break;
    case 1:
      get_string(3 + sm_pos_hl,35,str,8);
      x = hex2long(str);
      if(x != -1) funk_hr_ptr->funk_sb[sam_no].start = x;
      break;
    case 2:
      get_string(3 + sm_pos_hl,45,str,2);
      x = hex2char(str);
      if(x != -1) funk_hr_ptr->funk_sb[sam_no].volume = x;
      break;
    case 3:
      get_string(3 + sm_pos_hl,49,str,2);
      x = hex2char(str);
      if(x != -1) funk_hr_ptr->funk_sb[sam_no].balance = x;
      break;
    case 4:
      funk_hr_ptr->funk_sb[sam_no].pt_and_sop ^= 0x10;
      break;
    case 5:
      get_string(3 + sm_pos_hl,61,str,1);
      x = hex2nib(str);
      if(x != -1)
      {
        funk_hr_ptr->funk_sb[sam_no].pt_and_sop &= 0xf0;
        funk_hr_ptr->funk_sb[sam_no].pt_and_sop |= x & 0xf;
      }
      break;
    case 6:
      x = (funk_hr_ptr->funk_sb[sam_no].vv_waveform >> 4) + 1;
      if(x > 4) x = 0;
      funk_hr_ptr->funk_sb[sam_no].vv_waveform &= 0x0f;
      funk_hr_ptr->funk_sb[sam_no].vv_waveform |= x << 4;
      break;
    case 7:
      x = (funk_hr_ptr->funk_sb[sam_no].vv_waveform & 0xf) + 1;
      if(x > 4) x = 0;
      funk_hr_ptr->funk_sb[sam_no].vv_waveform &= 0xf0;
      funk_hr_ptr->funk_sb[sam_no].vv_waveform |= x & 0xf;
      break;
    case 8:
      get_string(3 + sm_pos_hl,74,str,1);
      x = hex2nib(str);
      if(x != -1)
      {
        funk_hr_ptr->funk_sb[sam_no].rl_and_as &= 0x0f;
        funk_hr_ptr->funk_sb[sam_no].rl_and_as |= x << 4;
      }
      break;
    case 9:
      get_string(3 + sm_pos_hl,77,str,1);
      x = hex2nib(str);
      if(x != -1)
      {
        funk_hr_ptr->funk_sb[sam_no].rl_and_as &= 0xf0;
        funk_hr_ptr->funk_sb[sam_no].rl_and_as |= x & 0xf;
      }
      break;
  }
  sm_dis_samentry(sm_pos_hl);
  update_screen();
  ch = ' ';
}

/***************************************************************************
*
***************************************************************************/
void sm_insert_sam(void)
{
  register int x,sam_no = sm_pos_real + sm_pos_hl;
  for(x = 0x3f;x > sam_no;x--)
  {
    memcpy(&funk_hr_ptr->funk_sb[x],&funk_hr_ptr->funk_sb[x - 1],sizeof(tfunk_sb));
    funk_sam_ptrs[x] = funk_sam_ptrs[x - 1];
  }
  clear_sam_entry(&funk_hr_ptr->funk_sb[x]);
  free(funk_sam_ptrs[x]);
  funk_sam_ptrs[x] = NULL;
  sm_display_all();
  update_screen();
}

void sm_delete_sam(void)
{
  register int x,sam_no = sm_pos_real + sm_pos_hl;
  free(funk_sam_ptrs[sam_no]);
  for(x = sam_no;x < 0x3f;x++)
  {
    memcpy(&funk_hr_ptr->funk_sb[x],&funk_hr_ptr->funk_sb[x + 1],sizeof(tfunk_sb));
    funk_sam_ptrs[x] = funk_sam_ptrs[x + 1];
  }
  clear_sam_entry(&funk_hr_ptr->funk_sb[0x3f]);
  funk_sam_ptrs[0x3f] = NULL;
  sm_display_all();
  update_screen();
}

/***************************************************************************
*
***************************************************************************/
void sm_main(void)
{
  register int nv;

  switch(ch)
  {
    case FC_GOTO_SEQ_EDIT:
      edit_mode = EM_SEQUENCE;
      main_editor_dis_bg();
      break;
    case FC_GOTO_PAT_EDIT:
      edit_mode = EM_PATTERN;
      main_editor_dis_bg();
      break;
    case FC_SCREEN_ESCAPE:
      if(quit_confirm()) edit_mode = EM_MAIN;
      break;
    case FC_TRAC_PAT:
      play_song(0);
      main_editor_dis_bg();
      break;
    case FC_TRAC_DGB:
      play_song(1);
      main_editor_dis_bg();
      break;
    case FC_TRAC_PTY:
      play_song(2);
      main_editor_dis_bg();
      break;
    case FC_SAVE_SONG:
      save_song(1);
      break;
    case FC_SAVE_MOD:
      save_song(0);
      break;
    case FC_SM_ED_ATTR:
      sm_edit_sd();
      break;
    case FC_ARROW_UP:
      sm_cursor_up();
      break;
    case FC_ARROW_DN:
      sm_cursor_down();
      break;
    case FC_ARROW_LEFT:
      if(sm_sd_option > 0) sm_sd_option--;
      sm_dis_samentry(sm_pos_hl);
      update_screen();
      break;
    case FC_ARROW_RIGHT:
      if(sm_sd_option < 9) sm_sd_option++;
      sm_dis_samentry(sm_pos_hl);
      update_screen();
      break;
    case FC_SM_LOAD_SAM:
      if(get_sam_file_name(sample_directory,&sam_fpos_real,&sam_fpos_hl,
        &sam_fpos_hl_old,sam_filename) != -1)
      {
        if(chdir(sample_directory) == -1)
          ferr_message("Couldn't locate sample directory.");
        else
        {
          load_sample(sm_pos_real + sm_pos_hl,sam_filename,resample_factor);
          if(ferr_val != FERR_OK)
            ferr_message(ferr_messages[ferr_val]);
        }
      }
      main_editor_dis_bg();
      break;
    case FC_DEL_ENTRY:
      sm_delete_sam();
      break;
    case FC_INS_ENTRY:
      sm_insert_sam();
      break;
    case FC_DEC_OCT:
      dec_oct_no();
      break;
    case FC_INC_OCT:
      inc_oct_no();
      break;
  }
  if((nv = mus_kb_input()) != -1)
  {
    register int period = nv + (pe_octave_no * 12);
    move((maxy - 4),10);
    addstr(note_display[period]);
    update_screen();
    play_slot(period);
  }
}
