/************************************************************
**
** Application: TranJPEG
**
** Title:       c.process
**
*************************************************************/

/*
*
* Copyright (c) 2015, Chris Johnson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the copyright holder nor the names of their
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

/* Include files */
/* from standard clib */
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>


/* headers from tasklib */
#include "TaskLib:h.os"
#include "TaskLib:h.wimp"
#include "TaskLib:h.werr"
#include "TaskLib:h.wos"
#include "TaskLib:h.err"
#include "TaskLib:h.poll"
#include "TaskLib:h.etc"
#include "TaskLib:h.flex"
#include "TaskLib:h.fsx"
#include "TaskLib:h.pane"
#include "TaskLib:h.temp"
#include "TaskLib:h.constants"
#include "TaskLib:h.conf"
#include "TaskLib:h.swis"
#include "TaskLib:h.save"
#include "TaskLib:h.xx2con"
#include "TaskLib:h.mlo"

/* headers from app */
#include "h.reslink"
#include "h.process"
#include "h.status"
#include "h.taskwindow"
#include "h.xdeb"
#include "h.exif"
#include "h.main"

/* gadgets in control window */
#define CTRL_FILE_COUNT   1
#define CTRL_CLEAR        4
#define CTRL_OPTIMISE     11
#define CTRL_PROGRESSIVE  12
#define CTRL_TRANSFORM    13
#define CTRL_ROTATE_RIGHT 14
#define CTRL_ROTATE_180   15
#define CTRL_ROTATE_LEFT  16
#define CTRL_ROTATE_AUTO  17
#define CTRL_FLIP_HORIZ   18
#define CTRL_FLIP_VERT    19
#define CTRL_TRANSPOSE    24
#define CTRL_TRANSVERSE   25
#define CTRL_SMARTSCALE   22
#define CTRL_SMARTSCALE_SET 8
#define CTRL_SMARTSCALE_POPUP 9
#define CTRL_SAVEPATH_POPUP 5
#define CTRL_SAVEPATH       6
#define CTRL_USE_SAVEPATH   7
#define CTRL_OVERWRITE_ORIGINAL 37
#define CTRL_OPEN_SETTINGS  2
#define CTRL_PROCESS        10
#define CTRL_TRANSVERSE_IM   21
#define CTRL_TRANSPOSE_IM    20
#define CTRL_GREYSCALE       26
#define CTRL_STRIP_EXIFDATA  27
#define CTRL_CROP            28
#define CTRL_CROP_WIDTH      30
#define CTRL_CROP_HEIGHT     32
#define CTRL_CROP_X_ORIG     34
#define CTRL_CROP_Y_ORIG     36
#define CTRL_CROP_WIDTH_LAB   29
#define CTRL_CROP_HEIGHT_LAB  31
#define CTRL_CROP_X_ORIG_LAB  33
#define CTRL_CROP_Y_ORIG_LAB  35

/* gadgets in settings window */
#define SET_TRIM_BLOCKS    7
#define SET_COPY_NONE      3
#define SET_COPY_COMM      4
#define SET_COPY_ALL       5
#define SET_COPY_IMAGE     6
#define SET_WRITE_ORIENT   9
#define SET_SAVE           11
#define SET_KEEP_TIMESTAMP 12


#define FILENAME_STORE_CHUNK 4000

/* globals */


/* In the following assignments, the smart scale details must be last.
   This is due to using  CTRL_SMARTSCALE + menu item index for the
   different smart scale options */
static process_settings pset = { 0, 0, 0, 0, 0, 1,
                                 CTRL_ROTATE_AUTO,
                                 CTRL_ROTATE_RIGHT,
                                 CTRL_ROTATE_180,
                                 CTRL_ROTATE_LEFT,
                                 CTRL_FLIP_HORIZ,
                                 CTRL_FLIP_VERT,
                                 CTRL_TRANSPOSE,
                                 CTRL_TRANSVERSE,
                                 CTRL_CROP,
                                 CTRL_SMARTSCALE
                               };


static choice_settings ch = { 2, 1, 0, 1, 0 };



static char * actions[] = { "rotate auto",
                            "rotate 90",
                            "rotate 180",
                            "rotate 270",
                            "flip horizontal",
                            "flip vertical",
                            "transpose",
                            "transverse",
                            "crop",
                            "scale 16/8",
                            "scale 4/8",
                            "scale 2/8",
                            "scale 1/8",
                            "skip file",
                            "copy file",
                            "no r",
                            "no xif",
                            "no transform"
                          };

static char * copyopt[] = { "copy none",
                            "copy comments",
                            "copy all"
                          };


static char * subdir[] = { "rotate auto",
                           ".rotright",
                           ".rot180",
                           ".rotleft",
                           ".fliphor",
                           ".flipver",
                           ".transpose",
                           ".transverse",
                           ".crop",
                           ".scale2",
                           ".scaleh",
                           ".scaleq",
                           ".scalee",
                           "skip file",
                           "copy file",
                           "no r",
                           "no xif",
                           ".notrsform"
                         };


static char * scale[] = {"x2", "1/2", "1/4", "1/8" };


static char *trimtx = "-trim ";

static crop_values crop;

int num_files = 0;
int next_file = 0;
static int auto_rotated_files;
static char * filename_store;
static int filename_store_offset;
static int filename_store_length;
static int filename_store_end;
int control_handle = 0;
static int nullevent_process_active;
static win_position control_pos;

static int tw_taskhandle;

static char infilename[256];
static char outfilename[256];
static char savepath[256];
static char jtran_cmd[640];
static char tw_cmd[1024];
static int use_save_path;
static int overwrite_original_file;

static int control_icon;       /* store the icon destination of a file drag */

int tw_processing = 0;

static char * copyptr;
static char * trimptr;
static int transform;
static int scale_index = 1;

static int settings_handle = 0;

static int use_outfile_sw;
static fstat fin;

static os_error  *control_close (int w, int userhandle);
static os_error  *settings_close (int w, int userhandle);
static os_error * get_exifcopy_index (void);


static char mboxbuf[256];
/**************************************/






/********************************************************************/

os_error * cjl_fs_copy(char * from, char * to, int action)
{
  os_regset rx;

  rx.r[0] = 26;
  rx.r[1] = (int)from;
  rx.r[2] = (int)to;
  rx.r[3] = action;
  rx.r[4] = 0;
  rx.r[5] = 0;
  rx.r[6] = 0;
  rx.r[7] = 0;
  rx.r[8] = 0;

  return (os_swix (OS_FSControl, &rx));
}







void control_transformended (void)
{
  int type;
  int orientation = 0;
  int or_offset;
  fstat fout;

  fs_exists (outfilename, &type);
  if (type == 1)
  {
    if ((transform == ROTATE_AUTO) && (ch.copyidx == 2) && ch.write_back_orientation)
    {
      /* write back the normal orientation value, but only if the exif data
         has been copied across and there is orientation data present */
      if (exif_get_orientation (outfilename, &orientation, &or_offset))
        write_back_normal_orientation (or_offset, outfilename);
    }
    if (ch.retain_timestamp)
    {
      /* Only attempt to write back orientation if using auto rotation and
      ** the complete exif data is being copied into the transformed file
      ** otherwise there will be no exif block to write in to */
      /* reset the time/date stamp if set to retain */
      fs_stat (outfilename, &fout);
      fout.load = fin.load;
      fout.exec = fin.exec;
      fs_stamp (outfilename, &fin);
    }
    fs_settype (outfilename, JPEG);
  }

  control_processfile();
  return;
}





static os_error *taskclosemessage (wimp_msgstr * msg, int *ack)
{
  if ( msg->hdr.task == tw_taskhandle )
  {
    control_transformended();
  }
  return (NULL);
  USE (ack);
  USE (msg);
}





static os_error *clear_filelist (void)
{
  os_error   *err = NULL;

  num_files = 0;
  filename_store_offset = 0;
  filename_store_length = 0;
  tw_processing = 0;
  /* free memory */
  err = flex_free ((flex_ptr) &filename_store);
  return (err);
}





/* We use the two sys vars JT$In and JT$Out for the input file
** and output file paths to limit the length of the various
** command lines
*/

os_error *control_processfile (void)
{
  os_error   *err;
  char * ptr;
  char *p;
  int process_file;
  int orientation = 0;
  char * actionptr;
  char * subdirptr;
  int this_action;
  char msg[64];


  err = NULL;
  process_file = 1;

  if (next_file > num_files)
  {
    /* processing finished */
    clear_filelist ();
    /* rem wimp msg handler */
    taskwindow_removemessages();
    remmessage (taskclosemessage, wimp_MCLOSETASK);
    if (transform == ROTATE_AUTO)
    {
      sprintf (msg, "Processing completed - %d files rotated", auto_rotated_files);
      status_update_status (msg);
      status_shade_icons ();
    }
    else
      status_updatefinished();
  }
  else
  {
    if (!tw_killed)
    {

      /* get the filename and update the offset*/
      ptr = filename_store + filename_store_offset;
      strcpy (infilename, ptr);

      filename_store_offset += strlen (ptr) + 1;

      /* Set the action for this pass */
      /* first set default */
      this_action = transform;
      /* Now deal with auto rotation, if set */
      if (transform == ROTATE_AUTO)
      {
        /* need to try to read exif data to find rotation required
        ** (if any) */

        if (exif_get_orientation (infilename, &orientation, NULL))
        {
          switch (orientation)
          {
            case 2:
              /* flip horizontal to correct */
              this_action = FLIP_HOR;
              break;

            case 3:
              /* rotate 180 to correct */
              this_action = ROTATE_180;
              break;

            case 4:
              /* flip vertical to correct */
              this_action = FLIP_VER;
              break;

            case 5:
              /* transpose to correct */
              this_action = TRANSPOSE;
              break;

            case 6:
              /* rotate right to correct */
              this_action = ROTATE_90;
              break;

            case 7:
              /* transverse to correct */
              this_action = TRANSVERSE;
              break;

            case 8:
              /* rotate left to correct */
              this_action = ROTATE_270;
              break;

            default:
              /* 1 is normal orientation, any other value >8 or <1 is invalid */
              this_action = NO_ROTATE;
              /* if not using save path skip all */
              if (!use_save_path) process_file = 0;
              break;

          }

        }
        else
        {
          /* Don't process this file */
          this_action = NO_EXIF;
          process_file = 0;
        }

      }

      /* do we process this file? */
      if (process_file)
      {
        /* Get the infile details (time stamp) */
        fs_stat (infilename, &fin);
        /* update filename field and status info */
        p = fs_leaf(infilename);
        status_update (p, this_action, &pset);

        /* set the action ptrs */
        if ( this_action != NO_ROTATE )
        {
          actionptr = actions[this_action];
          subdirptr = subdir[this_action];
        }

        /* setup output filename */
        if (overwrite_original_file)
        {
          strcpy (outfilename, infilename);
        }
        else
        {
          if (use_save_path)
          {
            sprintf (outfilename, "%s.%s", savepath, p);
            if (this_action == NO_ROTATE)
            {
              /* do we copy file to new location? */
              if (ch.copy_image)
              {
                if (strcmp (infilename, outfilename))
                {
                  /* not the same path so copy */
                  status_update (NULL, COPY_FILE, NULL);
                  err = cjl_fs_copy(infilename, outfilename, 0);
                }
              }
              next_file++;
              control_processfile();
              return(err);
            }
          }
          else
          {
            p--;
            *p = 0;
            strcpy (outfilename, infilename);
            *p = '.';
            strcat (outfilename, subdirptr);
            fs_cdir (outfilename);
            strcat (outfilename, p);
          }
        }

        /* If this is an auto-rotate, inc the file count */
        if (transform == ROTATE_AUTO) auto_rotated_files++;

        /* Set up the file path sys variables */
        /* Use the jpeg_cmd buffer for workspace */
        sprintf (jtran_cmd, "set JT$In %s", infilename);
        os_cli (jtran_cmd);
        sprintf (jtran_cmd, "set JT$Out %s", outfilename);
        os_cli (jtran_cmd);

        /* set up jpegtran cmd */
        /* Needs to be enclosed in quotes */
        /* allow two file cmd or use of -outfile switch */
        p = jtran_cmd;
        /* Cmd to be enclosed in quotes - start with an escaped " (\") */
        p += sprintf (p, "\"<TranJpegResA$Dir>.jpegtran ");

        if (pset.optimise)
          p += sprintf (p, "-opt ");

        if (pset.progressive)
          p += sprintf (p, "-progr ");

        if (pset.greyscale)
          p += sprintf (p, "-grays ");

        if (pset.strip_exif)
        {
          p += sprintf (p, "-copy none ");
        }
        else
        {
          p += sprintf (p, "-%s %s", copyptr, trimptr);
        }

        if (pset.transform)
        {
          if (transform == CROP)
          {
            p += sprintf (p, "-crop %dx%d+%d+%d ", crop.w, crop.h, crop.x, crop.y);
          }
          else
            p += sprintf (p, "-%s ", actionptr);
        }

        /* Cmd to be enclosed in quotes - end with an escaped " */
        if (use_outfile_sw)
        {
          p += sprintf (p, "-outfile <JT$Out> <JT$In>\"");
        }
        else
        {
          p += sprintf (p, "<JT$In> <JT$Out>\"");
        }

        /* now the tw cmd */
        sprintf (tw_cmd, "*TaskWindow %s -WimpSlot 5000K -name jpgtr "
                 "-quit -task &%.8X -txt &00000999", jtran_cmd, taskhandle);

        err = wos_startnewtask ( tw_cmd, &tw_taskhandle);
        if ( err )
        {
          messagebox (err->errmess);
          num_files = 0;
          control_transformended ();
          err = NULL;
        }

        next_file++;
        if (tw_taskhandle == 0)
        {
          /* Task must have already completed */
          control_transformended();
        }

      }
      else
      {
        /* update filename field */
        p = fs_leaf(infilename);
        status_update (p, this_action, NULL);
        next_file++;
        control_processfile();
      }

    }
  }
  return (err);
}






static os_error *control_update_file_count (void)
{
  os_error   *err;
  char str[5];

  err = NULL;

  if (control_handle)
  {
    sprintf (str, "%d", num_files);
    err = writeiconc ( control_handle, CTRL_FILE_COUNT, str );
  }


  return (err);
}






/* null event handler to complete the file load and open (if necessary)
** the control window */

static void control_start_processing (int handle)
{
  status_close (0, 0);
  control_open ();
  control_update_file_count ();
  next_file = 1;
  remzeroevent (control_start_processing, 0);
  nullevent_process_active = 0;
  return;

  USE (handle);
}



/* Extracts the text from the writable icon, and converts to an integer value.
** Specific for the writables in the control window */

static os_error * geticonvalue ( int i, int *val)
{
 os_error * err;
 wimp_icon iconinfo;

 err=wimp_get_icon_info(control_handle, i, &iconinfo);
 if(!err)
 {
   *val = atoi(iconinfo.data.indirecttext.buffer);
 }
 return(err);
}





static os_error *setup_processing (void)
{
  os_error   *err;


  err = NULL;

  if (num_files > 0)
  {
    /* Check save path for valid path */
    if ( use_save_path && (*savepath == 0))
    {
      main_issuewarning (WNOPATH);
      return (err);
    }

    /* Check there is something to do */
    if ( !pset.optimise && !pset.progressive && !pset.transform
                        && !pset.greyscale && !pset.strip_exif )
    {
      main_issuewarning (WNOPROC);
      return (err);
   }

    if (pset.transform == 0)
      transform = NO_TRANSFORM;
    else
    {
      transform = pset.transform_type;

      /* If crop is chosen, then get the crop area values now */
      if (transform == CROP)
      {
        err = geticonvalue (CTRL_CROP_WIDTH, &crop.w);
        err = geticonvalue (CTRL_CROP_HEIGHT, &crop.h);
        err = geticonvalue (CTRL_CROP_X_ORIG, &crop.x);
        err = geticonvalue (CTRL_CROP_Y_ORIG, &crop.y);
      }
    }

    /* Is it a smart scale? If so adjust the action_idx value for
    ** the correct scale factor
    */
    if (transform == SMARTSCALE_D)
    {
      transform += scale_index;
    }

    /* reset ptr offset to start of filenames */
    filename_store_offset = 0;
    err = taskwindow_addmessages();
    if (err)
    {
      main_issuewarning (WTWADDMSGTFAIL);
      return (NULL);
    }

    err = addmessage (taskclosemessage, wimp_MCLOSETASK);
    get_exifcopy_index ();
    if (ch.trim_edge)
      trimptr = trimtx;
    else
      trimptr = "";

    tw_processing = 1;
    auto_rotated_files = 0;

    status_updatestarted ();
    control_close (0,0);

    err = control_processfile ();
  }
  else
  {
    main_issuewarning (WNOFILE);
  }
  return (err);
}




/*********************************************************************/
/* Routines dealing with the settings window */
/*********************************************************************/


static char * path="<TranJPEG$Dir>.Resources.Config";
static char * cwritepath = "<Choices$Write>.CJohnson.TranJPEG.Config";
static char * creadpath = "Choices:CJohnson.TranJPEG.Config";


static contag contable[] =
{
  "TrimEdge", CONINT, &ch.trim_edge, NULL, NULL,
  "CopyExifType", CONINT, &ch.copyidx, NULL, NULL,
  "CopyImage", CONINT, &ch.copy_image, NULL, NULL,
  "WriteOrientation", CONINT, &ch.write_back_orientation, NULL, NULL,
  "UseOutfileSw", CONINT, &use_outfile_sw, NULL, NULL,
  "KeepTimeStamp", CONINT, &ch.retain_timestamp, NULL, NULL,

  NULL, 0, NULL, NULL, NULL
};




static conlink configlink =
{
 "TranJPEG",
 NULL,
 NULL
};




static os_error * configsave(void)
{
  char *spath;

  if (os_read_var_size ("Choices$Write") != 0)
  {
    /* use the choices$write path to save config file */
    spath = cwritepath;
    /* ensure all directories exist */
    fs_create_dir_chain (spath);
  }
  else
  {
    spath = path;
  }
  return(saveconfig(spath));
}





os_error * configinit(void)
{
  os_error * err;
  char *spath;
  int type;

  err = addcontable (contable, &configlink);

  if (os_read_var_size ("Choices$Path") != 0)
  {
    /* use the choices path to load config file */
    spath = creadpath;
  }
  else
  {
    /* Look inside app directory */
    spath = path;
  }

  fs_exists (spath, &type);

  if (!err && type) err = loadconfig (spath);

  use_outfile_sw = 1;
  use_save_path = 0;
  overwrite_original_file = 0;

  return (NULL);
}







static os_error * get_exifcopy_index (void)
{
  os_error   *err;
  wimp_i  list[4];
  wimp_which_block blk;

  blk.window = settings_handle;
  blk.bit_mask = 0x3F0000;
  blk.bit_set = 0x210000;

  err = wimp_which_icon ( &blk, (int*)&list );

  switch (list[0])
  {
    case SET_COPY_ALL:
      ch.copyidx = 2;
      break;

    case SET_COPY_COMM:
      ch.copyidx = 1;
      break;

    case SET_COPY_NONE:
      ch.copyidx = 0;
      break;

  }
  copyptr = copyopt[ch.copyidx];
  return(err);
}






static os_error *settings_click (int handle, int userhandle, wimp_mousestr * m)
{
  os_error   *err;

  err = NULL;

  switch (m->i)
  {
    case SET_TRIM_BLOCKS:
      selectst (settings_handle, SET_TRIM_BLOCKS, ch.trim_edge ^= 1 );
      break;

    case SET_COPY_NONE:
    case SET_COPY_COMM:
    case SET_COPY_ALL:
      if (m->bbits == 1) select ( settings_handle, m->i );
      get_exifcopy_index();
      break;

    case SET_COPY_IMAGE:
      selectst (settings_handle, SET_COPY_IMAGE, ch.copy_image ^= 1 );
      break;

    case SET_WRITE_ORIENT:
      selectst (settings_handle, SET_WRITE_ORIENT, ch.write_back_orientation ^= 1 );
      break;

    case SET_KEEP_TIMESTAMP:
      selectst (settings_handle, SET_KEEP_TIMESTAMP, ch.retain_timestamp ^= 1 );
      break;

    case SET_SAVE:
      err = configsave();
      if (m->bbits != 1) settings_close (0, 0);
      break;


  }

  return (err);

  USE (userhandle);
  USE (handle);
}








static os_error  *settings_close (int w, int userhandle)
{
  os_error   *err;

  err = NULL;

  if (settings_handle)
  {
    remcloseevent (settings_close, settings_handle, 0);
    remclickevent (settings_click, settings_handle, 0);
    remhelpevent (NULL, settings_handle, TSETTINGS);

    err = closedown (&settings_handle);

  }
  return (err);

  USE (userhandle);
  USE (w);
}





static os_error *settings_setup (void)
{
  os_error   *err;
  int ricon;

  err = NULL;

  /* set radio icon for copy exif */
  switch (ch.copyidx)
  {
    case 1:
      ricon = SET_COPY_COMM;
      break;

    case 2:
      ricon = SET_COPY_ALL;
      break;

    case 0:
    default:
      ricon = SET_COPY_NONE;
      break;

  }
  select ( settings_handle, ricon );

  selectst ( settings_handle, SET_TRIM_BLOCKS, ch.trim_edge );
  selectst ( settings_handle, SET_COPY_IMAGE, ch.copy_image );
  selectst ( settings_handle, SET_WRITE_ORIENT, ch.write_back_orientation );
  selectst ( settings_handle, SET_KEEP_TIMESTAMP, ch.retain_timestamp );

  return (err);
}




static os_error *settings_open (void)
{
  os_error   *err;
  wimp_wstate ws;
  int h, w;
  mousestr   mouse;


  if (settings_handle)
  {
    err = forward (settings_handle, 0, NULL);
  }
  else
  {
    err = createwindow (TSETTINGS, &settings_handle);
    if (!err)
    {
      addcloseevent (settings_close, settings_handle, 0);
      addclickevent (settings_click, settings_handle, 0);
      addhelpevent (NULL, settings_handle, TSETTINGS);

      settings_setup ();

      /* where was the click? */
      /* open window at click */
      err = getpointer (&mouse);
      if (!err)
      {
          err = wimp_get_wind_state (settings_handle, &ws);
          w = ws.o.box.x1 - ws.o.box.x0;
          h = ws.o.box.y1 - ws.o.box.y0;
          ws.o.box.x0 = mouse.x - 80;
          ws.o.box.y1 = mouse.y + 56;
          ws.o.box.x1 = ws.o.box.x0 + w;
          ws.o.box.y0 = ws.o.box.y1 - h;
          err = wimp_open_wind (&ws.o);
      }
    }
  }
  return (err);
}







/*********************************************************************/
/* Routines to set up a path to which to save the transformed files */
/*********************************************************************/




static os_error * pathsave(char * name, int type)
{
 char * p;

 strcpy (savepath, name);

 p = fs_leaf (savepath);

 if (p)
 {
  if (p > savepath && (*(p-1)) == '.') p--;
  *p = 0;
 }

 if (control_handle)
   wimp_set_icon_state ( control_handle,
                         CTRL_SAVEPATH,
                         (wimp_iconflags)0,
                         (wimp_iconflags)0);

 return(NULL);

 USE (type);
}


static os_error *menuwindow_atpointer (int handle)
{
  os_error * err;
  mousestr   mouse;

  err = getpointer (&mouse);
  if (!err) err = wimp_create_menu ((wimp_menustr *)handle, mouse.x - 80, mouse.y + 56);

  return (err);
}


/*
  Set the window extent of the 'Save as' dialogue so that it no longer
  shows the two redundant icons. This is the option chosen here. It
  works OK because the 'Save as' dialogue is always recreated from the
  template file whenever it is needed. The sprite icon type is also
  set to DIRECTORY to be more consistent with the action.
*/

static os_error * opensavepath(void)
{
  os_error * err;
  int        handle;
  wimp_redrawstr redrw;
  int dx, dy;

  err = setsave (savepath, DIRECTORY, pathsave, &handle);

 if(!err)
 {
   getcurdeltas (&dx, &dy);
   redrw.w = handle;
   redrw.box.x0 = 0;
   redrw.box.y0 = -(54 * dy);
   redrw.box.x1 = 88 * dx;
   redrw.box.y1 = 0;
   wimp_set_extent ( &redrw );
   /* set window title */
   wos_winsettitle ( handle, "Set path" );

   /* delete the 'OK' icon */
   wimp_delete_icon ( handle, 2 );
   err = menuwindow_atpointer (handle);
 }

 return(err);
}




static os_error * scalemenu_decode (int * menu)
{
  int c;
  os_error      * err;
  wimp_icon       isblock;

  c = menu[0];
  if (c >= 0)
  {
    /* There has been a choice */
    scale_index = c;
    err = setindirect (control_handle, CTRL_SMARTSCALE_SET,
                       sizeof (scale[scale_index]), scale[scale_index]);

    if (!err) err = wimp_get_icon_info(control_handle, CTRL_SMARTSCALE_SET, &isblock);
    if (!err) err = refreshicon(control_handle, &isblock);
  }
  return(NULL);
}



static os_error * openscalemenu (int handle)
{
  setmenufns (MSCALE, scalemenu_decode, 0);
  return (openupmenu (MSCALE));

  USE(handle);
}



/* Only unshade the crop area icons if the crop radio button is selected */

static void shade_crop_icons (void)
{
  int flag;

  /* Need to take account of the crop icon being 'selected', but shaded
  ** because the transform icon is not ticked */
  flag = ((pset.transform_type != CROP) || !pset.transform);
  shadeiconst (control_handle, CTRL_CROP_WIDTH, flag);
  shadeiconst (control_handle, CTRL_CROP_HEIGHT, flag);
  shadeiconst (control_handle, CTRL_CROP_X_ORIG, flag);
  shadeiconst (control_handle, CTRL_CROP_Y_ORIG, flag);
  shadeiconst (control_handle, CTRL_CROP_WIDTH_LAB, flag);
  shadeiconst (control_handle, CTRL_CROP_HEIGHT_LAB, flag);
  shadeiconst (control_handle, CTRL_CROP_X_ORIG_LAB, flag);
  shadeiconst (control_handle, CTRL_CROP_Y_ORIG_LAB, flag);
  return;
}






static void shade_scale_icons (void)
{
  int flag;

  flag = ((pset.transform_type != SMARTSCALE_D) || !pset.transform);
  shadeiconst (control_handle, CTRL_SMARTSCALE_SET, flag );
  shadeiconst (control_handle, CTRL_SMARTSCALE_POPUP, flag );
  return;
}





static void shade_transform_icons (void)
{
  shadeiconst (control_handle, CTRL_ROTATE_LEFT, !pset.transform );
  shadeiconst (control_handle, CTRL_ROTATE_180, !pset.transform );
  shadeiconst (control_handle, CTRL_ROTATE_RIGHT, !pset.transform );
  shadeiconst (control_handle, CTRL_ROTATE_AUTO, !pset.transform );
  shadeiconst (control_handle, CTRL_FLIP_HORIZ, !pset.transform );
  shadeiconst (control_handle, CTRL_FLIP_VERT, !pset.transform );
  shadeiconst (control_handle, CTRL_TRANSPOSE, !pset.transform );
  shadeiconst (control_handle, CTRL_TRANSVERSE, !pset.transform );
  shadeiconst (control_handle, CTRL_SMARTSCALE, !pset.transform );
  shadeiconst (control_handle, CTRL_SMARTSCALE_SET, !pset.transform );
  shadeiconst (control_handle, CTRL_SMARTSCALE_POPUP, !pset.transform );
  shadeiconst (control_handle, CTRL_TRANSVERSE_IM, !pset.transform );
  shadeiconst (control_handle, CTRL_TRANSPOSE_IM, !pset.transform );
  shadeiconst (control_handle, CTRL_CROP, !pset.transform );
  shade_scale_icons ();
  shade_crop_icons ();
  return;
}





static void shade_save_icons (void)

{
  if (overwrite_original_file)
  {
    shadeicon (control_handle, CTRL_SAVEPATH_POPUP);
    shadeicon (control_handle, CTRL_SAVEPATH);
    shadeicon (control_handle, CTRL_USE_SAVEPATH);
  }
  else
  {
    unshadeicon (control_handle, CTRL_USE_SAVEPATH);
    shadeiconst (control_handle, CTRL_SAVEPATH_POPUP, !use_save_path);
    shadeiconst (control_handle, CTRL_SAVEPATH, !use_save_path);
  }

  return;
}






static os_error *control_click (int handle, int userhandle, wimp_mousestr * m)
{
  os_error   *err;
  os_error   *warn;
  int         answer;
  int i;


  err = NULL;

  switch (m->i)
  {
    case CTRL_PROCESS:
      setup_processing ();
      break;

    case CTRL_ROTATE_LEFT:
    case CTRL_ROTATE_RIGHT:
    case CTRL_ROTATE_180:
    case CTRL_FLIP_HORIZ:
    case CTRL_FLIP_VERT:
    case CTRL_TRANSPOSE:
    case CTRL_TRANSVERSE:
    case CTRL_ROTATE_AUTO:
    case CTRL_SMARTSCALE:
    case CTRL_CROP:
      for (i = 0; i < NUM_TRANSFORMS; i++)
      {
        if (pset.transform_icons[i] == (m->i))
        {
          pset.transform_type = i;
          break;
        }
      }
      if (m->bbits == 1) select ( control_handle, m->i );
      shade_crop_icons ();
      shade_scale_icons ();
      break;

    case CTRL_OPTIMISE:
      selectst (control_handle, CTRL_OPTIMISE, pset.optimise ^= 1 );
      break;

    case CTRL_PROGRESSIVE:
      selectst (control_handle, CTRL_PROGRESSIVE, pset.progressive ^= 1 );
      break;

    case CTRL_GREYSCALE:
      selectst (control_handle, CTRL_GREYSCALE, pset.greyscale ^= 1 );
      shade_transform_icons ();
      break;

    case CTRL_STRIP_EXIFDATA:
      selectst (control_handle, CTRL_STRIP_EXIFDATA, pset.strip_exif ^= 1 );
      shade_transform_icons ();
      break;

    case CTRL_TRANSFORM:
      selectst (control_handle, CTRL_TRANSFORM, pset.transform ^= 1 );
      shade_transform_icons ();
      break;

    case CTRL_CLEAR:
      if (filename_store)
      {
        warn = geterror (QCLEARFILE);
        err = confirm (CONYN, &answer, warn->errmess);
        if (answer)
        {
          clear_filelist ();
          control_update_file_count ();
        }
      }
      break;

    case CTRL_USE_SAVEPATH:
      selectst (control_handle, CTRL_USE_SAVEPATH, use_save_path ^= 1 );
      shadeiconst (control_handle, CTRL_SAVEPATH_POPUP, !use_save_path);
      shadeiconst (control_handle, CTRL_SAVEPATH, !use_save_path);
      break;

    case CTRL_SAVEPATH_POPUP:
      opensavepath();
      break;

    case CTRL_OVERWRITE_ORIGINAL:
      selectst (control_handle, CTRL_OVERWRITE_ORIGINAL, overwrite_original_file ^= 1 );
      shade_save_icons ();
      break;

    case CTRL_OPEN_SETTINGS:
      err = settings_open ();
      break;

    case CTRL_SMARTSCALE_POPUP:
      err = openscalemenu (handle);
      break;

  }

  return (err);

  USE (userhandle);
}





static os_error  *control_close (int w, int userhandle)
{
  os_error   *err;

  err = NULL;

  wos_savewinposition (control_handle, &control_pos);
  remclickevent (control_click, control_handle, 0);
  remcloseevent (control_close, control_handle, 0);
  remdataload (control_handle, 0, control_loadfiles);
  remdataloadtype (control_handle, 0, control_loadfiletype);
  remhelpevent (NULL, control_handle, TCONTROL);

  err = closedown (&control_handle);

  if (settings_handle) settings_close(0, 0);
  return (err);

  USE (userhandle);
  USE (w);
}




static os_error * setup_control (void)
{
  select ( control_handle, pset.transform_icons[pset.transform_type] );

  selectst ( control_handle, CTRL_OPTIMISE, pset.optimise );
  selectst ( control_handle, CTRL_PROGRESSIVE, pset.progressive );
  selectst ( control_handle, CTRL_TRANSFORM, pset.transform );
  selectst ( control_handle, CTRL_GREYSCALE, pset.greyscale );
  selectst ( control_handle, CTRL_STRIP_EXIFDATA, pset.strip_exif );
  selectst ( control_handle, CTRL_USE_SAVEPATH, use_save_path );
  selectst ( control_handle, CTRL_OVERWRITE_ORIGINAL, overwrite_original_file );
  shade_save_icons ();
  shade_transform_icons ();

  return (NULL);
}




os_error *control_open (void)
{
  os_error   *err;

  if (control_handle)
  {
    err = forward (control_handle, 0, NULL);
  }
  else
  {
    err = createwindow (TCONTROL, &control_handle);
    if (!err)
    {
      addcloseevent (control_close, control_handle, 0);
      addclickevent (control_click, control_handle, 0);
      /* A drag of jpeg file(s) */
      adddataload (control_handle, 0, control_loadfiles);
      adddataloadtype (control_handle, 0, control_loadfiletype);
      addhelpevent (NULL, control_handle, TCONTROL);

      /* set the indirect address of writable to point at savepath */
      /* The display will then mirror changes in savepath, with update of icon */
      setindirect(control_handle, CTRL_SAVEPATH, sizeof(savepath), savepath);
      setindirect (control_handle, CTRL_SMARTSCALE_SET,
                         sizeof (scale[scale_index]), scale[scale_index]);

      setup_control ();

      if (control_pos.reopen)
      {
	open (control_handle, control_pos.x0, control_pos.y0,
	      control_pos.x1, control_pos.y1, 0, 0, -1);
      }
      else
      {
	popup (control_handle, 0);
	control_pos.reopen = 1;
      }
      control_update_file_count ();
    }
  }
  return (err);
}





os_error *control_loadfiletype (int type, mousestr * m, int userhandle, int *method)
{
  control_icon = m->icon;

  if ((type == JPEG) || (type == DIR))
  {
    if (*method == 0) *method = 1;
  }

  return (NULL);

  USE (m);
  USE (userhandle);

}


#define MAX_FILEPATH_SPACE 240


static os_error *check_filename_buffer (void)
{
  os_error   *err;

  err = NULL;

  if (filename_store)
  {
    /* Check we have space in currently allocated buffer */
    if ( (MAX_FILEPATH_SPACE + filename_store_offset) >= filename_store_length )
    {
      /* need more memory */
      err = flex_extend ((flex_ptr) &filename_store,
                                    filename_store_length + FILENAME_STORE_CHUNK );
      if (err)
      {
        main_issuewarning (WFLEXEXTFAIL);
      }
      else
      {
        filename_store_length += FILENAME_STORE_CHUNK;

      }
    }
  }
  else
  {
    /* must be the first file in drag */
    /* allocate some memory */

    err = flex_alloc ((flex_ptr) &filename_store, FILENAME_STORE_CHUNK);
    if (err)
    {
      main_issuewarning (WFLEXALFAIL);
    }
    else
    {
      /* zero/null the counts, etc */
      num_files = 0;
      filename_store_offset = 0;
      filename_store_length = FILENAME_STORE_CHUNK;

    }
  }

  return (err);
}


/* Struct of data returned by OS_GBPB 12 */

typedef struct fileinfo
{
  int  load;
  int  exec;
  int  length;
  int  attributes;
  int  objtype;
  int  filetype;
  char filename[256];
} fileinfo;



/* Read the next object in the given directory
   Returns name of object
   object offset for next read (-1 if finished)
   number of objects read (1 for object read, 0 if nothing read)
*/

static os_error *get_next_object (char *parentdir, int *obj_offset,
                                  fileinfo *buff, int size, int *read)
{
  os_error   *err;
  os_regset   rx;

  /* Use OS_GBPB 12 to read file info from a directory */

  rx.r[0] = 12;
  rx.r[1] = (int) parentdir;
  rx.r[2] = (int) buff;
  rx.r[3] = 1;                        /* ask for one object */
  rx.r[4] = *obj_offset;
  rx.r[5] = size;
  rx.r[6] = 0;                        /* match all files */

  err = os_swix (OS_GBPB, &rx);

  /* save back the object offset and number of objects read */
  *obj_offset = rx.r[4];
  *read = rx.r[3];
  return (err);
}





static void add_file (char *name)
{
  size_t len;
  char *ptr;

  if (!check_filename_buffer())
  {
    /* allow for terminating null */
    len = strlen (name) + 1;
    /* store the filename */
    ptr = filename_store + filename_store_offset;
    strcpy (ptr, name);
    /* null start of next name position */
    ptr[len] = '\0';
    /* update the offset */
    filename_store_offset += len;

    num_files += 1;

    if (!nullevent_process_active)
    {
      addzeroevent (control_start_processing, 0);
      nullevent_process_active = 1;
    }
  }
  return;
}





os_error *control_loadfiles (char *name, int type, int userhandle, int xvolatile)
{

  int         obj_offset;
  fileinfo    buff;
  int         read;
  char        fname[256];


  /* Only record the filename if we are NOT already processing.
   * This is to allow drag to status window, but only if the
   * processing has been completed
  */
  if (!tw_processing)
  {
    if ( type == JPEG)
    {
      add_file (name);
    }
    else
    {
      if (type == DIR)
      {
        /* Was it a drag to the set path writable? */
        if (control_icon == CTRL_SAVEPATH)
        {
          strcpy (savepath, name);
          wimp_set_icon_state ( control_handle,
                                CTRL_SAVEPATH,
                                (wimp_iconflags)0,
                                (wimp_iconflags)0);
        }
        else
        {
          /* need to check the files in this directory and store paths
          ** of any JPEG files */
          obj_offset = 0;
          while (obj_offset != -1)
          {
            get_next_object (name, &obj_offset, &buff, sizeof (fileinfo), &read);
            if (read)
            {
              /* a name has been returned */
              if (buff.filetype == JPEG)
              {
                sprintf (fname, "%s.%s", name, buff.filename);
                add_file (fname);
              }
            }
          }
        }

      }
    }
  }

  return (NULL);

  USE (type);
  USE (userhandle);
  USE (xvolatile);
}













