//
// File chooser test program.
//
// Copyright 1999-2010 by Michael Sweet.
// Copyright 2011-2021 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file.  If this
// file is missing or damaged, see the license at:
//
//     https://www.fltk.org/COPYING.php
//
// Please see the following page on how to report bugs and issues:
//
//     https://www.fltk.org/bugs.php
//
// Contents:
//
//   main()           - Create a file chooser and wait for a selection to be made.
//   close_callback() - Close the main window...
//   fc_callback()    - Handle choices in the file chooser...
//   pdf_check()      - Check for and load the first page of a PDF file.
//   ps_check()       - Check for and load the first page of a PostScript file.
//   show_callback()  - Show the file chooser...
//
//   extra_callback() - circle extra groups (none,group1,check_button);
//

//
// Include necessary headers...
//

#include <FL/Fl_File_Chooser.H>
#include <FL/Fl_File_Icon.H>
#include <FL/Fl_Shared_Image.H>
#include <FL/Fl_PNM_Image.H>
#include <FL/Fl_Light_Button.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Terminal.H>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>     // exit()
#include <locale.h>     // setlocale()..

#define TERMINAL_HEIGHT 120
#define TERMINAL_GREEN  "\033[32m"
#define TERMINAL_NORMAL "\033[0m"

//
// Globals...
//

Fl_Input                *filter;
Fl_File_Browser         *files;
Fl_File_Chooser         *fc;
Fl_Shared_Image         *image = 0;
Fl_Terminal             *tty = 0;

// for choosing extra groups
Fl_Choice *ch_extra;
// first extra group
Fl_Group *encodings = (Fl_Group*)0;
Fl_Choice *ch_enc;
// second extra widget
Fl_Check_Button *version = (Fl_Check_Button*)0;

//
// Functions...
//

void            close_callback(void);
void            create_callback(void);
void            dir_callback(void);
void            fc_callback(Fl_File_Chooser *, void *);
void            multi_callback(void);
Fl_Image        *pdf_check(const char *, uchar *, int);
Fl_Image        *ps_check(const char *, uchar *, int);
void            show_callback(void);

void            extra_callback(Fl_Choice*,void*);

//
// 'main()' - Create a file chooser and wait for a selection to be made.
//

int                     // O - Exit status
main(int  argc,         // I - Number of command-line arguments
     char *argv[])      // I - Command-line arguments
{
  Fl_Double_Window      *window;// Main window
  Fl_Button             *button;// Buttons
  Fl_Group              *grp;   // Groups
  Fl_File_Icon          *icon;  // New file icon


  // Make the file chooser...
  setlocale(LC_ALL, "");    // enable multilanguage errors in file chooser
  Fl::scheme(NULL);
  Fl_File_Icon::load_system_icons();

  fc = new Fl_File_Chooser(".", "*", Fl_File_Chooser::SINGLE, "Fl_File_Chooser Test");
  fc->callback(fc_callback);

  // Register the PS and PDF image types...
  Fl_Shared_Image::add_handler(pdf_check);
  Fl_Shared_Image::add_handler(ps_check);

  // Make the main window...
  window = new Fl_Double_Window(400, 215+TERMINAL_HEIGHT, "File Chooser Test");

  tty = new Fl_Terminal(0,215,window->w(),TERMINAL_HEIGHT);
  tty->ansi(true);
  tty->display_columns(100);  // at least 100 cols wide, even tho actual window smaller

  // Group: limit resizing to filter input (not browse button)
  grp = new Fl_Group(0,10,400,25);
  grp->begin();
  {
    filter = new Fl_Input(50, 10, 315, 25, "Filter:");
    // Process standard arguments and find filter argument if present
    int argn = 1;
    while (argn < argc) {
      if (Fl::arg(argc, argv, argn) == 0)  break;
    }
    if (argc > argn)
      filter->value(argv[argn]);
    else
      filter->value("PDF Files (*.pdf)\t"
                    "PostScript Files (*.ps)\t"
                    "Image Files (*.{bmp,gif,jpg,png})\t"
                    "C/C++ Source Files (*.{c,C,cc,cpp,cxx})");

    button = new Fl_Button(365, 10, 25, 25);
    button->tooltip("Click to open file browser..");
    button->callback((Fl_Callback *)show_callback);
    if ( (icon = Fl_File_Icon::find(".", Fl_File_Icon::DIRECTORY)) ) {
      // Icon found; assign it..
      button->labelcolor(FL_YELLOW);
      icon->label(button);
    } else {
      // Fallback if no icon found
      button->label("..");
    }
  }
  grp->end();
  grp->resizable(filter);

  // Group: prevent resizing of the light buttons
  grp = new Fl_Group(0,45,400,55);
  grp->begin();
  {
    button = new Fl_Light_Button(50, 45, 80, 25, "MULTI");
    button->callback((Fl_Callback *)multi_callback);

    button = new Fl_Light_Button(140, 45, 90, 25, "CREATE");
    button->callback((Fl_Callback *)create_callback);

    button = new Fl_Light_Button(240, 45, 115, 25, "DIRECTORY");
    button->callback((Fl_Callback *)dir_callback);

    //
    ch_extra = new Fl_Choice(150, 75, 150, 25, "Extra Group:");
    ch_extra->add("none|encodings group|check button");
    ch_extra->value(0);
    ch_extra->callback((Fl_Callback *)extra_callback);
  }
  grp->end();
  grp->resizable(0);
  //
  files = new Fl_File_Browser(50, 105, 340, 75, "Files:");
  files->align(FL_ALIGN_LEFT);

  // Prevent resizing close button, but keep at right edge of scrn
  grp = new Fl_Group(0,185,400,25);
  grp->begin();
  {
    Fl_Box *invis = new Fl_Box(100,185,1,1);
    invis->box(FL_NO_BOX);
    button = new Fl_Button(310, 185, 80, 25, "Close");
    button->callback((Fl_Callback *)close_callback);
    grp->resizable(invis);
  }
  grp->end();

  window->resizable(files);
  window->end();
  window->show(1, argv);

  Fl::run();

  return (0);
}


void
extra_callback(Fl_Choice*w,void*)
{
  int val=w->value();
  if (0 == val) fc->add_extra(NULL);
  else if (1 == val) {
    if(!encodings){
      encodings=new Fl_Group(0,0,254,30);
      ch_enc=new Fl_Choice(152,2,100,25,"Choose Encoding:");
      ch_enc->add("ASCII|Koi8-r|win1251|Utf-8");
      encodings->end();
    }
    fc->add_extra(encodings);
  } else {
    if (!version) {
      version = new Fl_Check_Button(5,0,200,25,"Save binary 1.0 version");
    }
    fc->add_extra(version);
  }
}


//
// 'close_callback()' - Close the main window...
//

void
close_callback(void)
{
  exit(0);
}


//
// 'create_callback()' - Handle clicks on the create button.
//

void
create_callback(void)
{
  fc->type(fc->type() ^ Fl_File_Chooser::CREATE);
}


//
// 'dir_callback()' - Handle clicks on the directory button.
//

void
dir_callback(void)
{
  fc->type(fc->type() ^ Fl_File_Chooser::DIRECTORY);
}


//
// 'fc_callback()' - Handle choices in the file chooser...
//

void
fc_callback(Fl_File_Chooser *fc,        // I - File chooser
            void            *data)      // I - Data
{
  const char            *filename;      // Current filename


  tty->printf("fc_callback(fc = %p, data = %p)\n", fc, data);

  filename = fc->value();

  tty->printf("    filename = \"%s\"\n", filename ? filename : "(null)");
}


//
// 'multi_callback()' - Handle clicks on the multi button.
//

void
multi_callback(void)
{
  fc->type(fc->type() ^ Fl_File_Chooser::MULTI);
}


//
// 'pdf_check()' - Check for and load the first page of a PDF file.
//

Fl_Image *                      // O - Page image or NULL
pdf_check(const char *name,     // I - Name of file
          uchar      *header,   // I - Header data
          int)                  // I - Length of header data (unused)
{
  const char    *home;          // Home directory
  char          preview[FL_PATH_MAX],   // Preview filename
                command[3 * FL_PATH_MAX]; // Command


  if (memcmp(header, "%PDF", 4) != 0)
    return 0;

  home = fl_getenv("HOME");
  snprintf(preview, FL_PATH_MAX, "%s/.preview.ppm", home ? home : "");

  snprintf(command, sizeof(command),
          "gs -r100 -dFIXED -sDEVICE=ppmraw -dQUIET -dNOPAUSE -dBATCH "
          "-sstdout=\"%%stderr\" -sOUTPUTFILE=\'%s\' "
          "-dFirstPage=1 -dLastPage=1 \'%s\' 2>/dev/null", preview, name);

  if (fl_system(command)) return 0;

  return new Fl_PNM_Image(preview);
}


//
// 'ps_check()' - Check for and load the first page of a PostScript file.
//

Fl_Image *                      // O - Page image or NULL
ps_check(const char *name,      // I - Name of file
         uchar      *header,    // I - Header data
         int)                   // I - Length of header data (unused)
{
  const char    *home;          // Home directory
  char          preview[FL_PATH_MAX],   // Preview filename
                outname[FL_PATH_MAX],   // Preview PS file
                command[3 * FL_PATH_MAX]; // Command
  FILE          *in,            // Input file
                *out;           // Output file
  int           page;           // Current page
  char          line[256];      // Line from file


  if (memcmp(header, "%!", 2) != 0)
    return 0;

  home = fl_getenv("HOME");
  snprintf(preview, FL_PATH_MAX, "%s/.preview.ppm", home ? home : "");

  if (memcmp(header, "%!PS", 4) == 0) {
    // PS file has DSC comments; extract the first page...
    snprintf(outname, FL_PATH_MAX, "%s/.preview.ps", home ? home : "");

    if (strcmp(name, outname) != 0) {
      in   = fl_fopen(name, "rb");
      out  = fl_fopen(outname, "wb");
      page = 0;

      while (fgets(line, sizeof(line), in) != NULL) {
        if (strncmp(line, "%%Page:", 7) == 0) {
          page ++;
          if (page > 1) break;
        }

        fputs(line, out);
      }

      fclose(in);
      fclose(out);
    }
  } else {
    // PS file doesn't have DSC comments; do the whole file...
    strncpy(outname, name, sizeof(outname) - 1);
    outname[sizeof(outname) - 1] = '\0';
  }

  snprintf(command, sizeof(command),
          "gs -r100 -dFIXED -sDEVICE=ppmraw -dQUIET -dNOPAUSE -dBATCH "
          "-sstdout=\"%%stderr\" -sOUTPUTFILE=\'%s\' \'%s\' 2>/dev/null",
          preview, outname);

  if (fl_system(command)) return 0;

  return new Fl_PNM_Image(preview);
}


//
// 'show_callback()' - Show the file chooser...
//

void
show_callback(void)
{
  int   i;                      // Looping var
  int   count;                  // Number of files selected
  char  relative[FL_PATH_MAX];  // Relative filename


  if (filter->value()[0])
    fc->filter(filter->value());

  fc->show();

  while (fc->visible()) {
    Fl::wait();
  }

  count = fc->count();
  if (count > 0)
  {
    files->clear();

    for (i = 1; i <= count; i ++)
    {
      if (!fc->value(i)) break;
      fl_filename_relative(relative, sizeof(relative), fc->value(i));
      tty->printf("%d/%d) %sPicked: '%s'\n     Relative: '%s'%s\n", i, count,
                  TERMINAL_GREEN, fc->value(i), relative, TERMINAL_NORMAL);
      files->add(relative, Fl_File_Icon::find(fc->value(i), Fl_File_Icon::PLAIN));
    }

    files->redraw();
  }
}
