//**************************************************************************
//*                     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:playlist entry add,delete,move

#include "newfunc\newfunc.h"
#include "playlist.h"
#include "display\display.h"
#include <malloc.h>

#define EDITLIST_ASM 1

extern char *id3filterkeyword;
extern unsigned int refdisp,displaymode;

struct playlist_entry_info *playlist_editlist_addfileone_postproc(struct playlist_side_info *psi_dest,struct playlist_entry_info *pei_dest)
{
 struct mainvars *mvp=psi_dest->mvp;
 unsigned int entrytype=pei_dest->entrytype;
 struct playlist_entry_info *pei_newpos;

 if((psi_dest==mvp->psip) && !(psi_dest->editsidetype&PLT_DOOMQUEUE) && (mvp->aktfilenum<psi_dest->firstsong) && (pds_stricmp(pei_dest->filename,mvp->pei0->filename)==0)){
  struct frame *frp=mvp->frp0;
  mvp->aktfilenum=pei_dest;
  playlist_randlist_pushq(psi_dest,pei_dest);
  mpxplay_calculate_index_start_end(frp,mvp,pei_dest); // switch between cue and other playlist
 }
 pei_newpos=playlist_order_entry(psi_dest,pei_dest);
 if(entrytype&DFTM_PLAYLIST)
  playlist_search_firstsong(psi_dest);
 return pei_newpos;
}

void playlist_editlist_addfile_any(struct playlist_side_info *psi_src,struct playlist_entry_info *pei_src)
{
 struct playlist_side_info *psi_dest=psi_src->psio;
 struct playlist_entry_info *startentry;
 unsigned int len;
 char strtmp[MAX_PATHNAMELEN];

 if(!(psi_src->editsidetype&PLT_ENABLED))
  return;

 if(GET_HFT(pei_src->entrytype)==HFT_DFT){  // add drive, dir or playlist
  startentry=psi_dest->lastentry;
  switch(pei_src->entrytype){
   case DFT_PLAYLIST:
   case DFT_SUBLIST :pds_strcpy(strtmp,pei_src->filename);
		     break;
   case DFT_SUBDIR:len=pds_strcpy(strtmp,pei_src->filename);
                   len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                   len+=pds_strcpy(&strtmp[len],"*.*");
                   len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                   pds_strcpy(&strtmp[len],"*.*");
		   break;
   case DFT_DRIVE:len=pds_strcpy(strtmp,"d:");
                  len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                  len+=pds_strcpy(&strtmp[len],"*.*");
                  len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                  pds_strcpy(&strtmp[len],"*.*");
                  strtmp[0]=pei_src->filename[0]; // copy drive label
		  break;
   default:return;
  }
  playlist_buildlist_one(psi_dest,strtmp,0,NULL);
  if(psi_dest->lastentry>startentry){
   if(startentry<psi_dest->firstentry)
    playlist_loadsub_setnewinputfile(psi_dest,strtmp); // !!!!!!
   playlist_chkfile_start_norm(psi_dest,startentry+1);
  }
 }else{
  if(playlist_editlist_addfile_one(psi_src,psi_dest,pei_src,NULL,EDITLIST_MODE_ALL))
   playlist_editlist_addfileone_postproc(psi_dest,psi_dest->lastentry);
 }
}

void playlist_editlist_copy_entry(struct playlist_side_info *psi_src,struct playlist_entry_info *pei_src)
{
 struct playlist_side_info *psi_dest=psi_src->psio;
 struct playlist_entry_info pei_tmp;
 unsigned int len;
 char strtmp[MAX_PATHNAMELEN];

 if(!(psi_src->editsidetype&PLT_ENABLED))
  return;

 if(GET_HFT(pei_src->entrytype)==HFT_DFT){  // copy drive, dir or playlist
  pds_memset(&pei_tmp,0,sizeof(struct playlist_entry_info));
  switch(pei_src->entrytype){
   case DFT_PLAYLIST:
   case DFT_SUBLIST :pds_strcpy(strtmp,pei_src->filename);
                     pei_tmp.id3info[I3I_ARTIST]=pei_src->id3info[I3I_ARTIST];
                     pei_tmp.id3info[I3I_TITLE]=pei_src->id3info[I3I_TITLE];
		     break;
   case DFT_SUBDIR:len=pds_strcpy(strtmp,pei_src->filename);
                   len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                   len+=pds_strcpy(&strtmp[len],"*.*");
                   len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                   pds_strcpy(&strtmp[len],"*.*");
		   break;
   case DFT_DRIVE:len=pds_strcpy(strtmp,"d:");
                  len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                  len+=pds_strcpy(&strtmp[len],"*.*");
                  len+=pds_strcpy(&strtmp[len],PDS_DIRECTORY_SEPARATOR_STR);
                  pds_strcpy(&strtmp[len],"*.*");
                  strtmp[0]=pei_src->filename[0]; // copy drive label
		  break;
   default:return;
  }
  pei_tmp.filename=&strtmp[0];
  if((pei_src->entrytype&DFTM_PLAYLIST) && (psi_dest->editsidetype&PLT_DIRECTORY) && !psi_dest->sublistlevel && !(psi_src->editsidetype&PLT_DIRECTORY)){
   pei_tmp.entrytype=DFT_PLAYLIST;
   if(pds_strcmp(pei_tmp.id3info[I3I_DFT_STORE],DFTSTR_SUBLIST)==0)
    pei_tmp.id3info[I3I_DFT_STORE]=DFTSTR_PLAYLIST;
  }else{
   pei_tmp.entrytype=DFT_SUBLIST;
   if((!pei_tmp.id3info[I3I_ARTIST] && !pei_tmp.id3info[I3I_TITLE]) || pds_strcmp(pei_tmp.id3info[I3I_DFT_STORE],DFTSTR_PLAYLIST)==0)
    pei_tmp.id3info[I3I_DFT_STORE]=DFTSTR_SUBLIST;
  }
  playlist_editlist_addfile_one(psi_src,psi_dest,&pei_tmp,NULL,EDITLIST_MODE_ALL);
  if(pei_tmp.entrytype&DFTM_PLAYLIST){
   playlist_order_entry(psi_dest,psi_dest->lastentry);
   playlist_search_firstsong(psi_dest);
  }
 }else{
  if(playlist_editlist_addfile_one(psi_src,psi_dest,pei_src,NULL,EDITLIST_MODE_ALL))
   playlist_editlist_addfileone_postproc(psi_dest,psi_dest->lastentry);
 }
}

unsigned int playlist_editlist_addfile_one(struct playlist_side_info *psi_src,struct playlist_side_info *psi_dest,struct playlist_entry_info *pei_src,struct playlist_entry_info *pei_dest,unsigned int modify)
{
 unsigned int i;

 if(psi_src && !(psi_src->editsidetype&PLT_ENABLED))
  return 0;

 if(!psi_dest)
  if(psi_src)
   psi_dest=psi_src->psio;
  else
   return 0;

 playlist_enable_side(psi_dest);

 if(!pei_dest){
  pei_dest=psi_dest->lastentry+1;
  if(pei_dest>psi_dest->endentry)
   return 0;
  pds_memset(pei_dest,0,sizeof(struct playlist_entry_info));
 }else{
  if(pei_dest>psi_dest->endentry)
   return 0;
 }
 if(pei_dest>psi_dest->lastentry)
  psi_dest->lastentry=pei_dest;

 if(modify&EDITLIST_MODE_FILENAME){
  char *flastp=psi_dest->filenameslastp;
  if(flastp<psi_dest->filenamesendp){
   pei_dest->filename=flastp;
   flastp+=pds_strcpy(flastp,pei_src->filename)+1;
   psi_dest->filenameslastp=flastp;
   if(psi_src && psi_src->chkfilenum_begin){
    if(pei_src>=psi_src->chkfilenum_begin)
     playlist_chkfile_start_norm(psi_dest,pei_dest);
   }
  }else
   return 0;
 }
 if(modify&EDITLIST_MODE_INDEX){
  funcbit_disable(pei_dest->infobits,PEIF_INDEXED);
  funcbit_copy(pei_dest->infobits,pei_src->infobits,PEIF_INDEXED);
  pei_dest->pstime=pei_src->pstime;
  pei_dest->petime=pei_src->petime;
 }
 if(modify&(EDITLIST_MODE_HEAD|EDITLIST_MODE_ENTRY)){
  pei_dest->entrytype=pei_src->entrytype;
  funcbit_disable(pei_dest->infobits,PEIF_COPYMASK);
  funcbit_copy(pei_dest->infobits,pei_src->infobits,PEIF_COPYMASK);
  pei_dest->timemsec=pei_src->timemsec;
  pei_dest->filesize=pei_src->filesize;
  pei_dest->infile_funcs=pei_src->infile_funcs;
  playlist_fulltime_add(psi_dest,pei_dest);
 }
 if(modify&EDITLIST_MODE_ID3){
  char *id3lastp=psi_dest->id3infolastp;
  for(i=0;(i<=I3I_MAX) && (id3lastp<psi_dest->id3infoendp);i++){
   if(!pei_dest->id3info[i]){ // !!! get_onefileinfos_from_otherside()
    if((!psi_src && pei_src->id3info[i]) || ((pei_src->id3info[i]>=psi_src->id3infobeginp) && (pei_src->id3info[i]<psi_src->id3infoendp))){
     pei_dest->id3info[i]=id3lastp;
     id3lastp+=pds_strcpy(id3lastp,pei_src->id3info[i])+1;
    }else
     pei_dest->id3info[i]=pei_src->id3info[i]; // NULL or mp3-genre
   }
  }
  psi_dest->id3infolastp=id3lastp;
  funcbit_disable(pei_dest->infobits,PEIF_ID3MASK);
  funcbit_copy(pei_dest->infobits,pei_src->infobits,PEIF_ID3MASK);
 }
 return modify;
}

#ifdef __WATCOMC__
void asm_delfile_filename(char **,unsigned int,unsigned int,char *,char *);
void asm_delfile_id3infos(char **,unsigned int,unsigned int,char *,char *);
#endif

void playlist_editlist_delfile_one(struct playlist_side_info *psi,struct playlist_entry_info *pei,unsigned int modify)
{
 char *pf;
 unsigned int i,memlen_move;

 if((psi->lastentry<psi->firstentry) || (pei<psi->firstentry) || (pei>psi->lastentry)
    || (pei->entrytype&DFTM_DRIVE) || (pei->entrytype==DFT_UPDIR) || (pei->entrytype==DFT_UPLIST))
  return;

 //delete filename from the filenamesworkarea
 if(modify&EDITLIST_MODE_FILENAME){
  pf=pei->filename;
  pei->filename=NULL;
  if(pf>=psi->filenamesbeginp && pf<psi->filenameslastp){
   //struct playlist_entry_info *peii;
   unsigned int memlen_del;
   char **fn,*flp;
   memlen_del=pds_strlen(pf)+1;
   memlen_move=psi->filenameslastp-pf;
   pds_memcpy(pf,pf+memlen_del,memlen_move);

   i=psi->lastentry-psi->firstentry+1;
   fn=&(psi->firstentry->filename);
   flp=psi->filenameslastp;

   // !!! sizeof(struct playlist_entry_info) dependant
#if defined(EDITLIST_ASM) && defined(__WATCOMC__)
   #pragma aux asm_delfile_filename=\
   "back1:"\
    "mov eax,dword ptr [ebx]"\
    "cmp eax,edi"\
    "jbe skip"\
    "cmp eax,esi"\
    "jae skip"\
     "sub eax,edx"\
     "mov dword ptr [ebx],eax"\
    "skip:add ebx,64"\
    "dec ecx"\
    "jnz back1"\
    parm [ebx][ecx][edx][edi][esi] modify[eax ebx ecx];
    asm_delfile_filename(fn,i,memlen_del,pf,flp);
#else
   do{
    char *fnp=*fn;
    if((fnp>pf) && (fnp<flp)){  // pointer is in filenamesworkarea
     fnp-=memlen_del;             // move down filename pointer
     *fn=fnp;
    }
    fn+=sizeof(struct playlist_entry_info)/sizeof(*fn);
   }while(--i);
#endif

   /*for(peii=psi->firstentry;peii<=psi->lastentry;peii++)
    if(peii->filename>pf && peii->filename<psi->filenameslastp) // pointer is in filenamesworkarea
     peii->filename-=memlen_del; // move down filename pointer*/

   psi->filenameslastp-=memlen_del;
  }
 }
 //delete id3infos from the id3infoworkarea
 if(modify&EDITLIST_MODE_ID3){
  i=0;
  do{
   pf=pei->id3info[i];
   pei->id3info[i]=NULL;
   if((pf>=psi->id3infobeginp) && (pf<psi->id3infolastp)){
    //struct playlist_entry_info *peii;
    char **id3iip,*ilp;
    unsigned int k,memlen_del=pds_strlen(pf)+1;
    memlen_move=psi->id3infolastp-pf;
    pds_memcpy(pf,pf+memlen_del,memlen_move);

    k=psi->lastentry-psi->firstentry+1;
    id3iip=&(psi->firstentry->id3info[0]);
    ilp=psi->id3infolastp;

    // !!! sizeof(struct playlist_entry_info) dependant
#if defined(EDITLIST_ASM) && defined(__WATCOMC__) && (I3I_MAX==6)
    #pragma aux asm_delfile_id3infos=\
    "push ebp"\
    "back_k:"\
     "mov ebp,7"\
     "back_j:"\
      "mov eax,dword ptr [ebx]"\
      "cmp eax,edi"\
      "jbe skip"\
      "cmp eax,esi"\
      "jae skip"\
       "sub eax,edx"\
       "mov dword ptr [ebx],eax"\
      "skip:add ebx,4"\
      "dec ebp"\
     "jnz back_j"\
     "add ebx,36"\
     "dec ecx"\
    "jnz back_k"\
    "pop ebp"\
    parm [ebx][ecx][edx][edi][esi] modify[eax ebx ecx];
    asm_delfile_id3infos(id3iip,k,memlen_del,pf,ilp);
#else
    do{
     int j=I3I_MAX+1;
     do{
      char *idp=*id3iip;
      if((idp>pf) && (idp<ilp)){ // pointer is in id3infoworkarea
       idp-=memlen_del;          //move down id3info[] pointer
       *id3iip=idp;
      }
      id3iip++;
     }while(--j);
     id3iip+=(sizeof(struct playlist_entry_info)-sizeof(pei->id3info))/sizeof(*id3iip);
    }while(--k);
#endif
    /*for(peii=psi->firstentry;peii<=psi->lastentry;peii++){
     char **id3iip=&peii->id3info[0];
     int j=I3I_MAX+1;
     do{
      if((id3iip[0]>pf) && (id3iip[0]<psi->id3infolastp)) // pointer is in id3infoworkarea
       id3iip[0]-=memlen_del; //move down id3info[] pointer
      id3iip++;
     }while(--j);
    }*/
    psi->id3infolastp-=memlen_del;
   }
  }while(++i<=I3I_MAX);
 }

 if(modify&EDITLIST_MODE_HEAD)
  playlist_fulltime_del(psi,pei);

 if(modify&EDITLIST_MODE_ENTRY){
  if((pei->infobits&PEIF_SELECTED) && psi->selected_files)
   psi->selected_files--;
  if((pei->entrytype&DFTM_DFT) && (pei<psi->firstsong) && (psi->firstsong>psi->firstentry))
   psi->firstsong--;

  playlist_randlist_delete(pei);

  //move down entries
  if(pei<psi->lastentry)
   pds_memcpy(pei,pei+1,(char *)(psi->lastentry)-(char *)pei);

  //clear last entry
  pds_memset(psi->lastentry,0,sizeof(struct playlist_entry_info));

  //correct pointers in random queue and at pei->myself
  playlist_randlist_correctq(psi,pei,psi->lastentry);
  playlist_peimyself_reset(psi,pei,psi->lastentry);

  //correct some variables (chkfilenum,aktfilenum,newfilenum)
  psi->lastentry--;
  if(psi->lastentry<psi->firstentry){
   playlist_disable_side_full(psi);
  }else{
   struct mainvars *mvp=psi->mvp;
   if(psi->chkfilenum_begin){
    if(psi->chkfilenum_begin>=pei)
     psi->chkfilenum_begin--;
    psi->chkfilenum_end--;
   }
   if(psi==mvp->psip){
    if(mvp->newfilenum){
     if(mvp->newfilenum>pei)
      mvp->newfilenum--;
     if(mvp->newfilenum>psi->lastentry)
      mvp->newfilenum=psi->lastentry;
     if(mvp->newfilenum<psi->firstsong)
      mvp->newfilenum=NULL;
    }
    if(mvp->aktfilenum>=psi->firstsong){
     if(mvp->aktfilenum>pei)
      mvp->aktfilenum--;
     else{
      if(mvp->aktfilenum==pei){
       mvp->aktfilenum=psi->firstsong-1;
       if(!mvp->newfilenum && (pei>psi->firstsong)){
        if(pei>psi->lastentry)
         pei=psi->lastentry;
        mvp->newfilenum=pei;
       }
      }
     }
    }
   }
   playlist_editorhighline_check(psi);
  }
 }
}

void playlist_editlist_delete_entry_manual(struct playlist_side_info *psi,struct playlist_entry_info *pei)
{
 if(!(pei->entrytype&DFTM_DRIVE) && !(pei->entrytype&DFTM_DIR) && ((psi->lastentry>psi->firstentry) || (psi->psio->editsidetype&PLT_ENABLED))){
  playlist_editlist_delfile_one(psi,pei,EDITLIST_MODE_ALL);
  refdisp|=RDT_RESET_EDIT;
 }
}

unsigned int playlist_editlist_allocated_copy_entry(struct playlist_entry_info *pei_dest,struct playlist_entry_info *pei_src)
{
 unsigned int len,i;
 if(!pei_src || !pei_dest)
  return 0;
 pds_memcpy(pei_dest,pei_src,sizeof(struct playlist_entry_info));
 pei_dest->filename=NULL;
 pds_memset(&pei_dest->id3info[0],0,sizeof(pei_dest->id3info));
 funcbit_enable(pei_dest->infobits,PEIF_ALLOCATED);
 len=pds_strlen(pei_src->filename);
 if(len){
  pei_dest->filename=malloc(len+1);
  if(!pei_dest->filename)
   goto err_out_dup;
  pds_strcpy(pei_dest->filename,pei_src->filename);
 }
 for(i=0;i<=I3I_MAX;i++){
  len=pds_strlen(pei_src->id3info[i]);
  if(len){
   pei_dest->id3info[i]=malloc(len+1);
   if(!pei_dest->id3info[i])
    goto err_out_dup;
   pds_strcpy(pei_dest->id3info[i],pei_src->id3info[i]);
  }
 }
 return 1;

err_out_dup:
 playlist_editlist_allocated_clear_entry(pei_dest);
 return 0;
}

void playlist_editlist_allocated_clear_entry(struct playlist_entry_info *pei)
{
 unsigned int i;
 if(!funcbit_test(pei->infobits,PEIF_ALLOCATED))
  return;
 if(pei->filename)
  free(pei->filename);
 for(i=0;i<=I3I_MAX;i++)
  if(pei->id3info[i])
   free(pei->id3info[i]);
 pds_memset(pei,0,sizeof(playlist_entry_info));
}

void playlist_editlist_addfile_ins_ehl(struct playlist_side_info *psi_src,struct playlist_entry_info *pei_src)
{
 if(GET_HFT(pei_src->entrytype)==HFT_DFT)
  playlist_editlist_addfile_any(psi_src,pei_src);
 else{
  struct playlist_side_info *psi_dest;
  struct playlist_entry_info *pei_dest;
  if(!playlist_editlist_addfile_one(psi_src,NULL,pei_src,NULL,EDITLIST_MODE_ALL))
   return;
  psi_dest=psi_src->psio;
  pei_dest=psi_dest->editorhighline;
  if(pei_dest<psi_dest->firstsong)
   pei_dest=psi_dest->firstsong;
  if(pei_dest<(psi_dest->lastentry-1)){
   playlist_editorhighline_set(psi_dest,psi_dest->lastentry);
   do{
    playlist_editlist_shiftfile(psi_dest,-1);
   }while(psi_dest->editorhighline>pei_dest);
  }else{
   playlist_editorhighline_set(psi_dest,pei_dest);
  }
  playlist_editorhighline_seek(psi_dest,+1,SEEK_CUR);
 }
}

// move/shift entry (up or down) in the playlist (ie: at ctrl-up/ctrl-down)
void playlist_editlist_shiftfile(struct playlist_side_info *psi,int direction)
{
 struct playlist_entry_info *pei_dest;

 pei_dest=psi->editorhighline+direction;
 if((psi->editorhighline>=psi->firstsong) && (pei_dest>=psi->firstsong) && (pei_dest<=psi->lastentry)){
  playlist_swap_entries(psi,psi->editorhighline,pei_dest);
  playlist_editorhighline_set(psi,pei_dest);
 }
}

//move playlist entry via the mouse (drag & move)
void playlist_editlist_mouse_shiftfile(struct mainvars *mvp,struct playlist_entry_info *ehls)
{
 int mfd;
 unsigned int i;
 struct playlist_side_info *psi=mvp->psie;
 struct playlist_entry_info *ehld=psi->editorhighline;

 if(ehls!=ehld){
  playlist_editorhighline_set_nocenter(psi,ehls);
  if(ehld>ehls){
   i=ehld-ehls;
   mfd=1;
  }else{
   i=ehls-ehld;
   mfd=-1;
  }
  for(;i;i--)
   playlist_editlist_shiftfile(psi,mfd);

  if(psi==mvp->psip)
   refdisp|=RDT_EDITOR|RDT_BROWSER;
  else
   refdisp|=RDT_EDITOR;
 }
}

//----------------------------------------------------------------------
//group functions

// copy (add) all playlist entries to the other side
void playlist_editlist_copyside(struct playlist_side_info *psi_src)
{
 struct playlist_entry_info *pei_src;

 for(pei_src=psi_src->firstsong;pei_src<=psi_src->lastentry;pei_src++)
  if(GET_HFT(pei_src->entrytype)!=HFT_DFT)
   playlist_editlist_addfile_one(psi_src,NULL,pei_src,NULL,EDITLIST_MODE_ALL);

 playlist_order_side(psi_src->psio);

 refdisp|=RDT_EDITOR|RDT_RESET_EDIT;
 if(psi_src->psio==psi_src->mvp->psip)
  refdisp|=RDT_BROWSER;
}

void playlist_editlist_addfile_selected_group(struct playlist_side_info *psi_src)
{
 struct playlist_entry_info *pei;

 if(psi_src->selected_files){
  for(pei=psi_src->firstentry;pei<=psi_src->lastentry;pei++)
   if(pei->infobits&PEIF_SELECTED){
    playlist_editlist_addfile_any(psi_src,pei);
    funcbit_disable(pei->infobits,PEIF_SELECTED);
   }
  psi_src->selected_files=0;
 }else
  playlist_editlist_addfile_any(psi_src,psi_src->editorhighline); // !!!

 refdisp|=RDT_EDITOR|RDT_RESET_EDIT;
 if(psi_src->psio==psi_src->mvp->psip)
  refdisp|=RDT_BROWSER;
}

void playlist_editlist_copy_selected_group(struct playlist_side_info *psi_src)
{
 struct playlist_entry_info *pei;

 if(psi_src->selected_files){
  for(pei=psi_src->firstentry;pei<=psi_src->lastentry;pei++)
   if(pei->infobits&PEIF_SELECTED){
    playlist_editlist_copy_entry(psi_src,pei);
    funcbit_disable(pei->infobits,PEIF_SELECTED);
   }
  psi_src->selected_files=0;
 }else
  playlist_editlist_copy_entry(psi_src,psi_src->editorhighline); // !!!

 refdisp|=RDT_EDITOR|RDT_RESET_EDIT;
 if(psi_src->psio==psi_src->mvp->psip)
  refdisp|=RDT_BROWSER;
}

void playlist_editlist_move_selected_group(struct playlist_side_info *psi_src)
{
 struct playlist_entry_info *pei;

 if(psi_src->selected_files){
  for(pei=psi_src->firstentry;pei<=psi_src->lastentry;pei++)
   if(pei->infobits&PEIF_SELECTED)
    playlist_editlist_copy_entry(psi_src,pei);
 }else
  playlist_editlist_copy_entry(psi_src,psi_src->editorhighline); // !!!

 playlist_editlist_delfile_selected_group(psi_src);

 refdisp|=RDT_EDITOR;
 if(psi_src->psio==psi_src->mvp->psip)
  refdisp|=RDT_BROWSER;
}

void playlist_editlist_delfile_selected_group(struct playlist_side_info *psi)
{
 struct playlist_entry_info *pei;
 unsigned int delcount,delfilenum,allfilenum;
 void *tw=NULL;
 char sout[48];

 if(psi->selected_files){
  pei=psi->lastentry;
  delcount=0;
  delfilenum=psi->selected_files;
  allfilenum=(psi->lastentry-psi->firstentry+1);
  if((delfilenum==allfilenum) && !(psi->editsidetype&PLT_DIRECTORY) && !(psi->psio->editsidetype&PLT_DOOMQUEUE)){
   playlist_clear_side(psi);
   playlist_disable_side_full(psi);
  }else{
   do{
    if(pei->infobits&PEIF_SELECTED){
     if((psi->psio->editsidetype&PLT_DOOMQUEUE) && (psi->lastentry<=psi->firstentry)) // !!!
      break;
     if(pei->entrytype!=DFT_SUBDIR){
      playlist_editlist_delfile_one(psi,pei,EDITLIST_MODE_ALL);
      funcbit_disable(pei->infobits,PEIF_SELECTED);
      delcount++;

      if(!(delcount&31)){
       if(pds_kbhit())
        if(pds_extgetch()==KEY_ESC)
         break;
       sprintf(sout,"\nRemoving entries: %3d/%d\n",delcount,delfilenum);
       tw=display_textwin_openwindow_message(tw,NULL,sout);
      }
     }
    }
    pei--;
   }while(pei>=psi->firstentry);
   if(delcount>=psi->selected_files)
    psi->selected_files=0;
   else
    psi->selected_files-=delcount;
   display_textwin_closewindow_message(tw);
   refdisp|=RDT_RESET_EDIT;
  }
 }else
  playlist_editlist_delete_entry_manual(psi,psi->editorhighline);

 refdisp|=RDT_EDITOR;
 if(psi==psi->mvp->psip)
  refdisp|=RDT_BROWSER;
}

void playlist_editlist_group_invert_selection(struct playlist_side_info *psi)
{
 struct playlist_entry_info *pei;
 unsigned int allfiles=psi->lastentry-psi->firstentry+1;
 char sout[48];

 if(psi->selected_files<=allfiles)
  psi->selected_files=allfiles-psi->selected_files;
 else
  psi->selected_files=0;

 for(pei=psi->firstentry;pei<=psi->lastentry;pei++){
  if((pei->entrytype&DFTM_DFT) && ((pei->entrytype&DFTM_DRIVE) || (pei->entrytype&DFTM_DIR) || (pei->entrytype==DFT_UPLIST)))
   continue;
  funcbit_inverse(pei->infobits,PEIF_SELECTED);
 }

 sprintf(sout,"Selected %d file%c",psi->selected_files,((psi->selected_files>1)? 's':' '));
 display_timed_message(sout);
}

void playlist_editlist_group_select_all(struct playlist_side_info *psi)
{
 struct playlist_entry_info *pei;
 unsigned int selected=0;
 char sout[48];

 for(pei=psi->firstentry;pei<=psi->lastentry;pei++){
  if((pei->entrytype&DFTM_DFT) && ((pei->entrytype&DFTM_DRIVE) || (pei->entrytype&DFTM_DIR) || (pei->entrytype==DFT_UPLIST)))
   continue;
  funcbit_enable(pei->infobits,PEIF_SELECTED);
  selected++;
 }

 psi->selected_files=selected;

 sprintf(sout,"Selected all (%d) file%c",selected,((selected>1)? 's':' '));
 display_timed_message(sout);
}

void playlist_editlist_group_unselect_all(struct playlist_side_info *psi)
{
 struct playlist_entry_info *pei;

 for(pei=psi->firstentry;pei<=psi->lastentry;pei++)
  funcbit_disable(pei->infobits,PEIF_SELECTED);

 psi->selected_files=0;

 display_timed_message("Unselected all files");
}

//------------------------------------------------------------------------
#define METASELECT_WINSIZE 30

static unsigned int selecthand_unselect;
static char selectstring[METASELECT_WINSIZE+20];

static void editlist_groupselect_metadata(struct playlist_side_info *psi)
{
 struct playlist_entry_info *pei;
 unsigned int ts_len,as_len,selected;
 char *s_titlep,temps[sizeof(selectstring)],sout[50];

 pds_strcpy(temps,selectstring);
 s_titlep=pds_strchr(temps,':');
 if(s_titlep){
  *s_titlep++=0;
  ts_len=pds_strlen(s_titlep);
 }else
  ts_len=0;
 as_len=pds_strlen(temps);

 selected=0;
 pei=psi->firstentry;
 do{
  unsigned int found=0;
  if((pei->entrytype&DFTM_DRIVE) || (pei->entrytype==DFT_UPDIR) || (pei->entrytype==DFT_UPLIST))
   continue;
  if(!s_titlep){
   unsigned int i;
   for(i=0;i<=I3I_MAX;i++){
    if(pei->id3info[i] && pds_strstri(pei->id3info[i],temps)){
     found=1;
     break;
    }
   }
   if(!found){
    if(pds_strstri(pei->filename,temps))
     found=1;
   }
  }else{
   char *ipa,*ipt;
   if(GET_HFT(pei->entrytype)==HFT_DFT || (!pei->id3info[I3I_ARTIST] && !pei->id3info[I3I_TITLE])){
    ipa=ipt=pds_getfilename_from_fullname(pei->filename);
   }else{
    ipa=pei->id3info[I3I_ARTIST];
    ipt=pei->id3info[I3I_TITLE];
    if(!ipa)
     ipa=ipt;
    if(!ipt)
     ipt=ipa;
   }
   if(!as_len || pds_strstri(ipa,temps)){ // title search only || artist found
    if(!ts_len || pds_strstri(ipt,s_titlep)) // artist search only || title found
     found=1;
   }
  }
  if(found){
   if(selecthand_unselect){
    if(pei->infobits&PEIF_SELECTED){
     funcbit_disable(pei->infobits,PEIF_SELECTED);
     psi->selected_files--;
     selected++;
    }
   }else{
    if(!(pei->infobits&PEIF_SELECTED)){
     funcbit_enable(pei->infobits,PEIF_SELECTED);
     psi->selected_files++;
    }
    selected++;
   }
  }
 }while(++pei<=psi->lastentry);
 sprintf(sout,"%s %d file%c",((selecthand_unselect)? "Unselected":"Selected"),selected,((selected>1)? 's':' '));
 display_timed_message(sout);
 //display_textwin_openwindow_timedmessage(sout);
 if(selected)
  refdisp|=RDT_EDITOR;
}

void playlist_editlist_groupselect_open(struct playlist_side_info *psi,unsigned int unselect)
{
 void *tw;
 selecthand_unselect=unselect;
 tw=display_textwin_allocwindow_items(TEXTWIN_FLAG_MSGCENTERALIGN|TEXTWIN_FLAG_CONFIRM|TEXTWIN_FLAG_NOWINMINSIZE,((unselect)? " Unselect files by metadata":" Select files by metadata"),editlist_groupselect_metadata,psi);
 display_textwin_additem_editline(tw,TEXTWIN_FLAG_MSGCENTERALIGN,0,-1,METASELECT_WINSIZE,&selectstring[0],sizeof(selectstring)-1);
 display_textwin_openwindow_items(tw,0,0,0);
}

//--------------------------------------------------------------------------
void playlist_editlist_id3filter(struct mainvars *mvp)
{
 struct playlist_side_info *psi=mvp->psip;
 struct playlist_entry_info *pei;
 unsigned int i,found,countf,counti,allfilenums;
 char sout[64];

 if(id3filterkeyword && (psi->editsidetype&PLT_ENABLED)){
  counti=countf=0;
  allfilenums=psi->lastentry-psi->firstsong+1;
  for(pei=psi->lastentry;pei>=psi->firstsong;pei--){
   found=0;
   if(pei->infobits&PEIF_ID3EXIST){
    for(i=0;i<=I3I_MAX;i++){
     if(pei->id3info[i]){
      if(pds_strstri(pei->id3info[i],id3filterkeyword)){
       found=1;
       break;
      }
     }
    }
   }else{
    if(pds_strstri(pei->filename,id3filterkeyword))
     found=1;
   }

   if(!found)
    playlist_editlist_delfile_one(psi,pei,EDITLIST_MODE_ALL);
   else
    countf++;

   counti++;
   if(!(counti&31)){
    sprintf(sout,"Filtering: %4d/%d, Found: %d",counti,allfilenums,countf);
    display_message(0,0,sout);
    if(pds_look_extgetch()==KEY_ESC){
     pds_extgetch();
     break;
    }
   }
  }
  clear_message();
 }
}

//------------------------------------------------------------------------
void playlist_editlist_insert_index(struct mainvars *mvp)
{
 struct playlist_side_info *psi=mvp->psip;
 struct playlist_entry_info *pei;
 struct frame *frp;
 struct mpxplay_infile_info_s *miis;
 if(!mvp->aktfilenum || (mvp->aktfilenum<psi->firstsong) || (mvp->aktfilenum>psi->lastentry))
  return;
 if(!playlist_editlist_addfile_one(psi,psi,mvp->aktfilenum,NULL,EDITLIST_MODE_ALL))
  return;
 pei=psi->lastentry;
 while(pei>(mvp->aktfilenum+1)){
  playlist_swap_entries(psi,pei,pei-1);
  pei--;
 }
 playlist_fulltime_del(psi,pei);
 frp=mvp->frp0;
 miis=frp->infile_infos;
 pei->pstime=(long)((float)frp->frameNum*(float)miis->timemsec/(float)frp->allframes);
 funcbit_enable(pei->infobits,PEIF_INDEXED);
 mvp->aktfilenum->petime=pei->pstime;
 funcbit_enable(mvp->aktfilenum->infobits,PEIF_INDEXED);
 mvp->aktfilenum=pei;
 playlist_pei0_set(mvp,pei);
 mpxplay_calculate_index_start_end(frp,mvp,mvp->pei0);
 playlist_editorhighline_set(psi,pei);
}

void playlist_editlist_delete_index(struct mainvars *mvp)
{
 struct playlist_side_info *psi=mvp->psie;
 struct playlist_entry_info *pei0=psi->editorhighline,*pei1,*aktfi=NULL;
 if(!(pei0->infobits&PEIF_INDEXED))
  return;
 pei1=pei0+1;
 if(pei0->petime && (pei0->petime==pei1->pstime) && (pds_strcmp(pei0->filename,pei1->filename)==0)){
  pei1->pstime=pei0->pstime;
  pei0->pstime=pei0->petime;
  if((pei0==mvp->aktfilenum) || (pei1==mvp->aktfilenum))
   aktfi=pei0;
  playlist_editlist_delfile_one(psi,pei0,EDITLIST_MODE_ALL);
 }else{
  pei1=pei0-1;
  if(pei0->pstime && (pei0->pstime==pei1->petime) && (pds_strcmp(pei0->filename,pei1->filename)==0)){
   pei1->petime=pei0->petime;
   pei0->petime=pei0->pstime;
   if((pei0==mvp->aktfilenum) || (pei1==mvp->aktfilenum))
    aktfi=pei1;
   playlist_editlist_delfile_one(psi,pei0,EDITLIST_MODE_ALL);
   playlist_editorhighline_set(psi,pei1);
  }
 }
 if(!pei1->pstime && !pei1->petime)
  funcbit_disable(pei1->infobits,PEIF_INDEXED);
 if(aktfi){
  mvp->newfilenum=NULL;
  mvp->aktfilenum=aktfi;
  playlist_pei0_set(mvp,aktfi);
  mpxplay_calculate_index_start_end(mvp->frp0,mvp,mvp->pei0);
 }
}
