#ifdef RAINE_WIN32
#include <windows.h> // GetLogicalDrives
#endif
#include "raine.h"
#include "../gui/menu.h"
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>


class TFileSel : public TMenu
{
  protected:
    char path[1024];
    char *res_file;
    int nb_files;
    char **ext;
  public:
    TFileSel(char *my_title, char *mypath, char **myext, char *res_str);
    virtual ~TFileSel();
    virtual void compute_nb_items();
    virtual int get_fgcolor(int n);
    int can_be_selected(int n) {
      return can_be_displayed(n);
    }
    virtual void exec_menu_item();
    virtual void set_dir(char *mypath);
};

static int selected_path;
static TFileSel *dlg;

static int sel_path(int sel) {
  selected_path = sel;
  return 1;
}

class path_sel {
  private:
    menu_item_t *menu;
    int nb,max;
  public:
    path_sel() {
      menu = NULL;
      nb = max = 0;
    }
    ~path_sel() {
      if (menu) {
	for(nb--; nb>= 0; nb--)
	  free((void*)menu[nb].label);
	free(menu);
      }
    }
    void add_item(char *path) {
      if (!path)
	return;
      if (nb == max) {
	max += 10;
	menu = (menu_item_t*)realloc(menu,(max+1)*sizeof(menu_item_t));
	memset(&menu[nb],0,(11)*sizeof(menu_item_t));
      }
      menu[nb].label = strdup(path);
      menu[nb].menu_func = sel_path;
      nb++;
    }
    menu_item_t *get_menu() {
      return menu;
    }
};

static void do_paths() {
  path_sel *paths = new path_sel();
  paths->add_item(getenv("HOME"));
#ifdef RAINE_UNIX
  FILE *f = fopen("/etc/mtab","r");
  if (f) {
    char buff[2048];
    while (!feof(f)) {
      fgets(buff,2048,f);
      if (strncmp(buff,"/dev/",5)) // keep only the /dev entries
	continue;
      char *s1 = strchr(buff,' ');
      if (!s1) continue;
      char *s2 = strchr(s1+1,' ');
      if (!s2) continue;
      *s2 = 0;
      paths->add_item(s1+1);
    }
    fclose(f);
  }
#else
  // windows
  paths->add_item(getenv("PROGRAMFILES"));
  paths->add_item(getenv("USERPROFILE"));
  int drives = GetLogicalDrives();
  int n;
  char path[3];
  strcpy(path,"x:");
  for (n=0; n<32; n++) {
    if (drives & (1<<n)) {
      path[0]=65+n;
      paths->add_item(path);
    }
  }
#endif
  selected_path = -1;
  TMenu *menu = new TMenu((char*)"Path selection",paths->get_menu());
  menu->execute();
  if (selected_path >= 0)
    dlg->set_dir((char*)paths->get_menu()[selected_path].label);
  delete menu;
  delete paths;
}

static int sort_menu(const void *a, const void *b) {
  return stricmp(((menu_item_t *)a)->label, ((menu_item_t *)b)->label);
}

TFileSel::TFileSel(char *my_title, char *mypath, char **myext, char *res_str) :
  TMenu(my_title,NULL)
{
  char *s;
  strcpy(path,mypath);
  int l = strlen(path);
  if (path[l-1] == SLASH[0])
    path[l-1] = 0;

  if ( !strchr(path,SLASH[0])) {
    strcpy(path,dir_cfg.exe_path);
    path[strlen(path)-1] = 0;
  }
  ext = myext;

  // First check if the path exists, and if not take the 1st one which does
  struct stat buf;
  int res;
  do {
    res = stat(path,&buf);
    if (res < 0) { // does not exist
      s = strrchr(path,SLASH[0]);
      if (s)
	*s = 0;
      else {
	strcat(path,SLASH);
	res = 0;
      }
    }
  } while (res < 0);
  title = path;
  res_file = res_str;
  strcpy(res_file,path);
  strcat(res_file,SLASH);
}

void TFileSel::compute_nb_items() {
  DIR *dir = opendir(path);
  int nb_menu = 10;
  char cwd[1024];
  char tmp_path[1024];
  if (!dir) {
    perror(path);
  } else {
    getcwd(cwd,1024);
    if (menu)
      free(menu);
    menu = (menu_item_t *)malloc(sizeof(menu_item_t)*(nb_menu+1));
    memset(menu,0,sizeof(menu_item_t)*(nb_menu+1));
    menu[0].label = "-- Path...";

    menu[1].label = "..";
    // values_list_size is used here to know if the entry is a directory or not
    // it needs to be linked to menu because of the call to qsort further
    menu[1].values_list_size = 1;
    nb_files = 2;
    struct dirent *dent;
    while ((dent = readdir(dir))) {
      if (!strcmp(dent->d_name,".") || !strcmp(dent->d_name,".."))
	continue;
      menu[nb_files].label = strdup(dent->d_name);
      sprintf(tmp_path,"%s%s%s",path,SLASH,dent->d_name);
      /* The big stupidity of readdir is that it doesn't set d_type (posix
       * compliant !!!).  */
      struct stat buf;
      if (!stat(tmp_path,&buf) && S_ISDIR(buf.st_mode)) {
	menu[nb_files].values_list_size = 1;
	nb_files++;
      } else {
	menu[nb_files].values_list_size = 0;
	int found = 0,idx;
	char *s;
	for (s=ext[0], idx=1; s; s=ext[idx++]) {
	  int l = strlen(s);
	  if (!stricmp(&dent->d_name[strlen(dent->d_name)-(l)],s)) {
	    found = 1;
	    break;
	  }
	}
	if (!found) {
	  // doesn't match the extension given
	  free((void*)menu[nb_files].label);
	} else
	  nb_files++;
      }

      if (nb_files == nb_menu) {
	nb_menu += 10;
	menu = (menu_item_t *)realloc(menu,sizeof(menu_item_t)*(nb_menu+1));
	memset(&menu[nb_files],0,sizeof(menu_item_t)*11);
	if (!menu) {
	  fprintf(stderr,"failed to realloc files buffer (%d entries)\n",nb_menu);
	  exit(1);
	}
      }
    }
    menu[nb_files].label = NULL;
    closedir(dir);
    qsort(&menu[2],nb_files-2,sizeof(menu_item_t),&sort_menu);
    chdir(cwd);
  }
  TMenu::compute_nb_items();
}

TFileSel::~TFileSel() {
  int nb;
  for (nb=2; nb<nb_files; nb++)
    free((void*)menu[nb].label);
  if (menu)
    free(menu);
}

int TFileSel::get_fgcolor(int n) {
  if (menu[n].values_list_size)
    return mymakecol(160,160,255);
  return fg;
}

void TFileSel::set_dir(char *mypath) {
  if (mypath != path)
    strcpy(path,mypath);
  if (!strstr(path,SLASH)) {
    chdir(path);
    getcwd(path,1024);
  }
  if (font) {
    delete font;
    font = NULL;
  }
  set_title(path);
  draw();
  strcpy(res_file,path);
  strcat(res_file,SLASH); // To show it's a path more easily
}

void TFileSel::exec_menu_item() {
  if (menu[sel].values_list_size) {
    if (sel == 1) { // ..
      char *s = strrchr(path,SLASH[0]);
      if (s) {
	*s = 0;
#ifdef RAINE_WIN32
	if (s[-1] == SLASH[0]) { s[-1] = 0; // double \ in windows
	  printf("double replace for %s\n",path);
	}
#endif
      }
#ifdef RAINE_WIN32
      if (strlen(path)==2) // only the drive letter is left
	strcat(path,SLASH);
#else
      if (!*path)
	strcpy(path,SLASH);
#endif
    } else
      sprintf(&path[strlen(path)],"%s%s",SLASH,menu[sel].label);
    set_dir(path);
  } else if (sel == 0) { // options
    do_paths();
  } else {
    exit_menu = 1;
    sprintf(res_file,"%s%s%s",path,SLASH,menu[sel].label);
  }
}
      
void fsel(char *mypath, char **ext, char *res_str) {
  dlg = new TFileSel(mypath,mypath,ext,res_str);
  dlg->execute();
  delete dlg;
}


