/* copycore.hh
 * 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
 */

#ifndef COPYCORE_HH
#define COPYCORE_HH

#include "wdefines.h"
#include <list>
#include <functional>
#include <string>
#include "fileentry.hh"
#include <thread>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include "nwc_os_makedirs.hh"

class CopyOpWin;
struct copyorder;
class FileEntry;
class NM_CopyOp_Dir;

class CopyCore
{
public:
    CopyCore( std::shared_ptr< struct copyorder > co );
    ~CopyCore();

    int deleteCopiedButNotMoved( std::function< void( const std::pair< std::string, bool > & ) > processed_callback );

    void setCancel( bool nv );
    bool getCancel() const;

    void registerFEToCopy( const FileEntry &fe,
                           int row,
                           const std::vector< NWC::OS::deep_dir_info > &file_base_segments );

    int buildCopyDatabase();

    int executeCopy();

    typedef enum { NM_COPY_OK,
                   NM_COPY_SKIP,
                   NM_COPY_CANCEL,
                   NM_COPY_NEED_DELETE } nm_copy_t;

    void setPreCopyCallback( std::function< void( loff_t size, int row, bool is_dir ) > cb );
    void setPostCopyCallback( std::function< void( const std::string &source_fullname, int row,
                                                   nm_copy_t res,
                                                   bool is_dir, unsigned long dir_error_counter,
                                                   const std::string &target_fullname ) > cb );

    loff_t getBytesToCopy() const;

    std::shared_ptr< struct copyorder > getCopyOrder();

    bool finished();

    void join();

    void setAskToCancel();

    bool timed_wait_for_finished();

    const std::vector< std::string > &incomplete_entries() const;
private:
    typedef enum { REG_COPY_OK, REG_COPY_SKIP, REG_COPY_CANCEL, REG_COPY_ERROR } reg_copy_erg_t;

    typedef enum { NM_NEWNAME_OK,
                   NM_NEWNAME_SKIP,
                   NM_NEWNAME_CANCEL,
                   NM_NEWNAME_OVERWRITE,
                   NM_NEWNAME_USE } nm_newname_t;
  
    nm_newname_t getNewName4File( const FileEntry *oldfe, const char *dest,
                                  char **newname_return, bool acceptrename );
    nm_newname_t getNewName4Dir( const FileEntry *oldfe, const char *dest,
                                 char **newname_return, bool acceptrename );
    nm_copy_t copydir( const FileEntry *fe,
                       NM_CopyOp_Dir *cod,
                       bool acceptrename,
                       const char *destdir,
                       std::string *effective_target_fullname,
                       bool skip_ok_test );
    nm_copy_t copyfile( const FileEntry *fe,
                        bool acceptrename,
                        const char *destdir,
                        std::string *effective_target_fullname );

    reg_copy_erg_t copyfile_reg( const char *sourcename,
                                 const char *destname,
                                 const char *destdirname,
                                 const char *destbasename,
                                 mode_t create_mode,
                                 CopyOpWin *cowin,
                                 loff_t bytesToCopy );

    int tryApplyOwnerPerm( const char *filename,
                           const uid_t wanted_user,
                           const gid_t wanted_group,
                           const mode_t wanted_mode );

    void pushForPostDelete( const FileEntry *fe );

    int executeCopyThread();

    void checkAskToCancel();

    void setFinished();

#define BUFSIZE 128*1024
    void *buf;

    std::shared_ptr< struct copyorder > m_copyorder;

    std::atomic< bool > m_cancel;

    std::list< std::pair< std::string, bool > > m_copy_deletefe_list;

    class copy_base_entry {
    public:
        copy_base_entry( const FileEntry &tfe, int row,
                         const std::vector< NWC::OS::deep_dir_info > &file_base_segments );
        ~copy_base_entry();
        const FileEntry &entry() const
        {
            return m_fe;
        }

        std::vector< NWC::OS::deep_dir_info > &get_file_base_segments()
        {
            return m_file_base_segments;
        }
        
        int m_row;  // corresponding row or -1
        NM_CopyOp_Dir *m_cod; // when fe is dir, this will be the corresponding structure
    private:
        FileEntry m_fe;  // element to copy
        std::vector< NWC::OS::deep_dir_info > m_file_base_segments;
    };

    int prepare_destination_dir( copy_base_entry &cbe,
                                 std::string &destdir,
                                 bool post_run );

    std::list< copy_base_entry > m_copy_list;

    std::function< void( loff_t size, int row, bool is_dir ) > m_pre_cb;
    std::function< void( const std::string &source_fullname, int row,
                         nm_copy_t res,
                         bool is_dir, unsigned long dir_error_counter,
                         const std::string &target_fullname ) > m_post_cb;

    loff_t m_bytes_to_copy;

    std::mutex m_finished_mutex;
    std::condition_variable m_finished_cond;
    std::atomic< bool> m_finished;

    std::atomic< bool> m_ask_to_cancel;

    std::thread m_copy_thread;

    std::vector< std::string > m_incomplete_entries;
};

#endif
