/* virtualdirmode_delete.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2013-2019 Ralf Hoffmann.
 * You can contact me at: ralf@boomerangsworld.de
 *   or http://www.boomerangsworld.de/worker
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "virtualdirmode.hh"
#include "aguix/fieldlistviewdnd.h"
#include "worker_locale.h"
#include "worker.h"
#include "nwcentryselectionstate.hh"
#include "datei.h"
#include "deleteop.h"
#include "deleteorder.hh"
#include "deletecore.hh"
#include "nmcopyopdir.hh"

void VirtualDirMode::deletef( struct deleteorder *delorder )
{
    long int tnrfiles, tnrdirs;
    int erg;
    bool cancel = false;
    DeleteOpWin *dowin;
    char *textstr, *buttonstr;
    std::list<const FileEntry*>::iterator itfe1;
  
    if ( delorder == NULL ) return;

    if ( delorder->dowin == NULL ) return;

    if ( ! ce.get() ) return;

    tnrfiles = ce->getNrOfFiles( 1 );
    tnrdirs = ce->getNrOfDirs( 1 );

    long int invisible_entries = getNrOfInvisibleEntries();

    if ( ( delorder->source == delorder->NM_ALLENTRIES ) &&
         ( ( tnrfiles + tnrdirs ) < 1 ) ) {
        auto es = ce->getEntry( ce->getActiveEntryPos() );

        if ( es ) {
            auto fse = es->getNWCEntry();

            if ( fse ) {

                if ( fse->getBasename() != ".." ) {
                    if ( fse->isDir( false ) ) tnrdirs++;
                    else tnrfiles++;

                    if ( isPosVisible( ce->getActiveEntryPos() ) == false ) {
                        invisible_entries++;
                    }
                }
            }
        }
    }
  
    if ( ( tnrfiles + tnrdirs ) < 1 )
        return;

    finishsearchmode();
  
    dowin = delorder->dowin;

    buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 11 ) ) + 1 +
                                   strlen( catalog.getLocale( 8 ) ) + 1 );
    sprintf( buttonstr, "%s|%s", catalog.getLocale( 11 ),
             catalog.getLocale( 8 ) );

    if ( invisible_entries > 0 ) {
        std::string s = AGUIXUtils::formatStringToString( catalog.getLocale( 1119 ),
                                                          tnrfiles, tnrdirs, invisible_entries );
        textstr = dupstring( s.c_str() );
    } else {
        textstr = (char*)_allocsafe( strlen( catalog.getLocale( 145 ) ) + A_BYTESFORNUMBER( tnrfiles ) +
                                     A_BYTESFORNUMBER( tnrdirs ) + 1 );
        sprintf( textstr, catalog.getLocale( 145 ), tnrfiles, tnrdirs );
    }

    erg = dowin->request( catalog.getLocale( 123 ), textstr, buttonstr );
    _freesafe( buttonstr );
    _freesafe( textstr );

    if ( erg != 0 ) return;

    disableDirWatch();
  
    // clear the reclist so the slace doesn't block us because he is still reading
    // from disk
    ft_request_list_clear();

    dowin->open();
    dowin->setmessage( catalog.getLocale( 140 ) );
    if ( dowin->redraw() != 0 ) cancel = true;

    DeleteCore *dc = new DeleteCore( delorder );
    int row_adjustment = 0;

    if ( cancel ) dc->setCancel( cancel );

    {
        std::list< NM_specialsourceExt > dellist;

        switch ( delorder->source ) {
            case deleteorder::NM_SPECIAL:
                break;
            case deleteorder::NM_ONLYACTIVE:
                break;
            case deleteorder::NM_ONLYSELECTED:
                getSelFiles( dellist, LM_GETFILES_ONLYSELECT, false );
                break;
            default:  // all selected entries
                getSelFiles( dellist, LM_GETFILES_SELORACT, false );
                break;
        }
        for ( auto &it : dellist ) {
            if ( it.entry() ) {
                dc->registerFEToDelete( *it.entry(), it.getID() );
            }
        }
    }

    if (dc->buildDeleteDatabase() != 0) dc->setCancel( true );

    dowin->redraw();

    dc->setPreDeleteCallback( [this,
                               &row_adjustment]( const FileEntry &fe, int id, const NM_CopyOp_Dir *cod )
                              {
                                  int row = getRowForCEPos( id, true );
                                  int actual_row = row - row_adjustment;
                                  if ( actual_row >= 0 ) {
                                      m_lv->setVisMark( actual_row, true );
                                      m_lv->showRow( actual_row, false );
                                      m_lv->timeLimitedRedraw();
                                  }
                              } );

    std::vector< int > ids_to_remove;

    dc->setPostDeleteCallback( [this,
                                &delorder,
                                &dc,
                                &row_adjustment,
                                &ids_to_remove]( const FileEntry &fe, int id,
                                                 DeleteCore::nm_delete_t res, const NM_CopyOp_Dir *cod )
                               {
                                   int row = getRowForCEPos( id, true );

                                   int actual_row = row - row_adjustment;

                                   auto es = ce->getEntry( id );

                                   if ( es ) {

                                       auto fse = es->getNWCEntry();

                                       m_lv->setVisMark( actual_row, false );

                                       if ( fse && fse->getFullname() == fe.fullname ) {

                                           if ( res == DeleteCore::NM_DELETE_OK &&
                                                ( cod == NULL || cod->error_counter == 0 ) ) {
                                               // success so remove entry
                                               ce->temporarilyHideEntry( id );

                                               if ( ! ce->isRealDir() ) {
                                                   ids_to_remove.push_back( id );
                                               }

                                               m_lv->deleteRow( actual_row );
                                               m_lv->timeLimitedRedraw();

                                               showCacheState();

                                               row_adjustment++;

                                               if ( ce->isRealDir() ) {
                                                   ce->updateActiveForRemoval( id );
                                               }
                                           } else if ( res == DeleteCore::NM_DELETE_SKIP ) {
                                           } else if ( res == DeleteCore::NM_DELETE_CANCEL ) {
                                               dc->setCancel( true );
                                           }
                                       }
                                   }
                               } );

    if ( ! dc->getCancel() ) {
        dc->executeDelete();
    }

    delete dc;
    dc = NULL;

    dowin->close();
  
    if ( !ce->isRealDir() ) {
        ce->beginModification( DMCacheEntryNWC::MOD_REMOVAL );

        for ( auto id : ids_to_remove ) {
            // entry is removed from NWC::Dir, not from DMCacheEntryNWC
            // this will be done at end
            ce->removeEntry( id );
        }

        ce->endModification();
    } else {
        ce->reload();
    }

    // trigger all the refresh stuff
    setCurrentCE( ce );

    m_lv->showActive();
    aguix->Flush();

    enableDirWatch();
}
