/* deletecore.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2013-2023 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 "deletecore.hh"
#include "worker_locale.h"
#include "deleteop.h"
#include "nmcopyopdir.hh"
#include "deleteorder.hh"
#include "simplelist.hh"
#include "verzeichnis.hh"
#include "aguix/lowlevelfunc.h"
#include "aguix/util.h"
#include "worker.h"

DeleteCore::DeleteCore( struct deleteorder *delorder ) :
    m_delorder( delorder ),
    m_cancel( false )
{
}

DeleteCore::~DeleteCore()
{
}

int DeleteCore::buildDeleteDatabase()
{
    unsigned long files = 0,
        dirs = 0,
        gf = 0,
        gd = 0;

    DeleteOpWin *dowin = m_delorder->dowin;

    if ( dowin->redraw() != 0 ) m_cancel = true;

    // create the NM_CopyOp_Dir for each dir in dellist
  
    for ( auto &cbe : m_del_list ) {
        if ( m_cancel == true ) break;

        bool enter = false;

        if ( cbe.entry().isDir() == true ) {
            if ( cbe.entry().isLink == false ) enter = true;
        }

        if ( enter == true ) {
            // fe is a dir so creating corresponding entry
            NM_CopyOp_Dir *cod1 = new NM_CopyOp_Dir( &cbe.entry() );

            if ( cod1->user_abort == false ) {
                // recursive call
                if ( cod1->createSubDirs( m_delorder, &gf, &gd ) != 0 ) {
                    m_cancel = true;
                }
            } else {
                m_cancel = true;
            }

            // add the values from this subdir to this dir
            files += cod1->files;
            dirs += cod1->dirs;

            cbe.m_cod = cod1;
            // this is a dir so inc the counter
            dirs++;
            gd++;

            dowin->set_files_to_delete( gf );
            dowin->set_dirs_to_delete( gd );
            if ( dowin->redraw() != 0 ) m_cancel = true;
        } else {
            // is not dir (mostly a file but can also be links ...)
            files++;
            gf++;
            cbe.m_cod = NULL;
        }
    }

    dowin->set_files_to_delete( files );
    dowin->set_dirs_to_delete( dirs );

    return 0;
}

int DeleteCore::executeDelete()
{
    DeleteOpWin *dowin = m_delorder->dowin;

    for ( auto &cbe : m_del_list ) {
        if ( m_cancel ) break;

        NM_CopyOp_Dir *tcod;

        tcod = cbe.m_cod;

        if ( m_pre_cb ) {
            m_pre_cb( cbe.entry(), cbe.m_row, cbe.m_cod );
        }

        nm_delete_t ce1 = NM_DELETE_CANCEL;

        if ( tcod != NULL ) {
            // determine if the dir is empty or not
            bool empty;
            bool skip;

            if ( ( tcod->files + tcod->dirs ) > 0 ) empty = false;
            else empty = true;

            skip = false;

            // skip if the user doesn't want to delete non-empty dirs
            if ( ( m_delorder->dirdel == m_delorder->NM_DIRDELETE_NONE ) && ( empty == false ) ) skip = true;

            // if the user doesn't select ALL or NONE and the the dir isn't empty show a request
            if ( ( m_delorder->dirdel == m_delorder->NM_DIRDELETE_NORMAL ) && ( empty == false ) ) {
                char *buttonstr, *textstr;
                int erg;

                textstr = (char*)_allocsafe( strlen( catalog.getLocale( 146 ) ) +
                                             strlen( cbe.entry().fullname ) + 1 );
                sprintf( textstr, catalog.getLocale( 146 ), cbe.entry().fullname );
                buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 11 ) ) + 1 +
                                               strlen( catalog.getLocale( 287 ) ) + 1 +
                                               strlen( catalog.getLocale( 288 ) ) + 1 +
                                               strlen( catalog.getLocale( 225 ) ) + 1 +
                                               strlen( catalog.getLocale( 8 ) ) + 1 );
                sprintf( buttonstr, "%s|%s|%s|%s|%s", catalog.getLocale( 11 ),
                         catalog.getLocale( 287 ),
                         catalog.getLocale( 288 ),
                         catalog.getLocale( 225 ),
                         catalog.getLocale( 8 ) );
                erg = dowin->request( catalog.getLocale( 123 ), textstr, buttonstr );
                _freesafe( buttonstr );
                _freesafe( textstr );
                switch ( erg ) {
                    case 1:
                        m_delorder->dirdel = m_delorder->NM_DIRDELETE_ALL;
                        break;
                    case 2:
                        m_delorder->dirdel = m_delorder->NM_DIRDELETE_NONE;
                        skip = true;
                        break;
                    case 3:
                        skip = true;
                        break;
                    case 4:
                        m_cancel = true;
                        skip = true;
                        break;
                }
            }
            if ( skip == false ) {
                ce1 = deldir( &cbe.entry(), tcod, m_delorder );
                if ( ce1 == DeleteCore::NM_DELETE_CANCEL ) {
                    m_cancel = true;
                } else if ( ce1 == DeleteCore::NM_DELETE_OK ) {
                    Worker::pushCommandLog( catalog.getLocale( 1422 ),
                                            cbe.entry().fullname,
                                            "",
                                            cbe.entry().fullname );
                }
            } else {

                // dir skiped so dec DeleteOpWin counter by the files/dirs skiped
                dowin->dec_file_counter( tcod->files );
                dowin->dec_dir_counter( tcod->dirs );

                ce1 = NM_DELETE_SKIP;
            }

            if ( m_post_cb ) {
                m_post_cb( cbe.entry(), cbe.m_row,
                           ce1, cbe.m_cod );
            }
        } else {
            if ( dowin != NULL ) dowin->setfilename( cbe.entry().fullname );
            ce1 = delfile( &cbe.entry(), m_delorder, false );
            if ( ce1 == DeleteCore::NM_DELETE_CANCEL ) {
                m_cancel = true;
            } else if ( ce1 == DeleteCore::NM_DELETE_OK ) {
                Worker::pushCommandLog( catalog.getLocale( 1422 ),
                                        cbe.entry().fullname,
                                        "",
                                        cbe.entry().fullname );
            }

            if ( m_post_cb ) {
                m_post_cb( cbe.entry(), cbe.m_row,
                           ce1, NULL );
            }
        }

        if ( dowin->redraw() != 0 ) m_cancel = true;
    }

    return 0;
}

DeleteCore::nm_delete_t DeleteCore::deldir( const FileEntry *fe, NM_CopyOp_Dir *cod, struct deleteorder *delorder )
{
    NM_CopyOp_Dir *cod1 = NULL;
    int id1;
    bool skip;
    nm_delete_t ce1;
    DeleteOpWin *dowin = delorder->dowin;
    bool delerror;

    skip = false;

    if ( dowin != NULL ) dowin->setfilename( fe->fullname );

    // first del all subdirs
    // then del all files 
    id1 = cod->subdirs->initEnum();
    cod1 = (NM_CopyOp_Dir*)cod->subdirs->getFirstElement( id1 );
    while ( cod1 != NULL ) {
        ce1 = deldir( cod1->fileentry, cod1, delorder );
        if ( ce1 == NM_DELETE_OK ) {
            // success
            cod->error_counter += cod1->error_counter;
        } else {
            cod->error_counter += cod1->error_counter + 1;
            if ( ce1 == NM_DELETE_CANCEL ) {
                setCancel( true );
                break;
            }
        }
        cod1 = (NM_CopyOp_Dir*)cod->subdirs->getNextElement( id1 );
    }
    cod->subdirs->closeEnum( id1 );

    // next only if read this dir correctly
    if ( ( cod->ok == true ) && ( getCancel() == false ) ) {
        for ( Verzeichnis::verz_it subfe_it1 = cod->verz->begin();
              subfe_it1 != cod->verz->end();
              subfe_it1++ ) {
            FileEntry *subfe = *subfe_it1;
            if ( strcmp( subfe->name, ".." ) != 0 ) {
                bool isdir = false;
                if ( subfe->isDir() == true ) {
                    if ( subfe->isLink == false ) isdir = true;
                }
                if ( isdir == false ) {
                    ce1 = delfile( subfe, delorder, false );
                    if ( ce1 == NM_DELETE_OK ) {
                        // success
                    } else {
                        cod->error_counter++;
                        if ( ce1 == NM_DELETE_CANCEL ) {
                            setCancel( true );
                            break;
                        }
                    }
                }
            }
        }
    }

    delerror = true;
    if ( getCancel() == false ) {
        if ( cod->error_counter == 0 ) {
            ce1 = delfile( fe, delorder, true );
            if ( ce1 == NM_DELETE_OK ) {
                // success
                delerror = false;
            } else {
                cod->error_counter++;
                if ( ce1 == NM_DELETE_CANCEL ) {
                    setCancel( true );
                }
            }
        }
    }
    if ( delerror == true ) {
        char *textstr = (char*)_allocsafe( strlen( catalog.getLocale( 289 ) ) +
                                           strlen( fe->fullname ) + 1 );
        sprintf( textstr, catalog.getLocale( 289 ), fe->fullname );
        char *buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 11 ) ) + 1 +
                                             strlen( catalog.getLocale( 8 ) ) + 1 );
        sprintf( buttonstr, "%s|%s", catalog.getLocale( 11 ),
                 catalog.getLocale( 8 ) );
        int erg = dowin->request( catalog.getLocale( 125 ), textstr, buttonstr );
        _freesafe( buttonstr );
        _freesafe( textstr );
        if ( erg == 1 ) setCancel( true );
        else skip = true;
    }
    if ( getCancel() == true ) return NM_DELETE_CANCEL;
    else if ( skip == true ) {
        cod->error_counter++;
        return NM_DELETE_SKIP;
    }
    return NM_DELETE_OK;
}

DeleteCore::nm_delete_t DeleteCore::delfile( const FileEntry *fe, struct deleteorder *delorder, bool dir )
{
    bool skip;
    int erg;
    DeleteOpWin *dowin = delorder->dowin;

    skip = false;

    if ( dir == true ) erg = worker_rmdir( fe->fullname );
    else erg = worker_unlink( fe->fullname );

    if ( erg != 0 ) {
        //    textstr="Failed to remove this file!";
        //    buttonstr="Ok|Cancel";

        char *textstr, *buttonstr;

        if ( dir == true ) {
            textstr = (char*)_allocsafe( strlen( catalog.getLocale( 147 ) ) + 
                                         strlen( fe->fullname ) + 1 );
            sprintf( textstr, catalog.getLocale( 147 ), fe->fullname );
        } else { 
            textstr = (char*)_allocsafe( strlen( catalog.getLocale( 286 ) ) +
                                         strlen( fe->fullname ) + 1 );
            sprintf( textstr, catalog.getLocale( 286 ), fe->fullname );
        }
        buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 11 ) ) + 1 +
                                       strlen( catalog.getLocale( 8 ) ) + 1 );
        sprintf( buttonstr, "%s|%s", catalog.getLocale( 11 ),
                 catalog.getLocale( 8 ) );
        erg = dowin->request( catalog.getLocale( 347 ), textstr, buttonstr );
        _freesafe( buttonstr );
        _freesafe( textstr );

        if ( erg == 1 ) setCancel( true );
        else skip = true;
    }
    if ( dowin != NULL ) {
        if ( dir == true ) {
            dowin->dir_finished();
            if ( dowin->redraw() != 0 ) setCancel( true );
        } else dowin->file_finished();
    }
    if ( getCancel() == true ) return NM_DELETE_CANCEL;
    else if ( skip == true ) return NM_DELETE_SKIP;
    return NM_DELETE_OK;
}

void DeleteCore::setPreDeleteCallback( std::function< void( const FileEntry &fe, int row, const NM_CopyOp_Dir *cod ) > cb )
{
    m_pre_cb = cb;
}

void DeleteCore::setPostDeleteCallback( std::function< void( const FileEntry &fe, int row,
                                                             nm_delete_t res, const NM_CopyOp_Dir *cod ) > cb )
{
    m_post_cb = cb;
}

void DeleteCore::registerFEToDelete( const FileEntry &fe,
                                     int row )
{
    m_del_list.push_back( delete_base_entry( fe, row ) );
}

bool DeleteCore::getCancel() const
{
    return m_cancel;
}

void DeleteCore::setCancel( bool nv )
{
    // disallow clearing the flag
    if ( nv == true ) {
        m_cancel = nv;
    }
}

DeleteCore::delete_base_entry::delete_base_entry( const FileEntry &fe,
                                                  int row ) :
    m_row( row ),
    m_cod( NULL ),
    m_fe( fe )
{
}

DeleteCore::delete_base_entry::~delete_base_entry()
{
    delete m_cod;
}
