/*
 * This file contains a function, get_dir_list() that will get a directory
 * listing and put all the names into a table (an array of char *'s table)
 * and return it to you (along with the number of entries in the directory
 * if desired). The table is NULL terminated (i.e. the entry after the last
 * one is a NULL), and is just like an argv style array.
 *
 * If an error occurs (memory allocation, bad directory name, etc), you
 * will get a NULL back.
 *
 * Each entry of the returned table points to a NULL-terminated string
 * that contains the name of a file in the directory.  If the name is
 * a directory, it has a slash appended to it.
 *
 * When you are done with the table, you should free it manually, or you
 * can call the function free_table() provided down below.
 *
 * See the sample main() down below for how to use it.  Ignore (or steal
 * if you'd like) the other code that isn't particularly relevant (i.e.
 * the support functions).
 * 
 *   Dominic Giampaolo
 *   dbg@sgi.com
 */

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <dirent.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <X11/keysymdef.h>
#include <X11/keysym.h>

#include "xrmap.h"
#include "numdefs.h"

extern Display * dpy;
extern int scr;
extern GC gc;
extern Window cmdwin, explwin;
extern int char_width;
extern XFontStruct * xfont[6];
extern int fonth[6], fontasc[6];
extern unsigned int explwin_width0, explwin_width, explwin_height, explwin_on;
extern int explwin_x, explwin_y;
extern Pixmap explpix;
extern int expl_table_entries;
extern int expl_lines;
extern int expl_shift;
extern int expl_output;
extern int caret_pos;
extern int line_clicked;
extern char expl_currdir[256];
extern char *expl_implicit;
extern char **expl_dirtable;

extern char *file_filter;
extern char *cmd_string;
extern char **msg;

extern char *editor_cmd, *im_viewer, *ps_viewer, *svg_viewer,
            *html_viewer, *midi_cmd;
extern char *prog_cmd[7];

extern void 
draw_win_string(ImageLayout *scene, Window win, char *s, int position);
extern void do_activate(ImageLayout * scene, KeySym keysym);

void free_table(char **table, int n)
{
  char **orig = table;

  for (; n > 0; n--, table++) {
     if (*table)
        free(*table);
  }

  free(orig);
}

void free_dirlist(char **table)
{
  char **orig = table;

  while(*table) {
     free(*table);
     table++;
  }

  free(orig);
}

int
dup_strcmp(char **a, char **b)
{
   return strcmp(*a, *b);
}

char *get_file_name(struct dirent *d)
{
  struct stat s;
  char *name;

  if (d == NULL) {
     fprintf(stderr, "BUG (got a NULL in get_file_name()).\n");
     return NULL;
  }

  if (stat(d->d_name, &s) < 0) {
     perror(d->d_name);
     return NULL;
  }

  if (S_ISDIR(s.st_mode)) {
     name = (char *)malloc(strlen(d->d_name)+2);
     if (name == NULL)
       return NULL;
     sprintf(name, "%s/", d->d_name);
  } else {
     name = (char *)strdup(d->d_name);
  }

  return name;
}

int file_filter_check(char *scanit)
{
struct stat buf;

   if (scanit == NULL) return 1;
   if (!strcmp(scanit, "./")) return 1;

   stat(scanit, &buf);
   if (S_ISDIR(buf.st_mode)) return 0;

   if (file_filter==NULL) return 0;
   if (*file_filter==0) return 0;
  
   if (strstr(scanit, file_filter)) 
      return 0;
   else
      return 1;
}

#define CHUNK 100

char **get_dir_list(char *dirname, int *num_entries)
{
   int i,size=CHUNK;
   char **table, old_dir[MAXPATHLEN];
   DIR  *dir;
   struct dirent *dirent;
 
   getcwd(old_dir, MAXPATHLEN);
   if (dirname && chdir(dirname) < 0)
      return NULL;
   
   dir = opendir(".");
   if (dir == NULL) {
      chdir(old_dir);
      return NULL;
   }
   
   table = (char **)calloc(size, sizeof(char *));
   if (table == NULL) {
      closedir(dir);
      chdir(old_dir);
      return NULL;
   }
 
   dirent = NULL;   i = 0;
   for (dirent = readdir(dir); dirent != NULL; dirent = readdir(dir)) {
      table[i] = get_file_name(dirent);
 
      if (file_filter_check(table[i])) continue;
          /* continue if table[i] is void or doesn't match the filter */
 
      i++;
      if (i == size) {
  	 char **table2;
 
 	 size *= 2;
 	 table2 = (char **)realloc(table, size * sizeof(char *));
 	 if (table2 == NULL) {
 	    free_table(table, i);
 	    closedir(dir);
 	    chdir(old_dir);
 	    return NULL;
 	 }
 
 	 table = table2;
      }
   }
 
   table[i] = NULL;    /* make sure the table ends with a NULL */
 
   if (num_entries)
      *num_entries = i;
   
   closedir(dir);
   chdir(old_dir);
   
   return table;
}

void show_explwin(ImageLayout *scene, int x, int y)
{
   static int expl_shift_old = -1;
   int i, j, b, d, w, p, h, ht, h_skip, hp_skip, char_space;
   int activate;
   char *s;
   char *banner[4] = { "home", "share", "  /", "  ."};
   XSizeHints xsh;

   activate = 0;

   if (!explwin_on) {
      activate = 1;
      expl_shift = 0;
      explwin_on = 1;
      if (explwin_x>-100000) {
         xsh.flags = USPosition | PPosition;
         xsh.x = explwin_x;
         xsh.y = explwin_y;
	 XSetWMNormalHints(dpy, explwin, &xsh);
      }
   }
   XMapRaised(dpy, explwin);

   d = 3*char_width/2;
   char_space = char_width * 4;
   h_skip = fonth[0]+8;
   XSetFont(dpy, gc, xfont[0]->fid);

   if (y == 0) {
      activate = 1;
      XClearArea(dpy, explwin, 0, 0, explwin_width, h_skip, False);
      w = 8 * char_space;
      XSetForeground(dpy, gc, scene->color[C_BG_TEXT].pix);
      XFillRectangle(dpy, explwin, gc, 0, 0,  w, h_skip);
      w = explwin_width - 2*char_space;
      XSetForeground(dpy, gc, scene->color[C_BG_ESCAPE].pix);
      XFillRectangle(dpy, explwin, gc,
                     w, 0,  explwin_width, h_skip);

      XSetForeground(dpy, gc, scene->color[C_FG_TEXT].pix);
      for (i=0; i<=4; i++) {
         w = 2*i*char_space;
	 s = banner[i];
	 if (i<=3)
            XDrawString(dpy, explwin, gc, d+w, fontasc[0]+4, s, strlen(s));
	 if (i>=1)
            XDrawLine(dpy, explwin, gc, w, 0, w, h_skip);
      }

      w = explwin_width - 2*char_space;
      s = msg[ESCAPE];
      XDrawString(dpy, explwin, gc, d+w, fontasc[0]+4, s, strlen(s));
      XDrawLine(dpy, explwin, gc, w, 0, w, h_skip);
      XDrawLine(dpy, explwin, gc, 0, h_skip, explwin_width, h_skip);
   }

 parse_again:
   if (y <= 1) {
      activate = 1;
      XClearArea(dpy, explwin,  0, h_skip+1, 
                 explwin_width, explwin_height, False);

      XSetForeground(dpy, gc, scene->color[C_FG_TEXT].pix);
      XDrawString(dpy, explwin, gc, d, fontasc[0] + h_skip + 4, 
                  expl_currdir, strlen(expl_currdir));

      h = 2*h_skip;
      XDrawLine(dpy, explwin, gc,  0, h, explwin_width, h);
   }

   if (!expl_dirtable) {
      activate = 1;
      expl_dirtable = get_dir_list(expl_currdir, &expl_table_entries);
      if (expl_dirtable)
         qsort(expl_dirtable, expl_table_entries, sizeof(char *), 
		 (int (*)(const void *, const void *))dup_strcmp);
   }
   
   if (!expl_dirtable) {
      char *error = msg[DIRECTORY_INEXISTENT_OR_INACCESSIBLE];
      XClearArea(dpy, explwin, 0, 2 * h_skip + 1, explwin_width, 
                 explwin_height, False);
      XSetForeground(dpy, gc, scene->color[C_FG_IMAGE].pix);
      XDrawString(dpy, explwin, gc, d, 3*h_skip, error, strlen(error));
      expl_shift_old = -1;
      return;
   }

   hp_skip = (5*h_skip)/6;
   expl_lines = (explwin_height-2*h_skip)/hp_skip;

   if (y <= 2) {
      if (!activate && expl_shift_old == expl_shift) return;
      expl_shift_old = expl_shift;
      XClearArea(dpy, explwin, 0, 2 * h_skip + 1, explwin_width - 13, 
                 explwin_height, False);
      XSetForeground(dpy, gc, scene->color[C_BG_TEXT].pix);
      XFillRectangle(dpy, explwin, gc, 
                 explwin_width - 12, 2 * h_skip + 12, 12, 
                 explwin_height - 2*h_skip - 24);
      XSetForeground(dpy, gc, scene->color[C_FG_TEXT].pix);
      XDrawLine(dpy, explwin, gc,  explwin_width - 13, 2*h_skip, 
                                   explwin_width - 13, explwin_height);
      /* Drawing small triangular icons */
      w = explwin_width - 6;
      h = 2 * h_skip + 1;
      for (i=0; i<=10; i++)
	  XDrawLine(dpy,explwin, gc, w-i/2, h+i, w+i/2, h+i);
      h = explwin_height-1;
      for (i=0; i<=10; i++)
          XDrawLine(dpy, explwin, gc, w-i/2, h-i, w+i/2, h-i);
      /* drawing the thumb */
      w = explwin_width - 10;
      p = explwin_height - 2 * h_skip - 28;
      ht = p * (expl_lines+1) / (expl_table_entries+1);
      if (ht<20) ht = 20;
      if (ht>p/2) ht = p/2;
      p = p - ht;
      h = 2 * h_skip + 14;
      if (expl_table_entries>2)
         h += (expl_shift * p)/(expl_table_entries-2);
      XDrawRectangle(dpy, explwin, gc, w, h, 7, ht);
   } else
      XClearArea(dpy, explwin, 2, 2 * h_skip + 1, 3, 
                 explwin_height, False);

   for (i=0; i<expl_table_entries-expl_shift; i++) 
   if (i<expl_lines) {
      s = expl_dirtable[i+expl_shift];
      b = -(s[strlen(s)-1]=='/');
      /* simple heuristics to decide file type from suffix */
      if (b==0) {
         if (strstr(s,".xpm") || strstr(s,".ppm") ||
             strstr(s,".png") || strstr(s,".jpg") ||
             strstr(s,".gif") || strstr(s,".tif")) b = 1;
         else
         if (strstr(s,".ps") || strstr(s,".eps") || strstr(s,".pdf")) b = 2;
         else
         if (strstr(s,".svg")) b = 3;
         else
         if (strstr(s,".htm")) b = 4;
         else
         if (strstr(s,".mid") || strstr(s,".midi")) b = 5;
         else
         if (strstr(s,".rc")) b = 6;
      }
      j = fontasc[0] + 2 * h_skip + i*hp_skip + 3;
      if (y>=3) {
	 p = (y-2*h_skip)/hp_skip;
	 if (p==i && x<=XTextWidth(xfont[0], s, strlen(s))+8) {
	    if (b==-1) {
	       if (!strcmp(s, "../")) {
		  i = strlen(expl_currdir)-1;
		  if (expl_currdir[i]=='/' && i>0) {
		     expl_currdir[i] = '\0';
		     --i;
		     s = rindex(expl_currdir, '/');
		     if (s) s[1] = '\0';
		  }
	       } else
	          strcat(expl_currdir, s);
	       free_dirlist(expl_dirtable);
	       expl_dirtable = NULL;
	       expl_shift = 0;
	       y = 1;
	       goto parse_again;
	    }
	    XSetForeground(dpy, gc, 
                        scene->color[C_FG_TICKS].pix);
            XFillRectangle(dpy, explwin, gc, d/4, j-5, 3, 4);
	    if (expl_output) {
	       char *cmd_old;
	       cmd_old = cmd_string;
	       cmd_string = malloc(strlen(expl_currdir)+strlen(s)+4);
	       if (!strncmp(expl_currdir, expl_implicit, 
                            strlen(expl_implicit))) {
	           sprintf(cmd_string, "%s%s", 
                      expl_currdir+strlen(expl_implicit), s);
               } else
	           sprintf(cmd_string, "%s%s", expl_currdir, s);
	       caret_pos = strlen(cmd_string);
               draw_win_string(scene, cmdwin, cmd_string, BOTTOM);
	       if (!strcmp(cmd_old, cmd_string) && line_clicked>=1) {
                  str2value(scene, line_clicked, cmd_string);
		  draw_win_cmd(scene, 2);
	       }
	       if (cmd_old) free(cmd_old);
	    } else {
	       char cmd[512];
               prog_cmd[0] = editor_cmd;
	       prog_cmd[1] = im_viewer;
	       prog_cmd[2] = ps_viewer;
	       prog_cmd[3] = svg_viewer;
	       prog_cmd[4] = html_viewer;
	       prog_cmd[5] = midi_cmd;
	       prog_cmd[6] = "xrmap -rcfile"; 
               sprintf(cmd, "%s %s%s &", prog_cmd[b], expl_currdir, s);
	       system(cmd);
	    }
	 }
      }
      if (b==-1)
         XSetForeground(dpy, gc, scene->color[C_FG_DIR].pix);
      else
      if (b==1)
         XSetForeground(dpy, gc, scene->color[C_FG_IMAGE].pix);
      else
         XSetForeground(dpy, gc, scene->color[C_FG_TEXT].pix);
         XDrawString(dpy, explwin, gc, d, j, s, strlen(s));
    }
}

void close_explwin()
{
    get_window_placement(explwin, &explwin_x, &explwin_y, 
                         &explwin_width, &explwin_height);
    XUnmapWindow(dpy, explwin);
    explwin_on = 0;
}

