/* filesel.c: File selection dialog box
   Copyright (c) 2001-2005 Matan Ziv-Av, Philip Kendall, Russell Marks,
			   Marek Januszewski

   $Id: filesel.c,v 1.34.2.1 2007/02/17 16:40:36 pak21 Exp $

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

   Author contact information:

   E-mail: philip-fuse@shadowmagic.org.uk

*/

#include <config.h>

#ifdef USE_WIDGET

#include <sys/types.h>
//#include <dirent.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
//#include <unistd.h>

#ifdef WIN32
#include <windows.h>
#endif				/* #ifdef WIN32 */

#include "fuse.h"
/*  #include "keyboard.h" */
#include "widget_internals.h"

struct widget_dirent **widget_filenames; /* Filenames in the current
					    directory */
size_t widget_numfiles;	  /* The number of files in the current
			     directory */

static const char *title;
static int is_saving;

#define PAGESIZE (is_saving ? 32 : 36)

/* The number of the filename in the top-left corner of the current
   display, that of the filename which the `cursor' is on, and that
   which it will be on after this keypress */
static size_t top_left_file, current_file, new_current_file;

static void widget_scan( char *dir );
static int widget_select_file( const struct dirent *dirent );
static int widget_scan_compare( const widget_dirent **a,
				const widget_dirent **b );

static char* widget_getcwd( void );
static int widget_print_all_filenames( struct widget_dirent **filenames, int n,
				       int top_left, int current,
				       const char *dir );
static int widget_print_filename( struct widget_dirent *filename, int position,
				  int inverted );

/* The filename to return */
char* widget_filesel_name;

/* Should we exit all widgets when we're done with this selector? */
static int exit_all_widgets;

static int widget_scandir( const char *dir, struct widget_dirent ***namelist,
			   int (*select_fn)(const struct dirent*) )
{
#if 0
  DIR *directory; struct dirent *dirent;

  int allocated, number;
  int i; size_t length;

  (*namelist) =
    (struct widget_dirent**)malloc( 32 * sizeof(struct widget_dirent*) );
  if( *namelist == NULL ) return -1;

  allocated = 32; number = 0;

  directory = opendir( dir );
  if( directory == NULL ) {
    free( *namelist );
    *namelist = NULL;
    return -1;
  }

  while( 1 ) {
    errno = 0;
    dirent = readdir( directory );

    if( dirent == NULL ) {
      if( errno == 0 ) {	/* End of directory */
	break;
      } else {
	for( i=0; i<number; i++ ) {
	  free( (*namelist)[i]->name );
	  free( (*namelist)[i] );
	}
	free( *namelist );
	*namelist = NULL;
	closedir( directory );
	return -1;
      }
    }

    if( select_fn( dirent ) ) {

      if( ++number > allocated ) {
	struct widget_dirent **oldptr = *namelist;

	(*namelist) = (struct widget_dirent**)realloc(
	  (*namelist), 2 * allocated * sizeof(struct widget_dirent*)
	);
	if( *namelist == NULL ) {
	  for( i=0; i<number-1; i++ ) {
	    free( oldptr[i]->name );
	    free( oldptr[i] );
	  }
	  free( oldptr );
	  closedir( directory );
	  return -1;
	}
	allocated *= 2;
      }

      (*namelist)[number-1] =
	(struct widget_dirent*)malloc( sizeof(struct widget_dirent) );
      if( (*namelist)[number-1] == NULL ) {
	for( i=0; i<number-1; i++ ) {
	  free( (*namelist)[i]->name );
	  free( (*namelist)[i] );
	}
	free( *namelist );
	*namelist = NULL;
	closedir( directory );
	return -1;
      }

      length = strlen( dirent->d_name ) + 1;
      if( length < 16 ) length = 16;

      (*namelist)[number-1]->name = (char*)malloc( length * sizeof(char) );
      if( (*namelist)[number-1]->name == NULL ) {
	free( (*namelist)[number-1] );
	for( i=0; i<number-1; i++ ) {
	  free( (*namelist)[i]->name );
	  free( (*namelist)[i] );
	}
	free( *namelist );
      }

      strcpy( (*namelist)[number-1]->name, dirent->d_name );

    }

  }

  if( closedir( directory ) ) {
    for( i=0; i<number; i++ ) {
      free( (*namelist)[i]->name );
      free( (*namelist)[i] );
    }
    free( *namelist );
    *namelist = NULL;
    return -1;
  }

  return number;
#endif
  return 0 ;
}

static void widget_scan( char *dir )
{
#if 0
  struct stat file_info;

  size_t i; int error;
  
  /* Free the memory belonging to the files in the previous directory */
  for( i=0; i<widget_numfiles; i++ ) {
    free( widget_filenames[i]->name );
    free( widget_filenames[i] );
  }

  widget_numfiles = widget_scandir( dir, &widget_filenames,
				    widget_select_file );
  if( widget_numfiles == (size_t)-1 ) return;

  for( i=0; i<widget_numfiles; i++ ) {
    error = stat( widget_filenames[i]->name, &file_info );
    widget_filenames[i]->mode = error ? 0 : file_info.st_mode;
  }

  qsort( widget_filenames, widget_numfiles, sizeof(struct widget_dirent*),
	 (int(*)(const void*,const void*))widget_scan_compare );
#endif
}

//static int widget_select_file(const struct dirent *dirent){
//  return( dirent->d_name && strcmp( dirent->d_name, "." ) );
//}

static int widget_scan_compare( const struct widget_dirent **a,
				const struct widget_dirent **b )
{
#if 0
  int isdir1 = S_ISDIR( (*a)->mode ),
      isdir2 = S_ISDIR( (*b)->mode );

  if( isdir1 && !isdir2 ) {
    return -1;
  } else if( isdir2 && !isdir1 ) {
    return 1;
  } else {
    return strcmp( (*a)->name, (*b)->name );
  }
#endif
  return 0 ;
}

/* File selection widget */

static int
widget_filesel_draw( void *data )
{
#if 0
  widget_filesel_data *filesel_data = data;
  char *directory;
  int error;

  exit_all_widgets = filesel_data->exit_all_widgets;
  title = filesel_data->title;

  directory = widget_getcwd();
  if( directory == NULL ) return 1;

  widget_scan( directory );
  new_current_file = current_file = 0;
  top_left_file = 0;

  /* Create the dialog box */
  error = widget_dialog_with_border( 1, 2, 30, 21 );
  if( error ) { free( directory ); return error; }
    
  /* Show all the filenames */
  widget_print_all_filenames( widget_filenames, widget_numfiles,
			      top_left_file, current_file, directory );
#endif
  return 0;
}

int widget_filesel_finish( widget_finish_state finished ) {

  /* Return with null if we didn't finish cleanly */
  if( finished != WIDGET_FINISHED_OK ) {
    if( widget_filesel_name ) free( widget_filesel_name );
    widget_filesel_name = NULL;
  }

  return 0;
}

int
widget_filesel_load_draw( void *data )
{
  is_saving = 0;
  return widget_filesel_draw( data );
}

int
widget_filesel_save_draw( void *data )
{
  is_saving = 1;
  return widget_filesel_draw( data );
}

static char* widget_getcwd( void )
{
#if 0
  char *directory; size_t directory_length;
  char *ptr;

  directory_length = 64;
  directory = (char*)malloc( directory_length * sizeof( char ) );
  if( directory == NULL ) {
    return NULL;
  }

  do {
    ptr = getcwd( directory, directory_length );
    if( ptr ) break;
    if( errno == ERANGE ) {
      ptr = directory;
      directory_length *= 2;
      directory =
	(char*)realloc( directory, directory_length * sizeof( char ) );
      if( directory == NULL ) {
	free( ptr );
	return NULL;
      }
    } else {
      free( directory );
      return NULL;
    }
  } while(1);

  return directory;
#endif
  return NULL ;
}

static int widget_print_all_filenames( struct widget_dirent **filenames, int n,
				       int top_left, int current,
				       const char *dir )
{
  int i;
  int error;

  /* Give us a clean box to start with */
  error = widget_dialog( 1, 2, 30, 21 );
  if( error ) return error;

  widget_print_title( 16, WIDGET_COLOUR_FOREGROUND, title );
  if( widget_stringwidth( dir ) > 223 ) {
    char buffer[128];
    int prefix = widget_stringwidth( "..." ) + 1;
    while( widget_stringwidth( dir ) > 223 - prefix ) dir++;
    snprintf( buffer, sizeof( buffer ), "...%s", dir );
    widget_print_title( 28, WIDGET_COLOUR_FOREGROUND, buffer );  
  } else {
    widget_print_title( 28, WIDGET_COLOUR_FOREGROUND, dir );
  }

  if( top_left ) widget_up_arrow( 1, 5, WIDGET_COLOUR_FOREGROUND );

  /* Print the filenames, mostly normally, but with the currently
     selected file inverted */
  for( i = top_left; i < n && i < top_left + PAGESIZE; i++ ) {
    if( i == current ) {
      widget_print_filename( filenames[i], i-top_left, 1 );
    } else {
      widget_print_filename( filenames[i], i-top_left, 0 );
    }
  }

  if( is_saving )
  {
    widget_printstring( 12, 22 * 8, WIDGET_COLOUR_FOREGROUND,
				     "\012RETURN\011 = select" );
    widget_printstring_right( 244, 22 * 8, WIDGET_COLOUR_FOREGROUND,
					   "\012TAB\011 = enter name" );
  }

  if( i < n )
    widget_down_arrow( 1, is_saving ? 20 : 22, WIDGET_COLOUR_FOREGROUND );

  /* Display that lot */
  widget_display_lines( 2, 21 );

  return 0;
}

/* Print a filename onto the dialog box */
static int widget_print_filename( struct widget_dirent *filename, int position,
				  int inverted )
{
#if 0
  char buffer[64], suffix[64], *dot = 0;
  int width, suffix_width = 0;
  int dir = S_ISDIR( filename->mode );
  int truncated = 0, suffix_truncated = 0;

#define FILENAME_WIDTH 112
#define MAX_SUFFIX_WIDTH 56

  int x = (position & 1) ? 132 : 16,
      y = 40 + (position >> 1) * 8;

  int foreground = inverted ? WIDGET_COLOUR_BACKGROUND
                            : WIDGET_COLOUR_FOREGROUND,

      background = inverted ? WIDGET_COLOUR_FOREGROUND
                            : WIDGET_COLOUR_BACKGROUND;

  widget_rectangle( x, y, FILENAME_WIDTH, 8, background );

  strncpy( buffer, filename->name, sizeof( buffer ) - dir - 1);
  buffer[sizeof( buffer ) - dir - 1] = '\0';

  if (dir)
    dir = widget_charwidth( '/' );
  else {
    /* get filename extension */
    dot = strrchr( filename->name, '.' );

    /* if .gz or .bz2, we want the previous component too */
    if( dot &&( !strcasecmp( dot, ".gz" ) || !strcasecmp( dot, ".bz2" ) ) ) {
      char *olddot = dot;
      *olddot = '\0';
      dot = strrchr( filename->name, '.' );
      *olddot = '.';
      if (!dot)
	dot = olddot;
    }

    /* if the dot is at the start of the name, ignore it */
    if( dot == filename->name )
      dot = 0;
  }

  if( dot ) {
    /* split filename at extension separator */
    buffer[dot - filename->name] = '\0';

    /* get extension width (for display purposes) */
    snprintf( suffix, sizeof( suffix ), "%s", dot );
    while( ( suffix_width = ( dot && !dir )
	     ? widget_stringwidth( suffix ) : 0 ) > 110 ) {
      suffix_truncated = 1;
      suffix[strlen( suffix ) - 1] = '\0';
    }
  }

  while( ( width = widget_stringwidth( buffer ) ) >=
	 FILENAME_WIDTH - dir - ( dot ? truncated + suffix_width : 0 ) ) {
    truncated = 2;
    if( suffix_width >= MAX_SUFFIX_WIDTH ) {
      suffix_truncated = 2;
      suffix[strlen (suffix) - 1] = '\0';
      suffix_width = widget_stringwidth (suffix);
    }
    else
      buffer[strlen (buffer) - 1] = '\0';
  }
  if( dir )
    strcat (buffer, "/");

  widget_printstring( x + 1, y, foreground, buffer );
  if( truncated )
    widget_rectangle( x + width + 2, y, 1, 8, 4 );
  if( dot )
    widget_printstring( x + width + 2 + truncated, y,
			foreground ^ 2, suffix );
  if( suffix_truncated )
    widget_rectangle( x + FILENAME_WIDTH, y, 1, 8, 4 );
#endif
  return 0;
}

void
widget_filesel_keyhandler( input_key key )
{
#if 0
  char *fn, *ptr;

  new_current_file = current_file;

  switch(key) {

#if 0
  case INPUT_KEY_Resize:	/* Fake keypress used on window resize */
    widget_dialog_with_border( 1, 2, 30, 20 );
    widget_print_all_filenames( widget_filenames, widget_numfiles,
				top_left_file, current_file        );
    break;
#endif
    
  case INPUT_KEY_Escape:
    widget_end_widget( WIDGET_FINISHED_CANCEL );
    break;
  
  case INPUT_KEY_Left:
  case INPUT_KEY_5:
  case INPUT_KEY_h:
    if( current_file > 0                 ) new_current_file--;
    break;

  case INPUT_KEY_Down:
  case INPUT_KEY_6:
  case INPUT_KEY_j:
    if( current_file < widget_numfiles-2 ) new_current_file += 2;
    break;

  case INPUT_KEY_Up:
  case INPUT_KEY_7:		/* Up */
  case INPUT_KEY_k:
    if( current_file > 1                 ) new_current_file -= 2;
    break;

  case INPUT_KEY_Right:
  case INPUT_KEY_8:
  case INPUT_KEY_l:
    if( current_file < widget_numfiles-1 ) new_current_file++;
    break;

  case INPUT_KEY_Page_Up:
    new_current_file = ( current_file > PAGESIZE ) ?
                       current_file - PAGESIZE     :
                       0;
    break;

  case INPUT_KEY_Page_Down:
    new_current_file = current_file + PAGESIZE;
    if( new_current_file >= widget_numfiles )
      new_current_file = widget_numfiles - 1;
    break;

  case INPUT_KEY_Home:
    new_current_file = 0;
    break;

  case INPUT_KEY_End:
    new_current_file = widget_numfiles - 1;
    break;

  case INPUT_KEY_Tab:
    if( is_saving ) {
      widget_text_t text_data;
      text_data.title = title;
      text_data.allow = WIDGET_INPUT_ASCII;
      text_data.text[0] = 0;
      if( widget_do( WIDGET_TYPE_TEXT, &text_data ) ||
	  !widget_text_text || !*widget_text_text      )
	break;
      if( *widget_text_text != '/' ) {	/* relative name */
        /* Get current dir name and allocate space for the leafname */
        fn = widget_getcwd();
        if( fn )
    	  fn = realloc( fn, strlen( fn ) + strlen( widget_text_text ) + 2 );
        if( !fn ) {
	  widget_end_widget( WIDGET_FINISHED_CANCEL );
	  return;
        }
        /* Append the leafname and return it */
        strcat( fn, "/" );
        strcat( fn, widget_text_text );
      } else {				/* absolute name */
	fn = strdup( widget_text_text );
        if( !fn ) {
	  widget_end_widget( WIDGET_FINISHED_CANCEL );
	  return;
        }
      }
      widget_filesel_name = fn;
      if( exit_all_widgets ) {
	widget_end_all( WIDGET_FINISHED_OK );
      } else {
	widget_end_widget( WIDGET_FINISHED_OK );
      }
    }
    break;

  case INPUT_KEY_Return:
    /* Get the new directory name */
    fn = widget_getcwd();
    if( fn == NULL ) {
      widget_end_widget( WIDGET_FINISHED_CANCEL );
      return;
    }
    ptr = fn;
    fn = realloc( fn,
       ( strlen(fn) + 1 + strlen( widget_filenames[ current_file ]->name ) +
	 1 ) * sizeof(char)
    );
    if( fn == NULL ) {
      free( ptr );
      widget_end_widget( WIDGET_FINISHED_CANCEL );
      return;
    }
    strcat( fn, "/" ); strcat( fn, widget_filenames[ current_file ]->name );
			
/*
  in Win32 errno resulting from chdir on file is EINVAL which may mean many things
  this will not be fixed in mingw - must use native function instead
  http://thread.gmane.org/gmane.comp.gnu.mingw.user/9197
*/ 

    if(chdir(fn)==-1) {
#ifndef WIN32
      if(errno==ENOTDIR) {
#else
      if( GetFileAttributes( fn ) != FILE_ATTRIBUTE_DIRECTORY	) {
#endif
	widget_filesel_name = fn;
	if( exit_all_widgets ) {
	  widget_end_all( WIDGET_FINISHED_OK );
	} else {
	  widget_end_widget( WIDGET_FINISHED_OK );
	}
      }
    } else {
      widget_scan( fn ); free( fn );
      new_current_file = 0;
      /* Force a redisplay of all filenames */
      current_file = 1; top_left_file = 1;
    }
    break;

  default:	/* Keep gcc happy */
    break;

  }

  /* If we moved the cursor */
  if( new_current_file != current_file ) {

    /* If we've got off the top or bottom of the currently displayed
       file list, then reset the top-left corner and display the whole
       thing */
    if( new_current_file < top_left_file ) {

      top_left_file = new_current_file & ~1;
      widget_print_all_filenames( widget_filenames, widget_numfiles,
				  top_left_file, new_current_file,
				  widget_getcwd() );

    } else if( new_current_file >= top_left_file+PAGESIZE ) {

      top_left_file = new_current_file & ~1; top_left_file -= PAGESIZE - 2;
      widget_print_all_filenames( widget_filenames, widget_numfiles,
				  top_left_file, new_current_file,
				  widget_getcwd() );

    } else {

      /* Otherwise, print the current file uninverted and the
	 new current file inverted */

      widget_print_filename( widget_filenames[ current_file ],
			     current_file - top_left_file, 0 );
	  
      widget_print_filename( widget_filenames[ new_current_file ],
			     new_current_file - top_left_file, 1 );
        
      widget_display_lines( 2, 21 );
    }

    /* Reset the current file marker */
    current_file = new_current_file;

  }
#endif
}

#endif				/* #ifdef USE_WIDGET */
