/* run filer_action(s)
 * we may need to perform operations on files from multiple
 * sources and fileraction doesn't currently allow this
 * so we need to run a separate action for each source directory */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "kernel.h"
#include "swis.h"

#include "tboxlibs/wimplib.h"
#include "tboxlibs/wimp.h"
#include "tboxlibs/flex.h"

#include "pinboard.h"
#include "options.h"
#include "drag.h"
#include "indirected.h"
#include "fs.h"
#include "fileraction.h"
#include "msgtrans.h"
#include "mystring.h"

#include "debug.h"

static void fileraction_cmd_move(char *source,char *destdir,int opts)
{
  _kernel_oserror *e;

  // append '*' to destination to transfer leafname
  char *dest_leaf;
  if (!flex_alloc((flex_ptr)&dest_leaf,3+strlen(destdir))) {
    printf("Not enough memory to move files\n");
    return;
  }

  sprintf(dest_leaf,"%s.*",destdir);

  e=_swix(OS_FSControl,_INR(0,3),26,source,dest_leaf,(1<<0) | (1<<7) | (OPTIONS_FORCE(opts)<<1) | (OPTIONS_CONFIRMCOPY(opts)<<3) | (OPTIONS_VERBOSE(opts)<<4) | (OPTIONS_VERBOSE(opts)<<8) | (OPTIONS_NEWER(opts)<<12));

  flex_free((flex_ptr)&dest_leaf);

  if (e) {
    printf("%s\n",e->errmess);
  }
}

static void fileraction_cmd_copy(char *source,char *destdir,int opts)
{
  _kernel_oserror *e;

  // append '*' to destination to transfer leafname
  char *dest_leaf;
  if (!flex_alloc((flex_ptr)&dest_leaf,3+strlen(destdir))) {
    printf("Not enough memory to move files\n");
    return;
  }

  sprintf(dest_leaf,"%s.*",destdir);

  e=_swix(OS_FSControl,_INR(0,3),26,source,dest_leaf,(1<<0) | (OPTIONS_FORCE(opts)<<1) | (OPTIONS_CONFIRMCOPY(opts)<<3) | (OPTIONS_VERBOSE(opts)<<4) | (OPTIONS_VERBOSE(opts)<<8) | (OPTIONS_NEWER(opts)<<12));

  flex_free((flex_ptr)&dest_leaf);

  if (e) {
    printf("%s\n",e->errmess);
  }
}

static void fileraction_cmd_delete(char *source,int opts)
{
  _kernel_oserror *e;

  e=_swix(OS_FSControl,_INR(0,1)|_IN(3),27,source,(1<<0) | (OPTIONS_FORCE(opts)<<1) | (OPTIONS_CONFIRMDELETE(opts)<<3) | (OPTIONS_VERBOSE(opts)<<4) | (OPTIONS_VERBOSE(opts)<<8));

  if (e) {
    printf("%s\n",e->errmess);
  }
}

static void fileraction_cmd_settype(char *source,int filetype,int opts)
{
  _kernel_oserror *e;

  if (OPTIONS_CONFIRMCOPY(opts)) {
    int key;
    printf("Set file type of %s (y/N)?",source);
    _swix(OS_Confirm,_OUT(0),&key);
    if (key!='y') return;
  }
  if (OPTIONS_VERBOSE(opts)) {
    printf("Setting type of %s to &%03x\n",source,filetype);
  }

  e=_swix(OS_File,_INR(0,2),18,source,filetype);

  if (e) {
    printf("%s\n",e->errmess);
  }
}

static void fileraction_cmd_count(char *source,int opts)
{
  _kernel_oserror *e;

  e=_swix(OS_FSControl,_INR(0,1)|_IN(3),28,source,(1<<0) | (1<<4) | (1<<8) | (OPTIONS_CONFIRMCOPY(opts)<<3));

  if (e) {
    printf("%s\n",e->errmess);
  }
}

static void fileraction_cmd_stamp(char *source,int opts)
{
  _kernel_oserror *e;

  if (OPTIONS_CONFIRMCOPY(opts)) {
    int key;
    printf("Stamp %s (y/N)?",source);
    _swix(OS_Confirm,_OUT(0),&key);
    if (key!='y') return;
  }
  if (OPTIONS_VERBOSE(opts)) {
    printf("Stamping %s\n",source);
  }

  int buf[2]={3,0};

  e=_swix(OS_Word,_INR(0,1),14,buf);
  if (e==NULL) e=_swix(OS_File,_INR(0,3),9,source,buf[0],buf[1]);

  if (e) {
    printf("%s\n",e->errmess);
  }
}

static int fileraction_start(int *taskh)
{
  int slot_next,slot_free;

  if (*taskh!=-1) return 1;

  // check free space
  wimp_slot_size(-1,-1,NULL,&slot_next,&slot_free);
  if (slot_next+slot_free<65536) return 0;

  if (wimp_start_task("Filer_Action",taskh)!=NULL) {
    *taskh=-1;
    return 0;
  }

  return 1;
}


static int filer_copy_ok(char *dest,char *source)
{
  // ensure we don't do anything recursive
  if (strncasecmp(source,dest,strlen(source))!=0) return 1; // OK to continue - differ

  // if next char is . then it's a problem
  if (dest[strlen(source)]=='.' || dest[strlen(source)]<32) return 0;

  return 1;
}

static int fileraction_move_to_self(char *source,char *destdir)
{
  int offset;

  // this would result in the original file getting deleted :(
  if (strncasecmp(source,destdir,strlen(destdir))!=0) return 0; // different prefix

  // now check to see if there's any further directory tree
  for (offset=strlen(destdir)+1;*(source+offset);offset++) {
    if (*(source+offset)=='.') return 0; // different level
  }

  return 1; // it's a match
}

void fileraction_run(source origin,fileraction action,int flags,void *param,_backdrop_state *state)
{
  /* param may be a dest directory (copy), a filetype (settype) or nothing (delete, stamp) */
  /* stfn is the function to call if single tasking */

  char *sourcepath,*sourceleaf;
  char *current_dir=NULL;
  int done,i,opts,ok;
  int fa_task;
  int cmdwin=0;
  char title_token[8],*title;

  sprintf(title_token,"Act%d",(int)action);
  title=msgtrans_lookup(title_token);

  opts=options_get();
  /* keep iterating until nothing happens */  

  if (action==FILERACTION_DELETE) {
    if (state->delete_always_confirm) {
      opts|=__OPTIONS_CONFIRMDELETE;
    }
  }

  do {
    done=0;
    fa_task=-1;
    for (i=0;i<pinlist->count;i++) {

      if ((pinlist->info[i].icontype & 0xff)!=pin_type_empty && drag_is_selected(origin,i) && pin_not_marked(&pinlist->info[i])) {
        if (!pin_needs_recheck(&pinlist->info[i])) {
          // filetype?
          if (flags & FILERACTION_RUN_NO_DIRS) {
            int objtype;
            fs_get_types(indirected(pinlist->info[i].data.filename_offset,char *),&objtype,NULL);
            ok=objtype!=2;
          } else {
            ok=1;
          }

          // non-recursive
          if (action==FILERACTION_COPY || action==FILERACTION_MOVE) {
            if (!filer_copy_ok((char *)param,indirected(pinlist->info[i].data.filename_offset,char *))) ok=0;
          }

          if (ok) {
            if (fs_pathprefix_from_path(&sourcepath,indirected(pinlist->info[i].data.filename_offset,char *))) {
              if (current_dir==NULL) {
                if (!flex_alloc((flex_ptr)&current_dir,1+strlen(sourcepath))) {
                  msgtrans_report_error("NoMem");
                  flex_free((flex_ptr)&sourcepath);
                  return;
                }
                strcpy(current_dir,sourcepath);
              }
              if (strcmp(current_dir,sourcepath)==0) {
                // do this one!
                pin_set_marked(&pinlist->info[i]);

                if (fs_leafname_from_path(&sourceleaf,indirected(pinlist->info[i].data.filename_offset,char *))) {
                  int st=0;
                  if (sourceleaf[0]=='$' || !OPTIONS_FILERACTION(opts)) {
                    // can't run this one through fileraction
                    if (!cmdwin) {
                      wimp_command_window((int)title);
                      cmdwin=1;
                    }
                    st=1;
                  } else {
                    // this one goes though
                    if (!fileraction_start(&fa_task)) {
                      if (!cmdwin) {
                        wimp_command_window((int)title);
                        cmdwin=1;
                      }
                      st=1;
                    }
                  }
                  if (st) {
                    switch (action) {
                      case FILERACTION_COPY:
                      case FILERACTION_COPYLOCAL:
                        fileraction_cmd_copy(indirected(pinlist->info[i].data.filename_offset,char *),(char *)param,opts);
                        break;

                      case FILERACTION_DELETE:
                        fileraction_cmd_delete(indirected(pinlist->info[i].data.filename_offset,char *),opts);
                        break;

                      case FILERACTION_SETTYPE:
                        fileraction_cmd_settype(indirected(pinlist->info[i].data.filename_offset,char *),*((int *)param),opts);
                        break;

                      case FILERACTION_COUNT:
                        fileraction_cmd_count(indirected(pinlist->info[i].data.filename_offset,char *),opts);
                        break;

                      case FILERACTION_STAMP:
                        fileraction_cmd_stamp(indirected(pinlist->info[i].data.filename_offset,char *),opts);
                        break;

                      case FILERACTION_MOVE:
                        if (fileraction_move_to_self(indirected(pinlist->info[i].data.filename_offset,char *),(char *)param)==0)
                          fileraction_cmd_move(indirected(pinlist->info[i].data.filename_offset,char *),(char *)param,opts);
                        break;
                    }
                  } else {
                    if (action==FILERACTION_MOVE) {
                      if (fileraction_move_to_self(indirected(pinlist->info[i].data.filename_offset,char *),(char *)param)==0) {
                        _swix(FilerAction_SendSelectedDirectory,_INR(0,1),fa_task,sourcepath);
                        _swix(FilerAction_SendSelectedFile,_INR(0,1),fa_task,sourceleaf);
                      }
                    } else {
                      _swix(FilerAction_SendSelectedDirectory,_INR(0,1),fa_task,sourcepath);
                      _swix(FilerAction_SendSelectedFile,_INR(0,1),fa_task,sourceleaf);
                    }
                  }

                  done++;
                  flex_free((flex_ptr)&sourceleaf);
                }

              }
              flex_free((flex_ptr)&sourcepath);
            }

          }

        }
      }

    }
    if (fa_task!=-1) {
      // run this instance
      switch (action) {
        case FILERACTION_MOVE:
        case FILERACTION_COPY:
        case FILERACTION_COPYLOCAL:
            _swix(FilerAction_SendStartOperation,_INR(0,4),fa_task,action,
            ((OPTIONS_VERBOSE(opts)<<0) | (OPTIONS_CONFIRMCOPY(opts)<<1) | (OPTIONS_FORCE(opts)<<2) | (OPTIONS_NEWER(opts)<<3) | (OPTIONS_FASTER(opts)<<6)),
            (char *)param,1+strlen((char *)param));
            break;

        case FILERACTION_DELETE:
            _swix(FilerAction_SendStartOperation,_INR(0,4),fa_task,action,
            ((OPTIONS_VERBOSE(opts)<<0) | (OPTIONS_CONFIRMDELETE(opts)<<1) | (OPTIONS_FORCE(opts)<<2) | (OPTIONS_NEWER(opts)<<3) | (OPTIONS_CONFIRMDELETE(opts)<<5) | (OPTIONS_FASTER(opts)<<6)),
            0,0);
            break;

        case FILERACTION_SETTYPE:
            _swix(FilerAction_SendStartOperation,_INR(0,4),fa_task,action,
            ((OPTIONS_VERBOSE(opts)<<0) | (OPTIONS_CONFIRMCOPY(opts)<<1) | (OPTIONS_FORCE(opts)<<2) | (OPTIONS_NEWER(opts)<<3) | (OPTIONS_FASTER(opts)<<6)),
            param,4);
            break;

        case FILERACTION_COUNT:
        case FILERACTION_STAMP:
            _swix(FilerAction_SendStartOperation,_INR(0,4),fa_task,action,
            ((OPTIONS_VERBOSE(opts)<<0) | (OPTIONS_CONFIRMCOPY(opts)<<1) | (OPTIONS_FORCE(opts)<<2) | (OPTIONS_NEWER(opts)<<3) | (OPTIONS_FASTER(opts)<<6)),
            0,0);
            break;

        default:
          break;
      }
    }
    if (current_dir) {
      flex_free((flex_ptr)&current_dir);
      current_dir=NULL;
    }
  } while (done);
  
  if (cmdwin) {
    wimp_command_window(0);
  }

  if (flags & FILERACTION_RUN_DO_POSTDRAG) {
    for (i=0;i<pinlist->count;i++) {
      if (!pin_not_marked(&pinlist->info[i])) drag_post_action(origin,i);
    }
    pin_remove_empties(state);
  }

  for (i=0;i<pinlist->count;i++) pin_clear_marked(&pinlist->info[i]);
}
