#include <FL/Fl_Native_File_Chooser.H>

#if SEAVIEW_FLTK_VERSION < 133

#include <FL/Fl_File_Icon.H>

#include "Fl_Native_File_Chooser_common.cxx"
#ifdef USE_GTK_FILE_CHOOSER
#  include "Fl_Native_File_Chooser_GTK.cxx"
#endif

#include <sys/stat.h>
#include <string.h>


// TMP until GTK chooser is integrated in FLTK
const char *mgFl_Native_File_Chooser::file_exists_message = "File exists. Are you sure you want to overwrite?";

int mgFl_Native_File_Chooser::have_looked_for_GTK_libs = 0;

/**
 The constructor. Internally allocates the native widgets.
 Optional \p val presets the type of browser this will be, 
 which can also be changed with type().
 */
mgFl_Native_File_Chooser::mgFl_Native_File_Chooser(int val) {
#if FLTK_ABI_VERSION <= 10302
  _btype       = val;
  _options     = NO_OPTIONS;
  _filter      = NULL;
  _filtvalue   = 0;
  _parsedfilt  = NULL;
  _preset_file = NULL;
  _prevvalue   = NULL;
  _directory   = NULL;
  _errmsg      = NULL;
#endif // FLTK_ABI_VERSION
#ifdef USE_GTK_FILE_CHOOSER
  if(have_looked_for_GTK_libs == 0) {
    // First Time here, try to find the GTK libs if they are installed
    mgFl_GTK_File_Chooser::probe_for_GTK_libs();
    have_looked_for_GTK_libs = -1;
  }
  
  // if we found all the GTK functions we need, we will use the GtkFileChooserDialog
  if (mgFl_GTK_File_Chooser::did_find_GTK_libs) _gtk_file_chooser = new mgFl_GTK_File_Chooser(val);
  else 
#endif // USE_GTK_FILE_CHOOSER
    _x11_file_chooser = new mgFl_FLTK_File_Chooser(val);
}

/**
 Destructor. 
 Deallocates any resources allocated to this widget.
 */
mgFl_Native_File_Chooser::~mgFl_Native_File_Chooser() {
  delete _x11_file_chooser;
}

/**
 Sets the current mgFl_Native_File_Chooser::Type of browser.
 */
void mgFl_Native_File_Chooser::type(int t) { return _x11_file_chooser->type(t); }
/**
 Gets the current mgFl_Native_File_Chooser::Type of browser.
 */
int mgFl_Native_File_Chooser::type() const { return _x11_file_chooser->type(); }
/**
 Sets the platform specific chooser options to \p val.
 \p val is expected to be one or more mgFl_Native_File_Chooser::Option flags ORed together.
 Some platforms have OS-specific functions that can be enabled/disabled via this method.
 <P>
 \code
 Flag              Description                                       Win       Mac       Other
 --------------    -----------------------------------------------   -------   -------   -------
 NEW_FOLDER        Shows the 'New Folder' button.                    Ignored   Used      Used
 PREVIEW           Enables the 'Preview' mode by default.            Ignored   Ignored   Used
 SAVEAS_CONFIRM    Confirm dialog if BROWSE_SAVE_FILE file exists.   Used      Used      Used
 USE_FILTER_EXT    Chooser filter pilots the output file extension.  Ignored   Used      Ignored
\endcode
 */
void mgFl_Native_File_Chooser::options(int o) {  _x11_file_chooser->options(o); }
/**
 Gets the platform specific mgFl_Native_File_Chooser::Option flags.
 */
int mgFl_Native_File_Chooser::options() const {  return _x11_file_chooser->options(); }

/**
 Returns the number of filenames (or directory names) the user selected.
 <P>
 \b Example:
 \code
 if ( fnfc->show() == 0 ) {
 // Print all filenames user selected
 for (int n=0; n<fnfc->count(); n++ ) {
 printf("%d) '%s'\n", n, fnfc->filename(n));
 }
 }
 \endcode
 */
int mgFl_Native_File_Chooser::count() const { return _x11_file_chooser->count(); }
/**
 Return the filename the user chose.
 Use this if only expecting a single filename.
 If more than one filename is expected, use filename(int) instead.
 Return value may be "" if no filename was chosen (eg. user cancelled).
 */
const char *mgFl_Native_File_Chooser::filename() const { return _x11_file_chooser->filename(); }
/**
 Return one of the filenames the user selected.
 Use count() to determine how many filenames the user selected.
 <P>
 \b Example:
 \code
 if ( fnfc->show() == 0 ) {
 // Print all filenames user selected
 for (int n=0; n<fnfc->count(); n++ ) {
 printf("%d) '%s'\n", n, fnfc->filename(n));
 }
 }
 \endcode
 */
const char *mgFl_Native_File_Chooser::filename(int i) const { return _x11_file_chooser->filename(i); }
/**
 Preset the directory the browser will show when opened.
 If \p val is NULL, or no directory is specified, the chooser will attempt
 to use the last non-cancelled folder.
 */
void mgFl_Native_File_Chooser::directory(const char *val) {  _x11_file_chooser->directory(val); }
/**
 Returns the current preset directory() value.
 */
const char *mgFl_Native_File_Chooser::directory() const { return _x11_file_chooser->directory(); }
/**
 Set the title of the file chooser's dialog window.
 Can be NULL if no title desired.
 The default title varies according to the platform, so you are advised to set the title explicitly.
 */
void mgFl_Native_File_Chooser::title(const char *t) {  _x11_file_chooser->title(t); }
/**
 Get the title of the file chooser's dialog window.
 Return value may be NULL if no title was set.
 */
const char* mgFl_Native_File_Chooser::title() const { return _x11_file_chooser->title(); }
/**
 Returns the filter string last set.
 Can be NULL if no filter was set.
 */
const char *mgFl_Native_File_Chooser::filter() const { return _x11_file_chooser->filter(); }
/**
 Sets the filename filters used for browsing. 
 The default is NULL, which browses all files.
 <P>
 The filter string can be any of:
 <P>
 - A single wildcard (eg. "*.txt")
 - Multiple wildcards (eg. "*.{cxx,h,H}")
 - A descriptive name followed by a "\t" and a wildcard (eg. "Text Files\t*.txt")
 - A list of separate wildcards with a "\n" between each (eg. "*.{cxx,H}\n*.txt")
 - A list of descriptive names and wildcards (eg. "C++ Files\t*.{cxx,H}\nTxt Files\t*.txt")
 <P>
 The format of each filter is a wildcard, or an optional user description 
 followed by '\\t' and the wildcard.
 <P>
 On most platforms, each filter is available to the user via a pulldown menu 
 in the file chooser. The 'All Files' option is always available to the user. 
 */
void mgFl_Native_File_Chooser::filter(const char *f) {  _x11_file_chooser->filter(f); }
/**
 Gets how many filters were available, not including "All Files" 
 */
int mgFl_Native_File_Chooser::filters() const { return _x11_file_chooser->filters(); }
/**
 Sets which filter will be initially selected.
 
 The first filter is indexed as 0. 
 If filter_value()==filters(), then "All Files" was chosen. 
 If filter_value() > filters(), then a custom filter was set.
 */
void mgFl_Native_File_Chooser::filter_value(int i) {  _x11_file_chooser->filter_value(i); }
/**
 Returns which filter value was last selected by the user.
 This is only valid if the chooser returns success.
 */
int mgFl_Native_File_Chooser::filter_value() const { return _x11_file_chooser->filter_value(); }
/**
 Sets the default filename for the chooser.
 Use directory() to set the default directory.
 Mainly used to preset the filename for save dialogs, 
 and on most platforms can be used for opening files as well. 
 */
void mgFl_Native_File_Chooser::preset_file(const char* f) {  _x11_file_chooser->preset_file(f); }
/**
 Get the preset filename.
 */
const char* mgFl_Native_File_Chooser::preset_file() const { return _x11_file_chooser->preset_file(); }
/**
 Returns a system dependent error message for the last method that failed. 
 This message should at least be flagged to the user in a dialog box, or to some kind of error log. 
 Contents will be valid only for methods that document errmsg() will have info on failures.
 */
const char *mgFl_Native_File_Chooser::errmsg() const { return _x11_file_chooser->errmsg(); }
/**
 Post the chooser's dialog. Blocks until dialog has been completed or cancelled.
 \returns
 - 0  -- user picked a file
 - 1  -- user cancelled
 - -1 -- failed; errmsg() has reason
 */
int mgFl_Native_File_Chooser::show() { return _x11_file_chooser->show(); }


mgFl_FLTK_File_Chooser::mgFl_FLTK_File_Chooser(int val) {
  _btype       = 0;
  _options     = 0;
  _filter      = NULL;
  _filtvalue   = 0;
  _parsedfilt  = NULL;
  _preset_file = NULL;
  _prevvalue   = NULL;
  _directory   = NULL;
  _errmsg      = NULL;
  _file_chooser= NULL;
  if (val >= 0) {
    _file_chooser = new Fl_File_Chooser(NULL, NULL, 0, NULL);
    type(val);			// do this after _file_chooser created
    }
  _nfilters    = 0;
} 

mgFl_FLTK_File_Chooser::~mgFl_FLTK_File_Chooser() {
  delete _file_chooser;
  _file_chooser = NULL;
  _filter      = strfree(_filter);
  _parsedfilt  = strfree(_parsedfilt);
  _preset_file = strfree(_preset_file);
  _prevvalue   = strfree(_prevvalue);
  _directory   = strfree(_directory);
  _errmsg      = strfree(_errmsg);
}


// PRIVATE: SET ERROR MESSAGE
void mgFl_FLTK_File_Chooser::errmsg(const char *msg) {
  _errmsg = strfree(_errmsg);
  _errmsg = strnew(msg);
}

// PRIVATE: translate Native types to Fl_File_Chooser types
int mgFl_FLTK_File_Chooser::type_fl_file(int val) {
  switch (val) {
    case mgFl_Native_File_Chooser::BROWSE_FILE:
      return(Fl_File_Chooser::SINGLE);
    case mgFl_Native_File_Chooser::BROWSE_DIRECTORY:
      return(Fl_File_Chooser::SINGLE | Fl_File_Chooser::DIRECTORY);
    case mgFl_Native_File_Chooser::BROWSE_MULTI_FILE:
      return(Fl_File_Chooser::MULTI);
    case mgFl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY:
      return(Fl_File_Chooser::DIRECTORY | Fl_File_Chooser::MULTI);
    case mgFl_Native_File_Chooser::BROWSE_SAVE_FILE:
      return(Fl_File_Chooser::SINGLE | Fl_File_Chooser::CREATE);
    case mgFl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY:
      return(Fl_File_Chooser::DIRECTORY | Fl_File_Chooser::MULTI | Fl_File_Chooser::CREATE);
    default:
      return(Fl_File_Chooser::SINGLE);
  }
}

void mgFl_FLTK_File_Chooser::type(int val) {
  _btype = val;
  _file_chooser->type(type_fl_file(val));
}

int mgFl_FLTK_File_Chooser::type() const {
  return(_btype);
}

void mgFl_FLTK_File_Chooser::options(int val) {
  _options = val;
}

int mgFl_FLTK_File_Chooser::options() const {
  return(_options);
}

int mgFl_FLTK_File_Chooser::show() {

  // FILTER
    if ( _parsedfilt ) {
      _file_chooser->filter(_parsedfilt);
    }

    // FILTER VALUE
    //     Set this /after/ setting the filter
    //
    _file_chooser->filter_value(_filtvalue);

    // DIRECTORY
    if ( _directory && _directory[0] ) {
      _file_chooser->directory(_directory);
    } else {
      _file_chooser->directory(_prevvalue);
    }

    // PRESET FILE
    if ( _preset_file ) {
      _file_chooser->value(_preset_file);
    }

    // OPTIONS: PREVIEW
    _file_chooser->preview( (options() & mgFl_Native_File_Chooser::PREVIEW) ? 1 : 0);
  
    // SHOW
    _file_chooser->show();

    // BLOCK WHILE BROWSER SHOWN
    while ( _file_chooser->shown() ) {
      Fl::wait();
    }

    if ( _file_chooser->value() && _file_chooser->value()[0] ) {
      _prevvalue = strfree(_prevvalue);
      _prevvalue = strnew(_file_chooser->value());
      _filtvalue = _file_chooser->filter_value(); // update filter value

      // HANDLE SHOWING 'SaveAs' CONFIRM
      if ( options() & mgFl_Native_File_Chooser::SAVEAS_CONFIRM && type() == mgFl_Native_File_Chooser::BROWSE_SAVE_FILE ) {
        struct stat buf;
        if ( stat(_file_chooser->value(), &buf) != -1 ) {
          if ( buf.st_mode & S_IFREG ) {    // Regular file + exists?
            if ( exist_dialog() == 0 ) {
              return(1);
            }
          }
        }
      }
    }

    if ( _file_chooser->count() ) return(0);
    else return(1);
}

const char *mgFl_FLTK_File_Chooser::errmsg() const {
  return(_errmsg ? _errmsg : "No error");
}

const char* mgFl_FLTK_File_Chooser::filename() const {
  if ( _file_chooser->count() > 0 ) {
    return(_file_chooser->value());
  }
  return("");
}

const char* mgFl_FLTK_File_Chooser::filename(int i) const {
    if ( i < _file_chooser->count() )
        return(_file_chooser->value(i+1));  // convert fltk 1 based to our 0 based
  return("");
}

void mgFl_FLTK_File_Chooser::title(const char *val) {
  _file_chooser->label(val);
}

const char *mgFl_FLTK_File_Chooser::title() const {
    return(_file_chooser->label());
}

void mgFl_FLTK_File_Chooser::filter(const char *val) {
  _filter = strfree(_filter);
  _filter = strnew(val);
  parse_filter();
}

const char *mgFl_FLTK_File_Chooser::filter() const {
  return(_filter);
}

int mgFl_FLTK_File_Chooser::filters() const {
  return(_nfilters);
}

void mgFl_FLTK_File_Chooser::filter_value(int val) {
  _filtvalue = val;
}

int mgFl_FLTK_File_Chooser::filter_value() const {
  return _filtvalue;
}

int mgFl_FLTK_File_Chooser::count() const {
  return _file_chooser->count();
}

void mgFl_FLTK_File_Chooser::directory(const char *val) {
  _directory = strfree(_directory);
  _directory = strnew(val);
}

const char *mgFl_FLTK_File_Chooser::directory() const {
  return _directory;
}

// PRIVATE: Convert our filter format to fltk's chooser format
//     FROM                                     TO (FLTK)
//     -------------------------                --------------------------
//     "*.cxx"                                  "*.cxx Files(*.cxx)"
//     "C Files\t*.{cxx,h}"                     "C Files(*.{cxx,h})"
//     "C Files\t*.{cxx,h}\nText Files\t*.txt"  "C Files(*.{cxx,h})\tText Files(*.txt)"
//
//     Returns a modified version of the filter that the caller is responsible
//     for freeing with strfree().
//
void mgFl_FLTK_File_Chooser::parse_filter() {
  _parsedfilt = strfree(_parsedfilt);	// clear previous parsed filter (if any)
  _nfilters = 0;
  char *in = _filter;
  if ( !in ) return;

  int has_name = strchr(in, '\t') ? 1 : 0;

  char mode = has_name ? 'n' : 'w';	// parse mode: n=title, w=wildcard
  char wildcard[1024] = "";		// parsed wildcard
  char name[1024] = "";

  // Parse filter user specified
  for ( ; 1; in++ ) {
    /*** DEBUG
    printf("WORKING ON '%c': mode=<%c> name=<%s> wildcard=<%s>\n",
			*in, mode,     name,     wildcard);
    ***/

    switch (*in) {
      // FINISHED PARSING NAME?
      case '\t':
        if ( mode != 'n' ) goto regchar;
        mode = 'w';
        break; 
      // ESCAPE NEXT CHAR
      case '\\':
	++in;
	goto regchar; 
      // FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS?
      case '\r':
      case '\n':
      case '\0':
	// APPEND NEW FILTER TO LIST
	if ( wildcard[0] ) {
	  // OUT: "name(wild)\tname(wild)"
	  char comp[2048];
	  sprintf(comp, "%s%.511s(%.511s)", ((_parsedfilt)?"\t":""),
					    name, wildcard);
	  _parsedfilt = strapp(_parsedfilt, comp);
	  _nfilters++;
	  //DEBUG printf("DEBUG: PARSED FILT NOW <%s>\n", _parsedfilt);
	}
	// RESET
	wildcard[0] = name[0] = '\0';
	mode = strchr(in, '\t') ? 'n' : 'w';
	// DONE?
	if ( *in == '\0' ) return;	// done
	else continue;			// not done yet, more filters

      // Parse all other chars
      default:				// handle all non-special chars
      regchar:				// handle regular char
	switch ( mode ) {
	  case 'n': chrcat(name, *in);     continue;
	  case 'w': chrcat(wildcard, *in); continue;
	}
	break;
    }
  }
  //NOTREACHED
}

void mgFl_FLTK_File_Chooser::preset_file(const char* val) {
  _preset_file = strfree(_preset_file);
  _preset_file = strnew(val);
}

const char* mgFl_FLTK_File_Chooser::preset_file() const {
  return _preset_file;
}

int mgFl_FLTK_File_Chooser::exist_dialog() {
  return fl_choice("%s", fl_cancel, fl_ok, NULL, mgFl_Native_File_Chooser::file_exists_message);
}

#endif // SEAVIEW_FLTK_VERSION < 133
