/***************************************************************************
*
* FunktrackerGOLD - By Jason Nunn
*
* Using Curses (ANSI C) NON-preemptive Processing
*
* Snail: 32 Rothdale Road, Moil, Darwin, NT, 0810, Australia
*
* ===========================================
*
* File Finder routines
*
***************************************************************************/
#include <ncurses.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#pragma pack(1)
#include "everything.h"
#include "funktracker.h"
#include "funkmisc.h"
#pragma pack()
#include "fickle.h"

/*protos dsp_mixxer.c*/
extern int sample_precision;

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

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

/*protos funkgold_main.c*/
extern int ch,maxy;
extern void display_topbar(void);
extern void update_screen(void);
extern void clear_area(char y1,char x1,char y2,char x2);
extern void get_string(char y, char x,char *str,int maxlen);
extern void ferr_message(char *mess_str);

/*protos funkgold_sm.c*/
extern double resample_factor;
extern unsigned char sm_pos_real;
extern unsigned char sm_pos_hl;

/*protos funkgold_pe.c*/
extern void play_slot(int note_value);
extern unsigned char pe_octave_no;
extern int mus_kb_input(void);

/*locals*/
typedef struct _tdir_entry
{
  char *filename;
#ifdef Linux
  umode_t mode;
#elif FreeBSD
  mode_t mode;
#endif
} tdir_entry;

tdir_entry *dir_entry_table;
int no_dir_entries;
int filefind_maxd;

int file_type;

#define FILE_TYPE_SONG 0
#define FILE_TYPE_SAM 1

int resample_indice = 10000000;
unsigned char *file_pos_real;
unsigned char *file_pos_hl;
unsigned char *file_pos_hl_old;

/***************************************************************************
*
***************************************************************************/
void print_resample_factor(void)
{
  char tmpstr[20];

  sprintf(tmpstr,"%f",resample_factor);
  clear_area(8,38,8,60);
  move(8,38);
  addstr(tmpstr);
  move(8,38 + 12);
  sprintf(tmpstr,"(x%d)",resample_indice);
  addstr(tmpstr);
}

void get_resample_params(void)
{
  register int resam_option = 1;

  attron(A_REVERSE);
  clear_area(6,18,10,62);
  attroff(A_REVERSE);
  clear_area(7,19,9,61);
  move(8,20);
  addstr("Resample factor: ");
  print_resample_factor();
  while(resam_option)
  {
    ch = getch();
    switch(ch)
    {
      case FC_ENTER:
        resam_option = 0;
        break;
      case FC_ARROW_UP:
        resample_factor -= 0.00000001 * resample_indice;
        if(resample_factor < 0.2) resample_factor = 0.2;
        print_resample_factor();
        break;
      case FC_ARROW_DN:
        resample_factor += 0.00000001 * resample_indice;
        if(resample_factor > 5) resample_factor = 5;
        print_resample_factor();
        break;
      case FC_ARROW_LEFT:
          if(resample_indice > 10) resample_indice /= 10;
          print_resample_factor();
        break;
      case FC_ARROW_RIGHT:
          if(resample_indice < 10000000) resample_indice *= 10;
          print_resample_factor();
        break;
    }
  }
}

/***************************************************************************
*
***************************************************************************/
int compar(const void *file1,const void *file2)
{
  return strcmp(
    (*(tdir_entry *)file1).filename,
    (*(tdir_entry *)file2).filename);
}

int wildcard(tdir_entry *entry)
{
  if(entry->mode & S_IFDIR) return 1;
  if(entry->mode & S_IFREG)
    if((*entry).filename[0] == '.') return 0;
  if(file_type == FILE_TYPE_SONG)
  {
    if(strstr((*entry).filename,".Funk") != NULL) return 1;
    if(strstr((*entry).filename,".fnk") != NULL) return 1;
    if(strstr((*entry).filename,".mod") != NULL) return 1;
  }
  else
  {
    if(strstr((*entry).filename,".wav") != NULL) return 1;
    if(strstr((*entry).filename,".snd") != NULL) return 1;
    if(strstr((*entry).filename,".raw") != NULL) return 1;
  }
  return 0;
}

/***************************************************************************
*
***************************************************************************/
void *add_slash(char *dirname)
{
  char *adjst_path;

  adjst_path = malloc(strlen(dirname) + 2);
  if(adjst_path != NULL)
  {
    strcpy(adjst_path,dirname);
    if(!((strlen(dirname) == 1) && (dirname[0] == '/')))
      strcat(adjst_path,"/");
  }
  return adjst_path;
}

int load_directory(char *dirname,tdir_entry *table,int dis)
{
  DIR *dp;
  struct dirent *dir_entry;
  struct stat statbuf;
  register int entry_no = 0;
  char *fullpath_str;
  char *adjst_path;

  if(dis)
  {
    attron(A_REVERSE);
    move(3,0);
    addstr("Please Wait... ");
    update_screen();
  }
  adjst_path = add_slash(dirname);
  if(adjst_path == NULL) return -1;
  if((dp = opendir(dirname)) == NULL) return -1;
  while(((dir_entry = readdir(dp)) != NULL) && (entry_no < MAX_DIR_ENTRIES))
  {
    fullpath_str = malloc(strlen(dir_entry->d_name) + strlen(adjst_path) + 1);
    if(fullpath_str != NULL)
    {
      strcpy(fullpath_str,adjst_path);
      strcat(fullpath_str,dir_entry->d_name);
      if(lstat(fullpath_str,&statbuf) != -1)
      {
        (table + entry_no)->filename = malloc(strlen(dir_entry->d_name) + 1);
        if((table + entry_no)->filename != NULL)
        {
          strcpy((table + entry_no)->filename,dir_entry->d_name);
          (table + entry_no)->mode = statbuf.st_mode;
          if(wildcard(table + entry_no))
            entry_no++;
        }
      }
      free(fullpath_str);
    }
  }
  closedir(dp);
  free(adjst_path);
  qsort(table,entry_no,sizeof(tdir_entry),compar);
  return entry_no;
}

void unload_directory(tdir_entry *table)
{
  int x;

  for(x = 0;x < no_dir_entries;x++)
  {
    free(table[x].filename);
    table[x].filename = NULL;
  }
}

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

  display_topbar();
  for(y = 0;y < (maxy - 8);y++)
  {
    move(y + 5,0);
    addch(' ');
    move(y + 5,56);
    addch(' ');
  }
  clear_area(4,0,4,56);
  clear_area(amaxy,0,amaxy,56);
  attroff(A_REVERSE);
  move(1,0);
  if(file_type == FILE_TYPE_SONG)
    addstr("Song Loader (*.Funk, *.fnk, *.mod");
  else
  {
    addstr("Sample Loader (");
    if(sample_precision == 8)
      addstr("8 bit");
    else
      addstr("16 bit");
    addstr(" *.snd, *.raw, *.wav");
  }
  addstr(" files)");
  move(4,59);
  addstr("Hit enter to load");
  move(5,59);
  addstr("file.");
  move(7,59);
  addstr("Hit 'R' to rename");
  move(8,59);
  addstr("file.");
  if(file_type == FILE_TYPE_SAM)
  {
    move(10,59);
    addstr("Use the usual note");
    move(11,59);
    addstr("keys to listen to");
    move(12,59);
    addstr("the sample before");
    move(13,59);
    addstr("loading it.");
  }
}

/***************************************************************************
*
***************************************************************************/
void filefind_disline(unsigned char pos_hl)
{
  register int x,ad = 55;
  char tmpstr[55];

  move(pos_hl + 5,1);
  if(pos_hl < filefind_maxd)
  {
    strncpy(tmpstr,(dir_entry_table + *file_pos_real + pos_hl)->filename,55);
    addstr(tmpstr);
    ad -= strlen(tmpstr);
    if((dir_entry_table + *file_pos_real + pos_hl)->mode & S_IFDIR)
    { 
      addch('/');
      ad--;
    }
  }
  for(x = 0;x < ad;x++) addch(' ');
}

void filefind_dis_path(char *directory)
{
  attroff(A_REVERSE);
  move(3,0);
  clear_area(3,0,3,79);
  move(3,0);
  addnstr(directory,80);
}

/*miwi 27/05/96*/
void filefind_dis_all(void)
{
  unsigned char pos_hl;
  attroff(A_REVERSE);
  for(pos_hl = 0;pos_hl < (maxy - 9);pos_hl++) filefind_disline(pos_hl);
  attron(A_REVERSE);
  filefind_disline(*file_pos_hl);
  *file_pos_hl_old = *file_pos_hl;
}

void filefind_move_hl(void)
{
  attroff(A_REVERSE);
  filefind_disline(*file_pos_hl_old);
  attron(A_REVERSE);
  filefind_disline(*file_pos_hl);
  *file_pos_hl_old = *file_pos_hl;
}

/***************************************************************************
*
***************************************************************************/
void file_cursor_up(void)
{
  if(*file_pos_hl > 0)
  {
    (*file_pos_hl)--;
    filefind_move_hl();
    filefind_disline(*file_pos_hl);
    update_screen();
  }
  else
  {
    if(*file_pos_real > 0) (*file_pos_real)--;
    filefind_dis_all();
    update_screen();
  }
}

/*miwi 27/05/96*/
void file_cursor_down(void)
{
  if(*file_pos_hl < (filefind_maxd - 1))
  {
    (*file_pos_hl)++;
    filefind_move_hl();
    filefind_disline(*file_pos_hl);
    update_screen();
  }
  else
  {
    if(no_dir_entries > filefind_maxd)
    {
      if(*file_pos_real < (no_dir_entries - (maxy - 9))) (*file_pos_real)++;
      filefind_dis_all();
      update_screen();
    }
  }
}

/***************************************************************************
* miwi 27/05/96
***************************************************************************/
int get_file_name(
  char *directory,
  unsigned char *fpos_real,
  unsigned char *fpos_hl,
  unsigned char *fpos_hl_old,
  char *filename)
{
  register char option = 0;
  char *adjst_path = NULL,*adjst2_path = NULL;
  char new_name[56];
  register int nv;
  register int amaxy = maxy - 9;

  dir_entry_table = malloc(MAX_DIR_ENTRIES * sizeof(tdir_entry));
  if(dir_entry_table == NULL) return -1;
  file_pos_real = fpos_real;
  file_pos_hl = fpos_hl;
  file_pos_hl_old = fpos_hl_old;

  if(chdir(directory) == -1) return -1;
  if((no_dir_entries = load_directory(".",dir_entry_table,0)) != -1)
  {
    filefind_maxd = no_dir_entries;
    if(filefind_maxd > amaxy) filefind_maxd = amaxy;
    filefind_dis_bg();
    filefind_dis_all();
    filefind_dis_path(directory);
    update_screen();
    while(option == 0)
    {
      ch = getch();
      switch(ch)
      {
        case FC_SCREEN_ESCAPE:
          option = -1;
          break;
        case FC_ARROW_UP:
          file_cursor_up();
          break;
        case FC_ARROW_DN:
          file_cursor_down();
          break;
        case FC_DIR_RENAME:
          get_string(5 + *file_pos_hl,1,new_name,55);
          if(rename((dir_entry_table + *file_pos_real + *file_pos_hl)->filename,
            new_name) != -1)
          {
            unload_directory(dir_entry_table);
            if((no_dir_entries = load_directory(".",dir_entry_table,1)) != -1)
            {
              filefind_maxd = no_dir_entries;
              if(filefind_maxd > amaxy) filefind_maxd = amaxy;
            }
          }
          filefind_dis_all();
          filefind_dis_path(directory);
          update_screen();
          break;
        case FC_ENTER:
          if((dir_entry_table + *file_pos_real + *file_pos_hl)->mode & S_IFDIR)
          {
            adjst_path = add_slash(directory);
            if(adjst_path != NULL)
            {
              adjst2_path = malloc(
                strlen(adjst_path) +
                strlen((dir_entry_table + *file_pos_real + *file_pos_hl)->filename) + 1);
              if(adjst2_path != NULL)
              {
                strcpy(adjst2_path,adjst_path);
                free(adjst_path);
                strcat(
                  adjst2_path,
                  (dir_entry_table + *file_pos_real + *file_pos_hl)->filename);
                if(chdir(adjst2_path) != -1)
                {
                  getcwd(directory,1024);
                  unload_directory(dir_entry_table);
                  if((no_dir_entries = load_directory(".",dir_entry_table,1)) != -1)
                  {
                    *file_pos_real = 0;
                    *file_pos_hl = 0;
                    *file_pos_hl_old = 0;
                    filefind_maxd = no_dir_entries;
                    if(filefind_maxd > amaxy) filefind_maxd = amaxy;
                    filefind_dis_all();
                    filefind_dis_path(directory);
                    update_screen();
                  }
                  else
                    ferr_message("Can't open directory.");
                }
                free(adjst2_path);
              }
            }
          }
          else
          {
            if(file_type == FILE_TYPE_SAM)
              get_resample_params();
            strcpy(filename,(dir_entry_table + *file_pos_real + *file_pos_hl)->filename);
            option = 1;
          }
          break;
      }
      if((file_type == FILE_TYPE_SAM) && ((nv = mus_kb_input()) != -1))
      {
        register int period = nv + (pe_octave_no * 12);
#pragma pack(1)
        tfunk_sb tmp_funk_sb;
        void *tmp_funk_sam_ptrs;
#pragma pack()

        memcpy(&tmp_funk_sb,
          &funk_hr_ptr->funk_sb[sm_pos_real + sm_pos_hl],sizeof(tfunk_sb));
        tmp_funk_sam_ptrs = funk_sam_ptrs[sm_pos_real + sm_pos_hl];
        load_sample(sm_pos_real + sm_pos_hl,
          (dir_entry_table + *file_pos_real + *file_pos_hl)->filename,1);
        if(ferr_val != FERR_OK)
          ferr_message(ferr_messages[ferr_val]);
        play_slot(period);
        free(funk_sam_ptrs[sm_pos_real + sm_pos_hl]);
        memcpy(&funk_hr_ptr->funk_sb[sm_pos_real + sm_pos_hl],
          &tmp_funk_sb,sizeof(tfunk_sb));
        funk_sam_ptrs[sm_pos_real + sm_pos_hl] = tmp_funk_sam_ptrs;
      }
    }
    unload_directory(dir_entry_table);
  }
  else
    option = -1;
  free(dir_entry_table);
  return option;
}

/***************************************************************************
*
***************************************************************************/
int get_song_file_name(
  char *directory,
  unsigned char *fpos_real,
  unsigned char *fpos_hl,
  unsigned char *fpos_hl_old,
  char *filename)
{
 file_type = FILE_TYPE_SONG;
  return get_file_name(
    directory,fpos_real,fpos_hl,fpos_hl_old,filename);
}

/***************************************************************************
*
***************************************************************************/
int get_sam_file_name(
  char *directory,
  unsigned char *fpos_real,
  unsigned char *fpos_hl,
  unsigned char *fpos_hl_old,
  char *filename)
{
  file_type = FILE_TYPE_SAM;
  return get_file_name(
    directory,fpos_real,fpos_hl,fpos_hl_old,filename);
}
