//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2006 by PDSoft (Attila Padar)                *
//*                    http://mpxplay.cjb.net                              *
//*                  email: mpxplay@freemail.hu                            *
//**************************************************************************
//*  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.                  *
//*  Please contact with the author (with me) if you want to use           *
//*  or modify this source.                                                *
//**************************************************************************
//function:file handling on disk (copy,move,delete)

#include "mpxinbuf.h"
#include "newfunc\newfunc.h"
#include "playlist.h"
#include "display\display.h"
#include "control\cntfuncs.h"

#define DISKFILE_FILECOPY_BLOCKSIZE 65536
#define DISKFILE_FILECOPY_BARLEN 40

#define DISKFILE_CPYCTRL_COPY          1
#define DISKFILE_CPYCTRL_MOVE          2
#define DISKFILE_CPYCTRL_DEL           4 // some functions are common
#define DISKFILE_CPYCTRL_MULTIFILE     8 // more files are selected
#define DISKFILE_CPYCTRL_EMPTYDIRCOPY 16 // copy empty dirs too
#define DISKFILE_CPYCTRL_NOEXTCHK     32 // don't check file extension (copy all files)

#define DISKFILE_CPYCTRL_OVERWRITE    64
#define DISKFILE_CPYCTRL_SKIPFILE    128
#define DISKFILE_CPYCTRL_DELFILE     256
#define DISKFILE_CPYCTRL_DELSUBDIR   512 // in file-delete function only
#define DISKFILE_CPYCTRL_ALLFILE    1024 // for overwrite/skipfile
#define DISKFILE_CPYCTRL_ALLSUBDIR  2048 // for delsubdir

#define DISKFILE_CPYCTRL_IGNALLERR   4096 // ignore all errors
#define DISKFILE_CPYCTRL_SUBFINDNEXT 8192 // call subdirscan_findnext

#define DISKFILE_COUNTCTRL_NEXTFILE   1
#define DISKFILE_COUNTCTRL_NEWSUBDIR  2 // for !EMPTYDIRCOPY
#define DISKFILE_COUNTCTRL_COMPLETE   4

typedef struct filecopy_t{
 struct mainvars *mvp;
 struct playlist_side_info *psi;
 struct playlist_entry_info *pei_selected; // current subdir or file
 struct playlist_entry_info *pei_curr;     // current file (it's not in subdir)
 struct playlist_entry_info *pei_last;
 unsigned int cpy_ctrl;
 unsigned int directories_selected;
 unsigned int entries_selected;
 unsigned int filenum_selcount;
 unsigned int filenum_curr;
 mpxp_int64_t filebytes_copied;

 unsigned int count_ctrl;
 struct playlist_entry_info *count_pei;
 void *count_tw;
 int count_itemnum;
 unsigned int counted_filenum;
 unsigned int counted_directories;
 mpxp_int64_t counted_filesizes;

 int retcode,last_error;
 long filelen;
 long filepos;
 char *buffer;
 void *tw;
 struct frame frp_src;
 struct frame frp_dest;
 char infilename[MAX_PATHNAMELEN];
 char outfilename[MAX_PATHNAMELEN];
 char lastfilename[MAX_PATHNAMELEN]; // last outfilename
 char outpath_root[MAX_PATHNAMELEN]; // starting outpath
 char outpath_curr[MAX_PATHNAMELEN]; // current outpath (subdir)
 char headtext[32];
 char buttontext[32];
 char filemask[32];
 struct pds_subdirscan_t dsi;
}filecopy_t;

static void diskfile_filecopymove_makefilenames(struct filecopy_t *fc);
static void diskfile_filecopy_closefiles(struct filecopy_t *fc);
static void diskfile_filecopy_do_copy(struct filecopy_t *fc);
static void diskfile_filecopymove_postprocess(struct filecopy_t *fc);
static void diskfile_filecopymove_select(struct filecopy_t *fc);
static void diskfile_filecopymove_loop(struct filecopy_t *fc);
static void playlist_diskfile_delete_do(struct filecopy_t *fc);

extern unsigned int refdisp,displaymode,preloadinfo;

//static unsigned int diskfile_keycode;
static char filemask_default_copy[32]="*.*"; // all supported files
static char filemask_default_del[32]="*.?*"; // all files (not only the supported)

/*static void diskfile_keyboard_topfunc(unsigned int extkey,struct mainvars *mvp)
{
 diskfile_keycode=extkey;
}

static void diskfile_keyboardtopfunc_init(void)
{
 mpxplay_control_keyboard_set_topfunc(&diskfile_keyboard_topfunc);
 diskfile_keycode=0;
}

static void diskfile_keyboardtopfunc_close(void)
{
 mpxplay_control_keyboard_set_topfunc(NULL);
 diskfile_keycode=0;
}

static unsigned int diskfile_getch(void)
{
 unsigned int keycode=diskfile_keycode;
 diskfile_keycode=0;
 return keycode;
}*/

static struct filecopy_t *diskfile_filecopy_alloc(struct mainvars *mvp)
{
 struct filecopy_t *fc;

 fc=calloc(1,sizeof(struct filecopy_t));
 if(!fc)
  return fc;

 fc->mvp=mvp;
 fc->psi=mvp->psie;

 return fc;
}

static void diskfile_filecopy_dealloc(struct filecopy_t *fc)
{
 void *tw;

 //if((fc->retcode==MPXPLAY_ERROR_FILEHAND_OK) || (fc->retcode==MPXPLAY_ERROR_FILEHAND_USERABORT)) // else shows timedmessage with key-check
 // diskfile_keyboardtopfunc_close();

 tw=display_textwin_openwindow_message(NULL,NULL,"Flushing disk caches ...");
 pds_drives_flush();
 display_textwin_closewindow_message(tw);

 if(fc){
  if(fc->buffer){
   free(fc->buffer);
   fc->buffer=NULL;
  }
  free(fc);
 }
 refdisp|=RDT_RESET_EDIT;
}

static void diskfile_count_files_updatemsg(struct filecopy_t *fc)
{
 char sout[100];
 if(fc->cpy_ctrl&DISKFILE_CPYCTRL_DEL)
  sprintf(sout,"(total %d files and %d subdirs)",fc->counted_filenum,fc->counted_directories);
 else
  sprintf(sout," %s %d files with %d subdirs to",((fc->cpy_ctrl&DISKFILE_CPYCTRL_MOVE)? "Move":"Copy"),fc->counted_filenum,fc->counted_directories);
 display_textwin_update_msg(fc->count_tw,fc->count_itemnum,sout);
}

static void diskfile_count_files_in_subdirs(struct filecopy_t *fc)
{
 struct playlist_entry_info *pei=fc->count_pei;
 struct pds_subdirscan_t *dsi;
 char searchpath[MAX_PATHNAMELEN];

 if((pei>fc->psi->lastentry) || (pei>fc->pei_last))
  goto err_out_finish;

 if((fc->cpy_ctrl&DISKFILE_CPYCTRL_MULTIFILE) && !(pei->infobits&PEIF_SELECTED))
  goto err_out_skip;

 if((pei->entrytype==DFT_DRIVE) || (pei->entrytype==DFT_UPDIR) || (pei->entrytype==DFT_UPLIST))
  goto err_out_skip;

 if(pei->entrytype==DFT_SUBDIR){
  dsi=&fc->dsi;
  if(!(fc->count_ctrl&DISKFILE_COUNTCTRL_NEXTFILE)){
   unsigned int len=pds_strcpy(searchpath,pei->filename);
   len+=pds_strcpy(&searchpath[len],PDS_DIRECTORY_SEPARATOR_STR);
   len+=pds_strcpy(&searchpath[len],"*.*");
   len+=pds_strcpy(&searchpath[len],PDS_DIRECTORY_SEPARATOR_STR);
   pds_strcpy(&searchpath[len],fc->filemask);
   if(!pds_subdirscan_open(searchpath,_A_NORMAL,dsi))
    funcbit_enable(fc->count_ctrl,DISKFILE_COUNTCTRL_NEXTFILE);

   if(fc->cpy_ctrl&DISKFILE_CPYCTRL_EMPTYDIRCOPY){
    fc->counted_directories++;
    diskfile_count_files_updatemsg(fc);
   }
  }
  if(fc->count_ctrl&DISKFILE_COUNTCTRL_NEXTFILE){
   int fferror=pds_subdirscan_findnextfile(dsi);
   //fprintf(stdout,"in:%d err:%d %s \n",fc->count_itemnum,fferror,dsi->fullname);
   if(fferror<0){
    funcbit_disable(fc->count_ctrl,DISKFILE_COUNTCTRL_NEXTFILE);
    pds_subdirscan_close(dsi);
   }else{
    if(dsi->flags&SUBDIRSCAN_FLAG_SUBDIR){
     if(fc->cpy_ctrl&DISKFILE_CPYCTRL_EMPTYDIRCOPY)
      fc->counted_directories++;
     else
      funcbit_enable(fc->count_ctrl,DISKFILE_COUNTCTRL_NEWSUBDIR);
    }
    if((fferror==0) && !(dsi->ff->attrib&(_A_SUBDIR|_A_VOLID))){
     if((fc->cpy_ctrl&DISKFILE_CPYCTRL_NOEXTCHK) || mpxplay_infile_check_extension(dsi->ff->name) || playlist_loadlist_check_extension(dsi->ff->name)){
      if(fc->count_ctrl&DISKFILE_COUNTCTRL_NEWSUBDIR){
       fc->counted_directories++;
       funcbit_disable(fc->count_ctrl,DISKFILE_COUNTCTRL_NEWSUBDIR);
      }
      fc->counted_filenum++;
      fc->counted_filesizes+=dsi->ff->size;
      diskfile_count_files_updatemsg(fc);
     }
    }
    return;
   }
  }
 }

err_out_skip:
 if(!(fc->cpy_ctrl&DISKFILE_CPYCTRL_MULTIFILE)) // no more file
  goto err_out_finish;
 pei++;
 fc->count_pei=pei;
 return;

err_out_finish:
 diskfile_count_files_updatemsg(fc);
 mpxplay_timer_deletefunc(diskfile_count_files_in_subdirs);
 funcbit_enable(fc->count_ctrl,DISKFILE_COUNTCTRL_COMPLETE);
}

static void diskfile_count_files_start(struct filecopy_t *fc)
{
 struct playlist_entry_info *pei;
 unsigned int start_count=0,filenum,dirnum;

 if(fc->psi->selected_files){
  pei=fc->psi->firstentry;
  filenum=dirnum=0;
  do{
   if(pei->infobits&PEIF_SELECTED){
    if(pei->entrytype==DFT_SUBDIR)
     dirnum++;
    else{
     filenum++;
     if(fc->cpy_ctrl&(DISKFILE_CPYCTRL_COPY|DISKFILE_CPYCTRL_MOVE)){
      if(pei->filesize)
       fc->counted_filesizes+=pei->filesize;
      else
       fc->counted_filesizes+=pds_getfilesize(pei->filename);
     }
    }
   }
   pei++;
  }while(pei<=fc->psi->lastentry);
  fc->directories_selected=dirnum;
  fc->entries_selected=filenum+dirnum;
  fc->counted_filenum=filenum;
  if(dirnum){
   fc->count_pei=fc->psi->firstentry;
   start_count=1;
  }else{
   funcbit_enable(fc->count_ctrl,DISKFILE_COUNTCTRL_COMPLETE);
  }
 }else{
  pei=fc->psi->editorhighline;
  fc->entries_selected=1;
  if(pei->entrytype==DFT_SUBDIR){
   fc->count_pei=pei;
   fc->directories_selected=1;
   start_count=1;
  }else{
   fc->counted_filenum=1;
   if(fc->cpy_ctrl&(DISKFILE_CPYCTRL_COPY|DISKFILE_CPYCTRL_MOVE)){
    if(pei->filesize)
     fc->counted_filesizes+=pei->filesize;
    else
     fc->counted_filesizes+=pds_getfilesize(pei->filename);
   }
   funcbit_enable(fc->count_ctrl,DISKFILE_COUNTCTRL_COMPLETE);
  }
 }

 if(start_count)
  mpxplay_timer_addfunc(diskfile_count_files_in_subdirs,fc,MPXPLAY_TIMERTYPE_REPEAT|MPXPLAY_TIMERFLAG_LOWPRIOR,0);
}

static void diskfile_count_files_stop(struct filecopy_t *fc)
{
 mpxplay_timer_deletefunc(diskfile_count_files_in_subdirs);
}

//-------------------------------------------------------------------------
static void diskfile_filecopymove_update_sides(struct filecopy_t *fc,char *infilename,char *outfilename,struct playlist_entry_info *pei_src)
{
 struct playlist_side_info *psi=fc->mvp->psi0;
 unsigned int side,modified_side,directory;
 struct playlist_entry_info *pei,pei_tmp;
 char inpath[MAX_PATHNAMELEN]="";

 if(infilename && (fc->cpy_ctrl&(DISKFILE_CPYCTRL_MOVE|DISKFILE_CPYCTRL_DEL)) && (fc->retcode==MPXPLAY_ERROR_FILEHAND_OK))
  pds_getpath_from_fullname(inpath,infilename);

 if(outfilename){
  pds_memcpy(&pei_tmp,pei_src,sizeof(struct playlist_entry_info));
  pei_tmp.filename=outfilename;
 }

 if(outfilename && ((fc->cpy_ctrl&DISKFILE_CPYCTRL_COPY) || (pds_stricmp(inpath,fc->outpath_curr)!=0))){
  for(side=0;side<PLAYLIST_MAX_SIDES;side++,psi++){
   if(!(psi->editsidetype&PLT_ENABLED) || !(psi->editsidetype&PLT_DIRECTORY) || psi->sublistlevel)
    continue;
   if(pds_stricmp(fc->outpath_curr,psi->currdir)!=0)
    continue;
   modified_side=0;
   pei=playlist_search_filename(psi,outfilename,-1,NULL);
   if((fc->retcode==MPXPLAY_ERROR_FILEHAND_OK) || (fc->retcode==MPXPLAY_ERROR_FILEHAND_DELETE)){
    //fprintf(stdout,"add rc:%d side:%d op:\"%s\" cd:\"%s\" \n",fc->retcode,side,fc->outpath_curr,psi->currdir);
    if(pei){ // update an existent playlist entry (with the datas from infile)
     playlist_editlist_delfile_one(psi,pei,EDITLIST_MODE_HEAD|EDITLIST_MODE_ID3);
     if(playlist_editlist_addfile_one(fc->psi,psi,&pei_tmp,pei,EDITLIST_MODE_HEAD|EDITLIST_MODE_ID3))
      modified_side=1;
    }else{   // create a new playlist entry (copy datas from infile)
     if(playlist_editlist_addfile_one(fc->psi,psi,&pei_tmp,NULL,(EDITLIST_MODE_ALL&(~EDITLIST_MODE_INDEX)))){
      pei=playlist_editlist_addfileone_postproc(psi,psi->lastentry);
      modified_side=1;
     }
    }
   }else{
    if(pei){ // delete entry (output file) at error
    //fprintf(stdout,"del rc:%d side:%d pei:%d if:\"%s\" of:\"%s\" \n",fc->retcode,side,(pei-psi->firstentry+1),fc->infilename,outfilename);
     playlist_editlist_delfile_one(psi,pei,EDITLIST_MODE_ALL);
     modified_side=1;
    }
   }

   if(modified_side){
    //if((psi!=fc->mvp->psie) && ((pei->entrytype==DFT_SUBDIR) || (preloadinfo!=PLI_DISPLOAD)))
    // playlist_editorhighline_set(psi,pei);
    if(psi==fc->mvp->psip)
     refdisp|=RDT_EDITOR|RDT_BROWSER;
    else
     refdisp|=RDT_EDITOR;
   }
  }
 }

 if(infilename && (fc->cpy_ctrl&(DISKFILE_CPYCTRL_MOVE|DISKFILE_CPYCTRL_DEL)) && (fc->retcode==MPXPLAY_ERROR_FILEHAND_OK)){
  psi=fc->mvp->psi0;
  pds_getpath_from_fullname(inpath,infilename);
  for(side=0;side<PLAYLIST_MAX_SIDES;side++,psi++){
   if(!(psi->editsidetype&PLT_ENABLED))
    continue;
   directory=((psi->editsidetype&PLT_DIRECTORY) && !psi->sublistlevel);
   if(!directory || pds_stricmp(inpath,psi->currdir)==0){
    pei=playlist_search_filename(psi,infilename,-1,NULL);
    //fprintf(stdout,"mdel rc:%d side:%d pei:%d if:\"%s\" of:\"%s\" \n",fc->retcode,side,(pei-psi->firstentry+1),infilename,outfilename);
    if(pei){
     if(pds_stricmp(inpath,fc->outpath_curr)==0){ // rename
      if(outfilename){ // ???
       playlist_editlist_delfile_one(psi,pei,EDITLIST_MODE_FILENAME);
       playlist_editlist_addfile_one(fc->psi,psi,&pei_tmp,pei,EDITLIST_MODE_FILENAME);
       refdisp|=RDT_EDITOR;
      }
     }else{                                       // move
      if(directory || (psi==fc->psi)){
       playlist_editlist_delfile_one(psi,pei,EDITLIST_MODE_ALL);
       if(psi==fc->mvp->psip)
        refdisp|=RDT_EDITOR|RDT_BROWSER;
       else
        refdisp|=RDT_EDITOR;
      }
     }
    }
   }
  }
 }
}

static void diskfile_filecopy_makebar(char *barstr,unsigned int percent)
{
 unsigned int i,b=percent*DISKFILE_FILECOPY_BARLEN/100;

 for(i=0;i<DISKFILE_FILECOPY_BARLEN;i++){
  if(i<b)
   barstr[i]='Û'; // 219
  else
   barstr[i]='°'; // 176
 }
 barstr[i]=0;
}

static display_textwin_button_t errorhand_buttons[]={
 {" Ignore "    ,0x1769}, // 'i'
 {""            ,0x1749}, // 'I'
 {" Ignore All ",0x1e61}, // 'a'
 {""            ,0x1e41}, // 'A'
 {" Cancel "    ,0x2e63}, // 'c'
 {""            ,0x2e43}, // 'C'
 {""            ,KEY_ESC},// ESC
 {NULL,0}
};

static display_textwin_button_t errorhand_button_ok[]={
 {"[ Ok ]"    ,KEY_ESC}, //
 {NULL,0}
};

static void diskfile_filecopy_errorhandler(struct filecopy_t *fc,unsigned int extkey)
{
 switch(extkey){
  case 0x1769:
  case 0x1749:mpxplay_timer_addfunc(diskfile_filecopymove_loop,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
              break;
  case 0x1e61:
  case 0x1e41:funcbit_enable(fc->cpy_ctrl,DISKFILE_CPYCTRL_IGNALLERR);
              mpxplay_timer_addfunc(diskfile_filecopymove_loop,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
              break;
  default:mpxplay_timer_addfunc(diskfile_filecopy_dealloc,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
 }
}

static void diskfile_filecopy_errormessage(struct filecopy_t *fc,char *msg,char *filename)
{
 const unsigned int flags=(TEXTWIN_FLAG_ERRORMSG|TEXTWIN_FLAG_MSGCENTERALIGN);
 void *tw=display_textwin_allocwindow_items(flags," Error ",diskfile_filecopy_errorhandler,fc);
 display_textwin_additem_msg_alloc(tw,flags,0,-1,msg);
 display_textwin_additem_msg_alloc(tw,flags,0,-1,filename);
 if((fc->cpy_ctrl&DISKFILE_CPYCTRL_MULTIFILE) || (fc->cpy_ctrl&DISKFILE_CPYCTRL_SUBFINDNEXT)){
  display_textwin_additem_msg_static(tw,flags,0,-1,"");
  display_textwin_additem_buttons(tw,flags,0,-1,errorhand_buttons,NULL);
 }else
  display_textwin_additem_buttons(tw,flags,0,-1,errorhand_button_ok,NULL);
 display_textwin_openwindow_items(tw,0,0,0);
}

static void playlist_diskfile_show_errmsg(struct filecopy_t *fc)
{
 switch(fc->last_error){
  case MPXPLAY_ERROR_FILEHAND_OK:break;
  case MPXPLAY_ERROR_FILEHAND_USERABORT:/*diskfile_filecopy_errormessage(fc,"Copy aborted by user!");*/break;
  case MPXPLAY_ERROR_FILEHAND_MEMORY   :diskfile_filecopy_errormessage(fc,"Memory allocation error at",fc->infilename);break;
  case MPXPLAY_ERROR_FILEHAND_CANTOPEN :diskfile_filecopy_errormessage(fc,"Cannot open for reading at",fc->infilename);break;
  case MPXPLAY_ERROR_FILEHAND_CANTCREATE:diskfile_filecopy_errormessage(fc,"Cannot create",fc->lastfilename);break;
  case MPXPLAY_ERROR_FILEHAND_CANTREAD :diskfile_filecopy_errormessage(fc,"Cannot read file (Where is the disk?)",fc->infilename);break;
  case MPXPLAY_ERROR_FILEHAND_CANTWRITE:diskfile_filecopy_errormessage(fc,"Cannot write file (Disk full?)",fc->lastfilename);break;
  case MPXPLAY_ERROR_FILEHAND_DELETE   :diskfile_filecopy_errormessage(fc,"Couldn't delete file",fc->infilename);break;
  case MPXPLAY_ERROR_FILEHAND_RENAME   :diskfile_filecopy_errormessage(fc,"Couldn't rename/move file",fc->infilename);break;
  case MPXPLAY_ERROR_FILEHAND_REMOVEDIR:diskfile_filecopy_errormessage(fc,"Couldn't delete directory",fc->lastfilename);break;
  case MPXPLAY_ERROR_FILEHAND_COPYDIR  :diskfile_filecopy_errormessage(fc,"Cannot copy drive/updir/uplist",fc->infilename);break;
  case MPXPLAY_ERROR_FILEHAND_SAMEDIR  :diskfile_filecopy_errormessage(fc,"Cannot copy/move a directory to itself!",fc->lastfilename);break;
  case MPXPLAY_ERROR_FILEHAND_SKIPFILE :break;
  default:diskfile_filecopy_errormessage(fc,"Unknown error in copy at",fc->infilename);break;
 }
}

static display_textwin_button_t existfile_buttons[]={
 {" Overwrite ",0x186f}, // 'o'
 {""           ,0x184f}, // 'O'
 {" All "      ,0x1e61}, // 'a'
 {""           ,0x1e41}, // 'A'
 {" Skip "     ,0x1f73}, // 's'
 {""           ,0x1f53}, // 'S'
 {" Skipall "  ,0x256b}, // 'k'
 {""           ,0x254b}, // 'K'
 {" Cancel "   ,0x2e63}, // 'c'
 {""           ,0x2e43}, // 'C'
 {""           ,KEY_ESC},// ESC
 {NULL,0}
};

static display_textwin_button_t samefile_buttons[]={
 {" Rename/Newdir ",0x1372}, // 'r'
 {""           ,0x1352}, // 'R'
 {" Skip file" ,0x1f73}, // 's'
 {""           ,0x1f53}, // 'S'
 {" Cancel "   ,0x2e63}, // 'c'
 {""           ,0x2e43}, // 'C'
 {""           ,KEY_ESC},// ESC
 {NULL,0}
};

static void diskfile_filecopymove_existfile_keyhand(struct filecopy_t *fc,unsigned int extkey)
{
 switch(extkey){
  case 0x1372:
  case 0x1352:
  case 0x186f:
  case 0x184f:if(pds_stricmp(fc->outfilename,fc->lastfilename)==0) // if the filename is not modified manually
               funcbit_enable(fc->cpy_ctrl,DISKFILE_CPYCTRL_OVERWRITE); // overwrite it
              mpxplay_timer_addfunc(diskfile_filecopymove_makefilenames,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
              return;
  case 0x1e61:
  case 0x1e41:if(pds_stricmp(fc->outfilename,fc->lastfilename)==0)
               funcbit_enable(fc->cpy_ctrl,(DISKFILE_CPYCTRL_OVERWRITE|DISKFILE_CPYCTRL_ALLFILE));
              mpxplay_timer_addfunc(diskfile_filecopymove_makefilenames,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
              return;
  case 0x1f73:
  case 0x1f53:funcbit_enable(fc->cpy_ctrl,DISKFILE_CPYCTRL_SKIPFILE);
              fc->retcode=MPXPLAY_ERROR_FILEHAND_SKIPFILE;
              break;
  case 0x256b:
  case 0x254b:funcbit_enable(fc->cpy_ctrl,(DISKFILE_CPYCTRL_SKIPFILE|DISKFILE_CPYCTRL_ALLFILE));
              fc->retcode=MPXPLAY_ERROR_FILEHAND_SKIPFILE;
              break;
  case 0x2e63:
  case 0x2e43:
  case KEY_ESC:fc->retcode=MPXPLAY_ERROR_FILEHAND_USERABORT;
               diskfile_filecopy_dealloc(fc);
               return;
 }
 diskfile_filecopymove_select(fc);
}

static display_textwin_button_t delsubdir_buttons[]={
 {" Delete "   ,0x2064}, // 'd'
 {""           ,0x2044}, // 'D'
 {" All "      ,0x1e61}, // 'a'
 {""           ,0x1e41}, // 'A'
 {" Skip "     ,0x1f73}, // 's'
 {""           ,0x1f53}, // 'S'
 //{" Skipall "  ,0x256b}, // 'k'
 //{""           ,0x254b}, // 'K'
 {" Cancel "   ,0x2e63}, // 'c'
 {""           ,0x2e43}, // 'C'
 {""           ,KEY_ESC},// ESC
 {NULL,0}
};

static void diskfile_filemovedel_delsubdir_keyhand(struct filecopy_t *fc,unsigned int extkey)
{
 switch(extkey){
  case 0x2064:
  case 0x2044:funcbit_enable(fc->cpy_ctrl,DISKFILE_CPYCTRL_DELSUBDIR);
              break;
  case 0x1e61:
  case 0x1e41:funcbit_enable(fc->cpy_ctrl,(DISKFILE_CPYCTRL_DELSUBDIR|DISKFILE_CPYCTRL_ALLSUBDIR));
              break;
  case 0x1f73:
  case 0x1f53:fc->pei_selected++;
              break;
  //case 0x256b:
  //case 0x254b:fc->pei_selected++;
  //
  //            break;
  case 0x2e63:
  case 0x2e43:
  case KEY_ESC:fc->retcode=MPXPLAY_ERROR_FILEHAND_USERABORT;
               diskfile_filecopy_dealloc(fc);
               return;
 }
 mpxplay_timer_addfunc(diskfile_filecopymove_loop,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
}

//-------------------------------------------------------------------------
static void diskfile_filecopymove_create_outpath(struct filecopy_t *fc,struct playlist_entry_info *pei)
{
 struct pds_subdirscan_t *dsi;
 unsigned int i,len;
 char path[MAX_PATHNAMELEN];

 if(fc->cpy_ctrl&DISKFILE_CPYCTRL_DEL)
  return;

 len=pds_strcpy(path,fc->outpath_root);
 if(path[len-1]!=PDS_DIRECTORY_SEPARATOR_CHAR)
  len+=pds_strcpy(&path[len],PDS_DIRECTORY_SEPARATOR_STR);
 len+=pds_strcpy(&path[len],pds_getfilename_from_fullname(pei->filename));

 dsi=&fc->dsi;
 i=pds_strlen(dsi->startdir);
 if((path[len-1]!=PDS_DIRECTORY_SEPARATOR_CHAR) && (dsi->currdir[i]!=PDS_DIRECTORY_SEPARATOR_CHAR))
  len+=pds_strcpy(&path[len],PDS_DIRECTORY_SEPARATOR_STR);
 len+=pds_strcpy(&path[len],&dsi->currdir[i]);
 pds_strcpy(fc->outpath_curr,path);
}

static int diskfile_filecopymove_create_outdir(struct filecopy_t *fc,struct playlist_entry_info *pei,unsigned int tree)
{
 if(tree){ // create all subdirectories step by step
  struct pds_subdirscan_t *dsi=&fc->dsi;
  unsigned int i,len;
  char *path=&fc->outpath_curr[0];
  len=pds_strcpy(path,fc->outpath_root);
  if(path[len-1]!=PDS_DIRECTORY_SEPARATOR_CHAR)
   len+=pds_strcpy(&path[len],PDS_DIRECTORY_SEPARATOR_STR);
  len+=pds_strcpy(&path[len],pds_getfilename_from_fullname(pei->filename));
  if(!pds_chkdir(path))
   if(pds_mkdir(path)<0)
    goto err_out_co;
  for(i=0;i<dsi->subdir_level;i++){
   len+=pds_strcpy(&path[len],PDS_DIRECTORY_SEPARATOR_STR);
   len+=pds_strcpy(&path[len],dsi->subdir_names[i]);
   if(!pds_chkdir(path))
    if(pds_mkdir(path)<0)
     goto err_out_co;
  }
  return 0;
 }

 diskfile_filecopymove_create_outpath(fc,pei);

 if(pds_mkdir(fc->outpath_curr)==0)
  return 0;

err_out_co:
 fc->retcode=MPXPLAY_ERROR_FILEHAND_CANTCREATE;
 pds_strcpy(fc->lastfilename,fc->outpath_curr);
 return -1;
}

static int diskfile_filecopymove_remove_indir(struct filecopy_t *fc,char *path)
{
 if(pds_rmdir(path)==0)
  return 0;

 fc->retcode=MPXPLAY_ERROR_FILEHAND_REMOVEDIR;
 pds_strcpy(fc->lastfilename,path);
 return -1;
}

static int diskfile_filecopymove_getnextfile(struct filecopy_t *fc)
{
 struct playlist_entry_info *pei;
 struct pds_subdirscan_t *dsi;
 int fferror;
 char path[MAX_PATHNAMELEN];

 if(fc->pei_curr)
  pei=fc->pei_selected=fc->pei_curr;
 else
  pei=fc->pei_selected;

 fc->pei_curr=NULL;

 if((pei>fc->psi->lastentry) || (pei>fc->pei_last))
  goto err_out_finish;
 if(fc->filenum_selcount>=fc->entries_selected)
  goto err_out_finish;

 if((fc->cpy_ctrl&DISKFILE_CPYCTRL_MULTIFILE) && !(pei->infobits&PEIF_SELECTED))
  goto err_out_skip;

 if((pei->entrytype==DFT_DRIVE) || (pei->entrytype==DFT_UPDIR) || (pei->entrytype==DFT_UPLIST)){
  fc->retcode=MPXPLAY_ERROR_FILEHAND_COPYDIR;
  goto err_out_skip;
 }

 if(pei->entrytype==DFT_SUBDIR){
  dsi=&fc->dsi;
  if(!(fc->cpy_ctrl&DISKFILE_CPYCTRL_SUBFINDNEXT)){
   unsigned int len;

   if((fc->cpy_ctrl&DISKFILE_CPYCTRL_DEL) && !(fc->cpy_ctrl&DISKFILE_CPYCTRL_DELSUBDIR)){
    void *tw=display_textwin_allocwindow_items(TEXTWIN_FLAG_ERRORMSG|TEXTWIN_FLAG_MSGCENTERALIGN,"Delete directory",diskfile_filemovedel_delsubdir_keyhand,fc);
    display_textwin_additem_msg_static(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,0,"The following directory will be DELETED");
    display_textwin_additem_msg_alloc(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,-1,pei->filename);
    display_textwin_additem_buttons(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,-1,delsubdir_buttons,NULL);
    display_textwin_openwindow_items(tw,0,0,0);
    return 2;
   }
   if(!(fc->cpy_ctrl&DISKFILE_CPYCTRL_ALLSUBDIR))
    funcbit_disable(fc->cpy_ctrl,DISKFILE_CPYCTRL_DELSUBDIR);

   pds_strcpy(fc->outpath_curr,fc->outpath_root);
   // create subdirectory in output dir
   if(fc->cpy_ctrl&(DISKFILE_CPYCTRL_COPY|DISKFILE_CPYCTRL_MOVE)){
    len=pds_strcpy(path,fc->outpath_root);
    if(path[len-1]!=PDS_DIRECTORY_SEPARATOR_CHAR)
     len+=pds_strcpy(&path[len],PDS_DIRECTORY_SEPARATOR_STR);
    pds_strcpy(&path[len],pds_getfilename_from_fullname(pei->filename));

    if(pds_strlicmp(path,pei->filename)==0){ // copy/move a directory to itself?
     fc->retcode=MPXPLAY_ERROR_FILEHAND_SAMEDIR;
     pds_strcpy(fc->lastfilename,path);
     goto err_out_skip;
    }

    if(fc->cpy_ctrl&DISKFILE_CPYCTRL_EMPTYDIRCOPY){
     if(!pds_chkdir(path)){ // !!!
      if(pds_mkdir(path)<0){
       fc->retcode=MPXPLAY_ERROR_FILEHAND_CANTCREATE;
       pds_strcpy(fc->lastfilename,path);
       goto err_out_skip;
      }
     }
     diskfile_filecopymove_update_sides(fc,NULL,path,pei);
    }
   }

   // start search in subdirs
   len=pds_strcpy(path,pei->filename);
   len+=pds_strcpy(&path[len],PDS_DIRECTORY_SEPARATOR_STR);
   len+=pds_strcpy(&path[len],"*.*");
   len+=pds_strcpy(&path[len],PDS_DIRECTORY_SEPARATOR_STR);
   pds_strcpy(&path[len],fc->filemask);
   if(pds_subdirscan_open(path,_A_NORMAL,dsi)==0){
    funcbit_enable(fc->cpy_ctrl,DISKFILE_CPYCTRL_SUBFINDNEXT);
    diskfile_filecopymove_create_outpath(fc,pei);
   }else{
    fc->filenum_selcount++;
    goto err_out_skip;
   }
  }

  fferror=pds_subdirscan_findnextfile(dsi);

  if(fferror<0){ // no more files in subdir
   if((fc->retcode==MPXPLAY_ERROR_FILEHAND_OK) && (fc->cpy_ctrl&(DISKFILE_CPYCTRL_MOVE|DISKFILE_CPYCTRL_DEL))){
    if(diskfile_filecopymove_remove_indir(fc,dsi->startdir)==0) // delete subdir at move/del
     diskfile_filecopymove_update_sides(fc,dsi->startdir,NULL,pei);
   }
   if((fc->retcode!=MPXPLAY_ERROR_FILEHAND_OK) || !(fc->cpy_ctrl&(DISKFILE_CPYCTRL_MOVE|DISKFILE_CPYCTRL_DEL))){
    if((pei->infobits&PEIF_SELECTED) && ((fc->retcode==MPXPLAY_ERROR_FILEHAND_OK) || (fc->cpy_ctrl&DISKFILE_CPYCTRL_MOVE))){ // !!! failed copy at move?
     funcbit_disable(pei->infobits,PEIF_SELECTED);
     if(fc->psi->selected_files)
      fc->psi->selected_files--;
     refdisp|=RDT_EDITOR;
    }
    fc->pei_selected++;
   }
   funcbit_disable(fc->cpy_ctrl,DISKFILE_CPYCTRL_SUBFINDNEXT);
   pds_subdirscan_close(dsi);
   fc->filenum_selcount++;
   goto err_out_getnext;
  }

  if(dsi->flags&SUBDIRSCAN_FLAG_UPDIR){
   if((fc->cpy_ctrl&(DISKFILE_CPYCTRL_MOVE|DISKFILE_CPYCTRL_DEL)) && (fc->retcode==MPXPLAY_ERROR_FILEHAND_OK))
    diskfile_filecopymove_remove_indir(fc,dsi->prevdir); // delete subdir at move/del
   diskfile_filecopymove_create_outpath(fc,pei);
  }
  if((dsi->flags&SUBDIRSCAN_FLAG_SUBDIR) && (fc->cpy_ctrl&(DISKFILE_CPYCTRL_COPY|DISKFILE_CPYCTRL_MOVE))){
   if(fc->cpy_ctrl&DISKFILE_CPYCTRL_EMPTYDIRCOPY){
    if(diskfile_filecopymove_create_outdir(fc,pei,0)<0)
     return 1;
   }else
    funcbit_enable(fc->count_ctrl,DISKFILE_COUNTCTRL_NEWSUBDIR);
  }

  if((fferror==0) && !(dsi->ff->attrib&(_A_SUBDIR|_A_VOLID))){
   if((fc->cpy_ctrl&DISKFILE_CPYCTRL_NOEXTCHK) || mpxplay_infile_check_extension(dsi->ff->name) || playlist_loadlist_check_extension(dsi->ff->name)){
    if(fc->count_ctrl&DISKFILE_COUNTCTRL_NEWSUBDIR){
     funcbit_disable(fc->count_ctrl,DISKFILE_COUNTCTRL_NEWSUBDIR);
     if(diskfile_filecopymove_create_outdir(fc,pei,1)<0)
      return 1;
    }
    pds_strcpy(fc->infilename,dsi->fullname);
    return 0;
   }
  }
  return 1;
 }

 // normal file (audio, playlist)
 if(!(fc->cpy_ctrl&DISKFILE_CPYCTRL_MULTIFILE) || pds_filename_cmp_mask(pei->filename,fc->filemask)){
  pds_strcpy(fc->infilename,pei->filename);
  pds_strcpy(fc->outpath_curr,fc->outpath_root);
  fc->pei_curr=pei;
  fc->filenum_selcount++;
  return 0;
 }

err_out_skip:
 fc->pei_selected++;
err_out_getnext:
 if(fc->cpy_ctrl&DISKFILE_CPYCTRL_MULTIFILE) // there are more files/dirs
  return 1;
err_out_finish:
 return -1; // no next, finish
}

static void diskfile_filecopymove_makefilenames(struct filecopy_t *fc)
{
 unsigned int pathlen,el_len;
 void *tw;

 fc->retcode=MPXPLAY_ERROR_FILEHAND_OK;

 if(fc->outfilename[0]){  // single file or overwrite
  char *p=pds_strcutspc(fc->outfilename);
  if(p>(&fc->outfilename[0]))
   pds_strcpy(fc->outfilename,p);

  pds_sfn_limit(fc->outfilename);  // !!! ???

  if(pds_chkdir(fc->outfilename)){ // is filename a path only?
   pathlen=pds_strcpy(fc->outpath_curr,fc->outfilename); // save the dir
   if(fc->outfilename[pathlen-1]!=PDS_DIRECTORY_SEPARATOR_CHAR)
    pds_strcpy(&fc->outfilename[pathlen++],PDS_DIRECTORY_SEPARATOR_STR);
   pds_strcpy(&fc->outfilename[pathlen],pds_getfilename_from_fullname(fc->infilename));
  }
 }else{ // multiply files
  pathlen=pds_strcpy(fc->outfilename,fc->outpath_curr);
  if(fc->outfilename[pathlen-1]!=PDS_DIRECTORY_SEPARATOR_CHAR)
   pds_strcpy(&fc->outfilename[pathlen++],PDS_DIRECTORY_SEPARATOR_STR);
  pds_strcpy(&fc->outfilename[pathlen],pds_getfilename_from_fullname(fc->infilename));
 }

 pds_fullpath(fc->outfilename,fc->outfilename);
 pds_getpath_from_fullname(fc->outpath_curr,fc->outfilename);

 if(pds_stricmp(fc->infilename,fc->outfilename)==0)
  goto err_out_samefile;

 if(fc->cpy_ctrl&DISKFILE_CPYCTRL_OVERWRITE)
  goto err_out_mkf;

 if(mpxinbuf_lowlevel_file_open_read(&fc->frp_dest,fc->outfilename)){
  mpxinbuf_lowlevel_file_close(&fc->frp_dest);
  if(!(fc->cpy_ctrl&DISKFILE_CPYCTRL_SKIPFILE))
   goto err_out_exists;
  fc->retcode=MPXPLAY_ERROR_FILEHAND_SKIPFILE;
 }

err_out_mkf:
 diskfile_filecopymove_select(fc);
 return;
err_out_exists:
 pds_strcpy(fc->lastfilename,fc->outfilename);
 el_len=pds_strlen(fc->outfilename)+1;
 if(el_len>50)
  el_len=50;
 tw=display_textwin_allocwindow_items(TEXTWIN_FLAG_ERRORMSG|TEXTWIN_FLAG_MSGCENTERALIGN,fc->headtext,diskfile_filecopymove_existfile_keyhand,fc);
 display_textwin_additem_msg_static(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,-1,"The following file exists");
 display_textwin_additem_editline(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,-1,el_len,fc->outfilename,MAX_PATHNAMELEN-2);
 display_textwin_additem_msg_static(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,-1,"Do you wish to write over the old file?");
 display_textwin_additem_buttons(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,-1,existfile_buttons,&existfile_buttons[0]);
 display_textwin_openwindow_items(tw,0,0,0);
 return;
err_out_samefile:
 tw=display_textwin_allocwindow_items(TEXTWIN_FLAG_ERRORMSG|TEXTWIN_FLAG_MSGCENTERALIGN,fc->headtext,diskfile_filecopymove_existfile_keyhand,fc);
 display_textwin_additem_msg_static(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,-1,"Cannot copy/move a file to itself!\nRename it or give a new target directory:");
 display_textwin_additem_editline(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,-1,50,fc->outfilename,MAX_PATHNAMELEN-2);
 display_textwin_additem_buttons(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,-1,samefile_buttons,NULL);
 display_textwin_openwindow_items(tw,0,0,0);
}

static void diskfile_filecopy_openfiles(struct filecopy_t *fc)
{
 fc->retcode=MPXPLAY_ERROR_FILEHAND_OK;
 fc->filepos=0;

 if(!fc->buffer)
  fc->buffer=pds_malloc(DISKFILE_FILECOPY_BLOCKSIZE);
 if(!fc->buffer){
  fc->retcode=MPXPLAY_ERROR_FILEHAND_MEMORY;
  goto err_out_open;
 }
 if(!mpxinbuf_lowlevel_file_open_read(&fc->frp_src,fc->infilename)){
  fc->retcode=MPXPLAY_ERROR_FILEHAND_CANTOPEN;
  goto err_out_open;
 }
 if(!mpxinbuf_lowlevel_file_open_create(&fc->frp_dest,fc->outfilename)){
  fc->retcode=MPXPLAY_ERROR_FILEHAND_CANTCREATE;
  goto err_out_open;
 }
 fc->filelen=mpxinbuf_lowlevel_file_length(&fc->frp_src);

 mpxplay_timer_addfunc(diskfile_filecopy_do_copy,fc,MPXPLAY_TIMERTYPE_REPEAT|MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);

 return;

err_out_open:
 mpxplay_timer_addfunc(diskfile_filecopy_closefiles,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
}

static void diskfile_filecopy_closefiles(struct filecopy_t *fc)
{
 unsigned int is_dest;
 mpxinbuf_lowlevel_file_close(&fc->frp_src);
 is_dest=mpxinbuf_lowlevel_file_isopen(&fc->frp_dest);
 mpxinbuf_lowlevel_file_close(&fc->frp_dest);
 if(is_dest && (fc->retcode!=MPXPLAY_ERROR_FILEHAND_OK))
  pds_unlink(fc->outfilename);

 if((fc->cpy_ctrl&DISKFILE_CPYCTRL_MOVE) && (fc->retcode==MPXPLAY_ERROR_FILEHAND_OK))
  fc->retcode=pds_unlink(fc->infilename);

 fc->filebytes_copied+=fc->filelen;

 mpxplay_timer_addfunc(diskfile_filecopymove_postprocess,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
}

static void diskfile_filecopy_keycheck(struct filecopy_t *fc,unsigned int extkey)
{
 if(extkey==KEY_ESC)
  fc->retcode=MPXPLAY_ERROR_FILEHAND_USERABORT;
}

static void diskfile_filecopy_do_copy(struct filecopy_t *fc)
{
 long inbytes,outbytes,percent_file,percent_group;
 char barstr_file[DISKFILE_FILECOPY_BARLEN+1],barstr_group[DISKFILE_FILECOPY_BARLEN+1];
 char msg[2*MAX_PATHNAMELEN+48+DISKFILE_FILECOPY_BARLEN*2];

 if(fc->filelen<1)
  percent_file=0;
 else if(fc->filelen<10000)
  percent_file=100*fc->filepos/fc->filelen;
 else
  percent_file=fc->filepos/(fc->filelen/100);
 diskfile_filecopy_makebar(barstr_file,percent_file);
 if(fc->counted_filenum>1){
  //percent_group=100*(fc->filenum_curr-1)/fc->counted_filenum;
  //percent_group+=percent_file/fc->counted_filenum;
  percent_group=(long)(100.0*((float)fc->filebytes_copied+(float)fc->filepos)/(float)fc->counted_filesizes);
  diskfile_filecopy_makebar(barstr_group,percent_group);
  sprintf(msg,"Copying the file\n%s\nto\n%s\n%s\nTotal %d/%d\n%s",fc->infilename,fc->outfilename,barstr_file,fc->filenum_curr,fc->counted_filenum,barstr_group);
 }else
  sprintf(msg,"Copying the file\n%s\nto\n%s\n%s",fc->infilename,fc->outfilename,barstr_file);
 fc->tw=display_textwin_openwindow_buttons(fc->tw,TEXTWIN_FLAG_MSGCENTERALIGN," File copy ",msg,diskfile_filecopy_keycheck,fc,NULL);
 if(fc->retcode==MPXPLAY_ERROR_FILEHAND_USERABORT)
  goto end_copy;
 inbytes=mpxinbuf_lowlevel_file_read(&fc->frp_src,fc->buffer,DISKFILE_FILECOPY_BLOCKSIZE);
 if(inbytes<=0){
  if(fc->filepos<fc->filelen)
   fc->retcode=MPXPLAY_ERROR_FILEHAND_CANTREAD;
  goto end_copy;
 }
 outbytes=mpxinbuf_lowlevel_file_write(&fc->frp_dest,fc->buffer,inbytes);
 if(outbytes<inbytes){
  fc->retcode=MPXPLAY_ERROR_FILEHAND_CANTWRITE;
  goto end_copy;
 }
 fc->filepos+=outbytes;

 return; // continue copy

end_copy:
 mpxplay_timer_deletefunc(diskfile_filecopy_do_copy);
 mpxplay_timer_addfunc(diskfile_filecopy_closefiles,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
}

static unsigned int diskfile_filemove_check(struct filecopy_t *fc)
{
 int drive_d,drive_s;

 drive_s=pds_getdrivenum_from_path(fc->infilename);
 drive_d=pds_getdrivenum_from_path(fc->outfilename);

 if(drive_d==drive_s)
  return 1;

 return 0;
}

static void diskfile_filemove_do(struct filecopy_t *fc)
{
 char msg[2*MAX_PATHNAMELEN+32];
 sprintf(msg,"Moveing the file\n%s\nto\n%s",fc->infilename,fc->outfilename);
 fc->tw=display_textwin_openwindow_buttons(fc->tw,TEXTWIN_FLAG_MSGCENTERALIGN," Move file ",msg,diskfile_filecopy_keycheck,fc,NULL);
 if(fc->retcode==MPXPLAY_ERROR_FILEHAND_USERABORT)
  return;
 pds_unlink(fc->outfilename); // !!!
 fc->retcode=pds_rename(fc->infilename,fc->outfilename);
}

static void diskfile_filecopymove_postprocess(struct filecopy_t *fc)
{
 struct frame *frp=fc->mvp->frp0;

 pds_fullpath(fc->outfilename,fc->outfilename);

 if((fc->cpy_ctrl&DISKFILE_CPYCTRL_MOVE) && (fc->retcode==MPXPLAY_ERROR_FILEHAND_OK) && (pds_stricmp(fc->mvp->pei0->filename,fc->infilename)==0)){ // currently playing file is moved
  struct playlist_side_info *psi=fc->psi;
  if(playlist_editlist_addfile_one(NULL,psi,fc->mvp->pei0,NULL,EDITLIST_MODE_ID3|EDITLIST_MODE_INDEX)){
   psi->lastentry->filename=&fc->outfilename[0];
   playlist_pei0_set(fc->mvp,psi->lastentry);
   playlist_editlist_delfile_one(psi,psi->lastentry,(EDITLIST_MODE_FILENAME|EDITLIST_MODE_ID3|EDITLIST_MODE_ENTRY));
  }
  if(!diskfile_filemove_check(fc)){ // moved to other drive
   if(mpxinbuf_lowlevel_file_isopen(frp)){ // re-open at new location
    mpxinbuf_lowlevel_file_close(frp);
    //mpxinbuf_lowlevel_file_parse_lowfuncs(frp,fc->outfilename);
    if(mpxinbuf_lowlevel_file_open_read(frp,fc->outfilename))
     mpxinbuf_lowlevel_file_seek(frp,frp->filepos);
   }
  }
 }

 if((fc->cpy_ctrl&(DISKFILE_CPYCTRL_MOVE|DISKFILE_CPYCTRL_COPY)) && (fc->retcode==MPXPLAY_ERROR_FILEHAND_OK) && !mpxinbuf_lowlevel_file_isopen(frp) && (pds_stricmp(fc->mvp->pei0->filename,fc->outfilename)==0)){ // re-open currently played file (delete, after copy/create again)
  if(mpxinbuf_lowlevel_file_open_read(frp,fc->outfilename))
   mpxinbuf_lowlevel_file_seek(frp,frp->filepos);
 }

 if(fc->pei_curr && (fc->cpy_ctrl&(DISKFILE_CPYCTRL_COPY|DISKFILE_CPYCTRL_MOVE))){
  // delete, update or create new playlist entry in directory browser after copy/move
  if((fc->retcode==MPXPLAY_ERROR_FILEHAND_OK) || (fc->retcode==MPXPLAY_ERROR_FILEHAND_CANTREAD) || (fc->retcode==MPXPLAY_ERROR_FILEHAND_CANTWRITE) || (fc->retcode==MPXPLAY_ERROR_FILEHAND_USERABORT) || (fc->retcode==MPXPLAY_ERROR_FILEHAND_DELETE)){
   pds_getpath_from_fullname(fc->outpath_curr,fc->outfilename);
   diskfile_filecopymove_update_sides(fc,fc->infilename,fc->outfilename,fc->pei_curr);
  }
 }

 if(!(fc->cpy_ctrl&DISKFILE_CPYCTRL_ALLFILE))
  funcbit_disable(fc->cpy_ctrl,(DISKFILE_CPYCTRL_OVERWRITE|DISKFILE_CPYCTRL_SKIPFILE));

 display_textwin_closewindow_buttons(fc->tw);
 fc->tw=NULL;

 if(fc->retcode!=MPXPLAY_ERROR_FILEHAND_OK){
  if(fc->retcode==MPXPLAY_ERROR_FILEHAND_USERABORT) // aborted by user
   goto err_out_finish;
  pds_strcpy(fc->lastfilename,fc->outfilename);
 }

 fc->outfilename[0]=0;
 if(fc->pei_curr){ // if not a subdir-filename
  if(((fc->cpy_ctrl&DISKFILE_CPYCTRL_COPY) && (fc->retcode==MPXPLAY_ERROR_FILEHAND_OK)) || (fc->retcode==MPXPLAY_ERROR_FILEHAND_DELETE)){
   if(fc->pei_curr->infobits&PEIF_SELECTED){
    funcbit_disable(fc->pei_curr->infobits,PEIF_SELECTED);
    if(fc->psi->selected_files)
     fc->psi->selected_files--;
   }
  }
  if((fc->cpy_ctrl&DISKFILE_CPYCTRL_COPY) || (fc->retcode!=MPXPLAY_ERROR_FILEHAND_OK))
   fc->pei_curr++; // not incremented if move was successfull (because then the entry was deleted above)
 }

 if((fc->retcode!=MPXPLAY_ERROR_FILEHAND_OK) && (fc->retcode!=MPXPLAY_ERROR_FILEHAND_SKIPFILE) && !(fc->cpy_ctrl&DISKFILE_CPYCTRL_IGNALLERR)){// || (fc->retcode!=fc->last_error)){
  fc->last_error=fc->retcode;
  playlist_diskfile_show_errmsg(fc);
  return;
 }

 mpxplay_timer_addfunc(diskfile_filecopymove_loop,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
 return;

err_out_finish:
 mpxplay_timer_addfunc(diskfile_filecopy_dealloc,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
}

static void diskfile_filecopymove_select(struct filecopy_t *fc)
{
 fc->filenum_curr++;
 if(fc->retcode==MPXPLAY_ERROR_FILEHAND_OK){
  if((fc->cpy_ctrl&DISKFILE_CPYCTRL_MOVE) && diskfile_filemove_check(fc))
   diskfile_filemove_do(fc);
  else{
   mpxplay_timer_addfunc(diskfile_filecopy_openfiles,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
   return;
  }
 }
 mpxplay_timer_addfunc(diskfile_filecopymove_postprocess,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
}

static void diskfile_filecopymove_loop(struct filecopy_t *fc)
{
 int retcode;

 fc->retcode=MPXPLAY_ERROR_FILEHAND_OK;

 retcode=diskfile_filecopymove_getnextfile(fc);

 if(retcode>0){  // no file
  if(retcode==1){ // no file, get next (loop in timer)
   if((fc->retcode!=MPXPLAY_ERROR_FILEHAND_OK) && !(fc->cpy_ctrl&DISKFILE_CPYCTRL_IGNALLERR))
    goto err_out_loop;
   else
    mpxplay_timer_modifyfunc(diskfile_filecopymove_loop,MPXPLAY_TIMERTYPE_REPEAT|MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
  }else{ //do nothing (warning message follows)
   mpxplay_timer_modifyfunc(&diskfile_filecopymove_loop,0,0);
  }
 }else{
  mpxplay_timer_modifyfunc(&diskfile_filecopymove_loop,0,0);
  if(retcode==0){ // no error, do copy/move/del
   if(fc->cpy_ctrl&DISKFILE_CPYCTRL_DEL)
    playlist_diskfile_delete_do(fc);
   else
    diskfile_filecopymove_makefilenames(fc);
  }else{           // finish/abort copy
   if((fc->retcode!=MPXPLAY_ERROR_FILEHAND_OK) && !(fc->cpy_ctrl&DISKFILE_CPYCTRL_IGNALLERR)){
    funcbit_disable(fc->cpy_ctrl,DISKFILE_CPYCTRL_MULTIFILE); // hack for [Ok]
    goto err_out_loop;
   }else
    mpxplay_timer_addfunc(diskfile_filecopy_dealloc,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
  }
 }
 return;

err_out_loop:
 mpxplay_timer_modifyfunc(&diskfile_filecopymove_loop,0,0);
 fc->last_error=fc->retcode;
 playlist_diskfile_show_errmsg(fc);
}

static display_textwin_button_t copymove_confirm_buttons[]={
 {""          ,KEY_ENTER1},// gray enter
 {""          ,KEY_ENTER2},// white enter
 {"[ Cancel ]",0x2e63},    // 'c'
 {""          ,0x2e43},    // 'C'
 {""          ,KEY_ESC},   // ESC
 {NULL,0}
};

static void diskfile_copymove_start(struct filecopy_t *fc,unsigned int extkey)
{
 switch(extkey){
  case KEY_ENTER1:
  case KEY_ENTER2:
   diskfile_count_files_stop(fc);
   if(((fc->cpy_ctrl&DISKFILE_CPYCTRL_MULTIFILE) || fc->directories_selected) && (!fc->filemask[0] || !pds_strlenc(fc->filemask,' '))){ // empty filemask
    if(fc->cpy_ctrl&DISKFILE_CPYCTRL_DEL)
     pds_strcpy(filemask_default_del,"*.?*"); // restore default
    else
     pds_strcpy(filemask_default_copy,"*.*"); //
   }else{
    pds_sfn_limit(fc->outpath_root);
    mpxplay_timer_addfunc(diskfile_filecopymove_loop,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
    //diskfile_keyboardtopfunc_init();
    if(fc->cpy_ctrl&DISKFILE_CPYCTRL_MULTIFILE){
     if(fc->cpy_ctrl&DISKFILE_CPYCTRL_DEL)
      pds_strcpy(filemask_default_del,fc->filemask);
     else
     pds_strcpy(filemask_default_copy,fc->filemask);
     if(pds_strricmp(fc->filemask,".*")!=0)
      funcbit_enable(fc->cpy_ctrl,DISKFILE_CPYCTRL_NOEXTCHK);
    }
    break;
   }
  case 0x2e63:
  case 0x2e43:
  case KEY_ESC:
   mpxplay_timer_addfunc(diskfile_filecopy_dealloc,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
 }
}

void playlist_diskfile_copy_or_move(struct mainvars *mvp,unsigned int move)
{
 struct filecopy_t *fc;
 struct playlist_side_info *psi=mvp->psie,*psio=psi->psio;
 const static char *functext[2]={"Copy","Move"};
 char msg[MAX_PATHNAMELEN+64];
 void *tw;

 if(move>1)
  return;
 if(!(displaymode&DISP_FULLSCREEN))
  return;

 fc=diskfile_filecopy_alloc(mvp);
 if(!fc)
  return;

 if(move)
  funcbit_enable(fc->cpy_ctrl,DISKFILE_CPYCTRL_MOVE);
 else
  funcbit_enable(fc->cpy_ctrl,DISKFILE_CPYCTRL_COPY);

 funcbit_enable(fc->cpy_ctrl,DISKFILE_CPYCTRL_EMPTYDIRCOPY); // !!!

 if(psi->selected_files){
  funcbit_enable(fc->cpy_ctrl,DISKFILE_CPYCTRL_MULTIFILE);
  fc->pei_selected=fc->psi->firstentry;
  fc->pei_last=psi->lastentry;
 }else
  fc->pei_selected=fc->pei_last=fc->psi->editorhighline;

 if((psio->editsidetype&PLT_DIRECTORY) && !psio->sublistlevel)
  pds_strcpy(fc->outpath_root,psio->currdir);
 else
  if(psi->editsidetype&PLT_DIRECTORY)
   pds_strcpy(fc->outpath_root,psi->currdir);
  else
   pds_getcwd(fc->outpath_root);

 diskfile_count_files_start(fc);

 if(psi->selected_files || !(fc->count_ctrl&DISKFILE_COUNTCTRL_COMPLETE)){
  sprintf(fc->headtext," %s files ",functext[move]);
  sprintf(msg," %s %d file%s to",functext[move],psi->selected_files,((psi->selected_files>1)? "s":""));
 }else{
  struct playlist_entry_info *pei=psi->editorhighline;
  sprintf(fc->headtext," %s file ",functext[move]);
  sprintf(msg," %s \"%s\" to",functext[move],pds_getfilename_from_fullname(pei->filename));
 }
 sprintf(fc->buttontext,"[%s]",fc->headtext);
 copymove_confirm_buttons[0].text=&fc->buttontext[0];

 fc->count_tw=tw=display_textwin_allocwindow_items(TEXTWIN_FLAG_MSGCENTERALIGN,fc->headtext,diskfile_copymove_start,fc);
 fc->count_itemnum=display_textwin_additem_msg_alloc(tw,0,0,0,msg);

 if(psi->selected_files || fc->directories_selected){
  display_textwin_additem_editline(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,1,50,fc->outpath_root,MAX_PATHNAMELEN-2);
  display_textwin_additem_separatorline(tw,2);
  pds_strcpy(fc->filemask,filemask_default_copy);
  display_textwin_additem_msg_static(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,3,"Filemask: ");
  display_textwin_additem_editline(tw,TEXTWIN_FLAG_MSGCENTERALIGN,sizeof("Filemask: ")-1,3,8,fc->filemask,sizeof(fc->filemask)-1);
  //display_textwin_additem_msg_static(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,4,"(\"*.*\" : supported files; \"*.?*\" : all files)");
 }else{
  struct playlist_entry_info *pei=psi->editorhighline;
  unsigned int pathlen=pds_strcpy(fc->outfilename,fc->outpath_root);
  if(fc->outfilename[pathlen-1]!=PDS_DIRECTORY_SEPARATOR_CHAR)
   pds_strcpy(&fc->outfilename[pathlen++],PDS_DIRECTORY_SEPARATOR_STR);
  pds_strcpy(&fc->outfilename[pathlen],pds_getfilename_from_fullname(pei->filename));
  display_textwin_additem_editline(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,1,50,fc->outfilename,MAX_PATHNAMELEN-2);
 }

 display_textwin_additem_separatorline(tw,-1);
 display_textwin_additem_buttons(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,-1,&copymove_confirm_buttons[0],NULL);
 display_textwin_openwindow_items(tw,0,0,0);
}

//-------------------------------------------------------------------------
// delete files

static display_textwin_button_t delcurrplay_buttons[]={
 {" Delete "   ,0x2064}, // 'd'
 {""           ,0x2044}, // 'D'
 {" Skip "     ,0x1f73}, // 's'
 {""           ,0x1f53}, // 'S'
 {" Cancel "   ,0x2e63}, // 'c'
 {""           ,0x2e43}, // 'C'
 {""           ,KEY_ESC},// ESC
 {NULL,0}
};

static void diskfile_filedel_delcurrplay_keyhand(struct filecopy_t *fc,unsigned int extkey)
{
 switch(extkey){
  case 0x2064:
  case 0x2044:funcbit_enable(fc->cpy_ctrl,DISKFILE_CPYCTRL_DELFILE);
              playlist_diskfile_delete_do(fc);
              break;
  case 0x1f73:
  case 0x1f53:if(fc->pei_curr)
               fc->pei_curr++;
              mpxplay_timer_addfunc(diskfile_filecopymove_loop,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
              break;
  case 0x2e63:
  case 0x2e43:
  case KEY_ESC:fc->retcode=MPXPLAY_ERROR_FILEHAND_USERABORT;
               diskfile_filecopy_dealloc(fc);
 }
}

static void playlist_diskfile_delete_do(struct filecopy_t *fc)
{
 void *tw;
 struct frame *frp=fc->mvp->frp0;
 //char msg[MAX_PATHNAMELEN+64];

 if(pds_stricmp(fc->mvp->pei0->filename,fc->infilename)==0){
  if(fc->cpy_ctrl&DISKFILE_CPYCTRL_DELFILE)
   mpxinbuf_lowlevel_file_close(frp); // ???
  else
   goto err_out_delcurrplay;
 }
 if(!(fc->cpy_ctrl&DISKFILE_CPYCTRL_ALLFILE))
  funcbit_disable(fc->cpy_ctrl,DISKFILE_CPYCTRL_DELFILE);

 fc->retcode=pds_unlink(fc->infilename);

 if(fc->retcode==MPXPLAY_ERROR_FILEHAND_OK){
  diskfile_filecopymove_update_sides(fc,fc->infilename,NULL,NULL);
 }else{
  if(fc->retcode==MPXPLAY_ERROR_FILEHAND_USERABORT) // aborted by user
   goto err_out_finish;
  pds_strcpy(fc->lastfilename,fc->infilename);
  if((pds_stricmp(fc->mvp->pei0->filename,fc->infilename)==0) && !mpxinbuf_lowlevel_file_isopen(frp)){ // re-open currently played file (if delete was not successull)
   if(mpxinbuf_lowlevel_file_open_read(frp,fc->infilename))
    mpxinbuf_lowlevel_file_seek(frp,frp->filepos);
  }
  fc->pei_curr++;
 }

 if((fc->retcode!=MPXPLAY_ERROR_FILEHAND_OK) && !(fc->cpy_ctrl&DISKFILE_CPYCTRL_IGNALLERR)){// || (fc->retcode!=fc->last_error)){
  fc->last_error=fc->retcode;
  playlist_diskfile_show_errmsg(fc);
  return;
 }

 mpxplay_timer_addfunc(diskfile_filecopymove_loop,fc,MPXPLAY_TIMERTYPE_REPEAT|MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0); // repeat flag needed to avoid stop
 return;

err_out_delcurrplay:
 tw=display_textwin_allocwindow_items(TEXTWIN_FLAG_ERRORMSG|TEXTWIN_FLAG_MSGCENTERALIGN,fc->headtext,diskfile_filedel_delcurrplay_keyhand,fc);
 display_textwin_additem_msg_static(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,-1,"Program is currently playing this file");
 display_textwin_additem_msg_alloc(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,-1,fc->infilename);
 display_textwin_additem_msg_static(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,-1,"Do you wish to delete it?");
 display_textwin_additem_buttons(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,-1,delcurrplay_buttons,NULL);
 display_textwin_openwindow_items(tw,0,0,0);
 return;

err_out_finish:
 mpxplay_timer_addfunc(diskfile_filecopy_dealloc,fc,MPXPLAY_TIMERFLAG_LOWPRIOR|MPXPLAY_TIMERFLAG_INDOS,0);
}

void playlist_diskfile_delete_init(struct mainvars *mvp)
{
 struct filecopy_t *fc;
 struct playlist_side_info *psi=mvp->psie;
 struct playlist_entry_info *pei=psi->editorhighline;
 unsigned int y;
 void *tw;
 char msg[MAX_PATHNAMELEN+64];

 if(!(displaymode&DISP_FULLSCREEN))
  return;

 fc=diskfile_filecopy_alloc(mvp);
 if(!fc)
  return;

 funcbit_enable(fc->cpy_ctrl,DISKFILE_CPYCTRL_DEL);
 funcbit_enable(fc->cpy_ctrl,DISKFILE_CPYCTRL_EMPTYDIRCOPY);

 if(psi->selected_files){
  funcbit_enable(fc->cpy_ctrl,DISKFILE_CPYCTRL_MULTIFILE);
  fc->pei_selected=fc->psi->firstentry;
  fc->pei_last=psi->lastentry;
 }else
  fc->pei_selected=fc->pei_last=fc->psi->editorhighline;

 diskfile_count_files_start(fc);

 if(psi->selected_files || !(fc->count_ctrl&DISKFILE_COUNTCTRL_COMPLETE))
  pds_strcpy(fc->headtext," Delete files ");
 else
  pds_strcpy(fc->headtext," Delete file ");

 sprintf(fc->buttontext,"[%s]",fc->headtext);
 copymove_confirm_buttons[0].text=&fc->buttontext[0];

 fc->count_tw=tw=display_textwin_allocwindow_items(TEXTWIN_FLAG_MSGCENTERALIGN,fc->headtext,diskfile_copymove_start,fc);

 y=0;
 if(psi->selected_files || !(fc->count_ctrl&DISKFILE_COUNTCTRL_COMPLETE)){
  if(fc->directories_selected){
   sprintf(msg,"You have selected %d files and %d directories",fc->counted_filenum,fc->directories_selected);
   display_textwin_additem_msg_alloc(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,y++,msg);
   sprintf(msg,"(total %d files and %d subdirs)",fc->counted_filenum,fc->counted_directories);
   fc->count_itemnum=display_textwin_additem_msg_alloc(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,y++,msg);
  }else{
   sprintf(msg,"You have selected %d files",fc->counted_filenum);
   display_textwin_additem_msg_alloc(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,y++,msg);
  }
  display_textwin_additem_msg_static(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,y++,"Do you wish to delete them?");
  display_textwin_additem_separatorline(tw,y++);
 }else{
  sprintf(msg,"Delete \"%s\" %s from disk?\nAre you sure?",pds_getfilename_from_fullname(pei->filename),((pei->entrytype==DFT_SUBDIR)? "directory":"file"));
  display_textwin_additem_msg_alloc(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,y++,msg);
 }
 if(psi->selected_files || fc->directories_selected){
  pds_strcpy(fc->filemask,filemask_default_del);
  display_textwin_additem_msg_static(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,y,"Filemask: ");
  display_textwin_additem_editline(tw,TEXTWIN_FLAG_MSGCENTERALIGN,sizeof("Filemask: ")-1,y++,8,fc->filemask,sizeof(fc->filemask)-1);
  //display_textwin_additem_msg_static(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,5,"(\"*.*\" : supported files; \"*.?*\" : all files)");
 }

 display_textwin_additem_separatorline(tw,-1);
 display_textwin_additem_buttons(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,-1,&copymove_confirm_buttons[0],NULL);
 display_textwin_openwindow_items(tw,0,0,0);
}
