/* worker.h
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2001-2025 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 WORKER_H
#define WORKER_H

#include "wdefines.h"
#include "functionproto.h"
#include <string>
#include "generic_callback.hh"
#include "layoutsettings.hh"
#include "timedtext.hh"
#include "processhandler.hh"
#include <map>
#include "aguix/message.h"
#include "hw_volume_manager.hh"
#include <memory>
#include "event_callbacks.hh"
#include "temporaryfilecache.hh"
#include "deeppathstore.hh"
#include <set>
#include "kvpstore.hh"
#include <future>
#include "worker_types.h"
#include "existence_test.hh"
#include "wconfig_initial_command.hh"
#include "file_command_log.hh"
#include "flagreplacer.hh"

class ListerMode;
class Lister;
class BookmarkDBProxy;
class PopUpMenu;
class HWVolume;
class WaitThread;
class TimeoutStore;
class EventQueue;
class Requester;
class Button;
class AWindow;
class AContainer;
class WCHotkey;
class WCButton;
class WCPath;
class PartSpace;
class CopyfileProgressCallback;
class PersDeepPathStore;
class HintDB;
class PathnameWatcher;
class Slider;
class MenuTreeUI;
class MenuTreeNode;
class WConfig;
class PersistentKVP;
class PrefixDB;
class FilterStash;

class worker_version
{
public:
    worker_version() : m_major( 0 ),
                       m_minor( 0 ),
                       m_patch( 0 )
    {}

    worker_version( int major ,
                    int minor,
                    int patch ) : m_major( major ),
                                  m_minor( minor ),
                                  m_patch( patch )
    {}

    bool operator<( const worker_version &rhs ) const
    {
        if ( m_major < rhs.m_major ) return true;

        if ( m_major > rhs.m_major ) return false;

        if ( m_minor < rhs.m_minor ) return true;

        if ( m_minor > rhs.m_minor ) return false;

        if ( m_patch < rhs.m_patch ) return true;

        return false;
    }

    bool operator<=( const worker_version &rhs ) const
    {
        return ! ( rhs < *this );
    }

    bool operator>( const worker_version &rhs ) const
    {
        return rhs < *this;
    }

    int m_major;
    int m_minor;
    int m_patch;
};

class Worker
{
public:
  Worker( int argc, char **argv, int optind = -1 );
  ~Worker();
  Worker( const Worker &other );
  Worker &operator=( const Worker &other );

  void run();
  static AGUIX *getAGUIX();
  static int getMaxModeNr();
  static int getID4Mode(ListerMode*);
  static const char *getNameOfMode( int nr );
  static const char *getLocaleNameOfMode( int nr );
  static int getID4Name( const char *str );
  ListerMode *getMode4ID(int,Lister *);
  AWindow *getMainWin();
  void setStatebarText(const char*);
  static int getNrOfCommands();
  static std::shared_ptr< FunctionProto > getCommand4ID(int);
  static int getID4Command(FunctionProto*);
  Lister *getOtherLister(Lister*);
  int getSide(Lister*);
  Lister *getLister(int);
  Lister *getActiveLister();
  int interpret( const command_list_t &,ActionMessage *);
  AWindow *getListerWin( Lister *l );
  
  int quit(int mode);
  void checkfirststart( char **setlang );
  void setTitle(const char *add_infos);
  
  static Requester *getRequester();
  void activateShortkeyFromList();

  int PS_readSpace( const char*name );
  
  loff_t PS_getBlocksize();
  loff_t PS_getFreeSpace();
  loff_t PS_getSpace();
  
  std::string PS_getFreeSpaceH() const;
  std::string PS_getSpaceH() const;
  void PS_setLifetime( double t );
  void setWaitCursor();
  void unsetWaitCursor();
  static const std::string getDataDir();
  
  int runCommand( const char *exestr,
		  const char *tmpname,
		  const char *tmpoutput,
		  bool inbackground,
                  RefCount< GenericCallbackArg< void, int > > pea );

  static std::string getWorkerConfigDir();

  void openWorkerMenu();
    void handleHelp();

  int getStatebarWidth();

  static BookmarkDBProxy &getBookmarkDBInstance();

  void registerPopUpCallback( RefCount<GenericCallbackArg<void, AGMessage*> > cb,
                              const PopUpMenu *menu );
  void unregisterPopUpCallback( RefCount<GenericCallbackArg<void, AGMessage*> > cb );

  LayoutSettings getCustomLayout() const;
  bool getUseCustomLayout() const;
  void setCustomLayout( const LayoutSettings &nv );
  void unsetCustomLayout();

    enum state_bar_text_update_type {
        UPDATE_ON_TEXT_CHANGE,  ///< same time, different text
        UPDATE_ON_TIME_CHANGE   ///< different time, same text
    };
    
    void pushStatebarText( const TimedText &text );
    void updateStatebarText( const TimedText &text,
                             state_bar_text_update_type update_type = UPDATE_ON_TEXT_CHANGE );
    void removeStatebarText( const TimedText &text );

    HWVolumeManager &getVolumeManager();
    void clearNewVolumesInfo();

    void registerTimeout( loff_t timeout_ms );
    void unregisterTimeout( loff_t timeout_ms );
    void wakeupMainLoop();

    void showCommandFailed( int status );

    void setIgnoreKey( KeySym key, bool state );
    bool isIgnoredKey( KeySym key ) const;

    void shuffleButton( int dir );
    void switchToButtonBank( int nr );

    EventCallbacks &getEventHandler();
    TemporaryFileCache::TemporaryFileCacheEntry getTempCopy( const std::string &filename,
                                                             CopyfileProgressCallback *progress_callback = NULL );

    DeepPathStore &getPathStore();

    bool storePathPers( const std::string &path, time_t ts );
    bool storePathPers( const std::string &path,
                        const std::string &command_str,
                        time_t ts );
    bool storePathPersArchive( const std::string &path, time_t ts );
    void removePathPers( const std::string &path );
    void removePathPersArchive( const std::string &path );
    void removePathProgramPers( const std::string &program );
    DeepPathStore getPathsPers();
    DeepPathStore getPathsPersArchive();
    std::map< std::string, DeepPathStore > getPathProgramsPers();
    loff_t getSizeOfPersPaths() const;
    loff_t getSizeOfPersPathsArchive() const;
    loff_t getSizeOfPersPathPrograms() const;
    void relocateEntriesPers( const std::string &dest,
                              const std::string &source,
                              time_t ts,
                              bool move );

    bool storePathPersIfInProgress( const std::string &path );
    void setPathJumpInProgress( bool nv = true );
    void setPathJumpInProgress( const std::string last_filter );

    void showNextHint();

    RefCount< PathnameWatcher > getPathnameWatcher();

    bool checkEnterDir( const std::string &dir );

    static mode_t getUMask();
    
    void openCommandMenu( bool show_recently_used, WorkerTypes::command_menu_node start_node );

    worker_version getLastRunningVersion();
    void checkForUpdates( const worker_version &last_running_version,
                          WConfig *conf );

    void setOverrideXIM( bool nv );

    static PersistentKVP &getPersKVPStore();

    PrefixDB &getDirHistPrefixDB();

    bool getKeyChainCandidateActive() const;

    static KVPStore &getKVPStore();

    void enqueueFuture( std::future< ExistenceTest::existence_state_t > f );

    std::list< WorkerTypes::reg_command_info_t > getListOfCommandsWithoutArgs() const;
    WorkerTypes::reg_command_info_t getCommandInfo( const std::string &command, bool args ) const;

    static std::string getTerminalCommand( const std::string &cmdfile,
                                           const std::string &outfile,
                                           bool &remove_after_exec );

    void issueConfigOpen( const std::string &panel );
    void issueConfigOpen( const std::string &panel, const WConfigInitialCommand &cmd );

    std::shared_ptr< WPUContext > getCurrentWPU();

    static void pushCommandLog( const std::string &command,
                                const std::string &path,
                                const std::string &src,
                                const std::string &description );

    std::list< FileCommandLog::Entry > getCommandLog() const;
    void clearCommandLog();

    std::shared_ptr< FilterStash > getFilterStash();
protected:
  void createMainWin();
  void setupMainWin();
  void closeMainWin();
  void shufflePath();
  void shuffleButton( int dir, bool nocycle );
  void about();
  bool configure( const std::string &panel = "" );
  void showPathBank();
  void showButtonBank();
  void setPath(int);
  void setPath( WCPath *);
  int activateShortkey(AGMessage*);
  void activateButton(int,int,int);
    void activateButton( int pos );
    void activateHotkey( int pos );
    void activateCommandByID( int id, bool interactive = false );
  int saveListerState();
  int initListerState();
  void updateTime();

  static AGUIX *aguix;
  static int workerInstances;
  Lister *lister[2];
  AWindow *mainwin;
  Button *aboutb,*configureb,*clockbar;
  Button *statebar;
  Button **pathbs;
  Button **buttons;
  Slider *button_bank_slider;
  AWindow *listerwin[2];
  AContainer *maincont;
  
  int m_main_window_width, m_main_window_height;
    int m_main_window_last_absolute_x;
    int m_main_window_last_absolute_y;
    bool m_main_window_last_maximized_x;
    bool m_main_window_last_maximized_y;

  unsigned int curRows,curColumns;
  int pbanknr,bbanknr;
  
  enum {WORKER_NORMAL,WORKER_QUIT};
  int runningmode;
  static int argc;
  static char **argv;
  static int m_optind;
  static std::string startcwd;
  time_t lasttimep;
  
  DNDMsg *dm;

  static Requester *req_static;
  
    static int shortkeysort( void *p1, void *p2, int sortmode );
  struct shortkeylisttype {WCHotkey *h;
                           WCButton *b;
                           WCPath *p;};
  PartSpace *freesp;
  bool isRoot;
  
  KeySym lastkey;
  unsigned int lastmod;
  Time lastkey_time;
  
  typedef struct _keyhash {
    WCButton *b;
    WCHotkey *h;
    WCPath *p;
    struct _keyhash *next;
  } keyhash;
  keyhash **keyhashtable;
  int keyhashtable_size;
  void buildtable();
  void deletetable();
  unsigned int keyhashfunc( KeySym k, unsigned int m );
  void insertkey( KeySym k, unsigned int m, WCButton *b, WCHotkey *h, WCPath *p );
  int findkey( KeySym k, unsigned int m, WCButton **b, WCHotkey **h, WCPath **p );
  int findkey( KeySym k1, unsigned int m1, KeySym k2, unsigned int m2, WCButton **b, WCHotkey **h, WCPath **p );
  bool isKeyChainPrefix( KeySym k, unsigned int m );
  int waitcursorcount;
  
  int getMaxButtonsWidth();

  std::string m_statebar_text;

  std::string queryLanguage();

  void createWorkerMenu();
  PopUpMenu *m_worker_menu;
  void startMenuAction( AGMessage *msg );
  void saveWorkerState();

  int m_interpret_recursion_depth;
  static const int m_interpret_max_recursion = 1000;

  static std::unique_ptr<BookmarkDBProxy> m_bookmarkproxy;

  std::map<const PopUpMenu *, std::list< RefCount<GenericCallbackArg<void, AGMessage*> > > > m_popup_callbacks;

  void handlePopUps( AGMessage *msg );

  bool m_use_custom_layout;
  LayoutSettings m_custom_layout;

    typedef enum {
        RS_RECONFIG,
        RS_RELAYOUT,
        RS_NONE
    } restart_t;
    restart_t restart;

    std::list< TimedText > m_statebar_texts;
    bool getValidStatebarText( TimedText &return_text );
    bool getNextValidStatebarText( TimedText &return_text );
    void updateStatebar();

    TimedText m_key_message;
    std::string getNamesOfKeyChainCompletions( KeySym k, unsigned int m,
                                               std::list< WCButton * > *buttons,
                                               std::list< WCHotkey * > *hotkeys,
                                               std::list< WCPath * > *paths );

    HWVolumeManager m_volume_manager;
    void checkNewVolumes();
    std::list< HWVolume > m_known_volumes;
    TimedText m_new_volumes_infotext;

    void updateVolumeManagerConfig();
    void initVolumeManagerStuff();

    void queryNewVolumeAction( const std::list< HWVolume > &l );

    void initWaitThread();
    void shutdownWaitThread();

    RefCount< EventQueue > m_eventqueue;
    std::shared_ptr< TimeoutStore > m_timeouts;
    RefCount< WaitThread > m_waitthread;

    loff_t m_current_clock_update_ms;
    void setClockUpdate( loff_t time_ms );

    loff_t m_current_dbus_update_ms;
    void setDbusUpdate( loff_t time_ms );

    loff_t m_current_statebar_update_ms;
    void setStatebarUpdate( loff_t time_ms );

    TemporaryFileCache m_temp_file_cache;
    ProcessHandler m_process_handler;
    void waitForEvent();

    struct worker_event_store {
        worker_event_store() : check_children( false ) {}
        bool check_children;
    } m_event_store;

    int buildShortkeyLV( FieldListView *lv,
                         List *sklist );

    std::map< KeySym, int > m_ignored_keys;

    void updateSettingsFromWConfig();

    EventCallbacks m_event_handler;
    std::map< EventCallbacks::event_callback_type_t, bool > m_events_seen;
    void fireSeenEvents();

    void updateHintDB();

    void prepareMenuTree( MenuTreeUI &menuui );
    void initWorkerTextMenu( MenuTreeNode *menu );
    bool checkCommandListForCommand( const command_list_t &list, const std::string &name );

    void updateRunningVersion();

    bool importUpdate( WConfig *baseconfig,
                       const std::pair< worker_version, std::string > &config_entry );

    void switchCurrentMode( int mode );
    void configureCurrentMode();

    std::string getOverrideXIMPath() const;

    void setKeyChainCandidateActive( bool nv );

    void showButtonCommandHelp( int i, int j, int state );

    void loadWindowGeometry();

    void cleanFutures();
    void checkFutures();

    void disableHelp();
    void showHelp( KeySym lastkey,
                   unsigned int lastmod,
                   const std::list< WCButton * > &button_list,
                   const std::list< WCHotkey * > &hotkey_list,
                   const std::list< WCPath * > &path_list );

    std::string getButtonHelpText( int relative_pos_in_bank ) const;

    void setCurrentWPU( std::shared_ptr< WPUContext > wpu );

    void checkErrnoCount();
    void tryIncreaseXZMemlimit();

    int requestCommandArgs( const std::string &name,
                            std::string &result );

    void initClockbarFlagReplacer();

    DeepPathStore m_path_store;
    std::unique_ptr< PersDeepPathStore > m_pers_path_store;
    std::unique_ptr< PersDeepPathStore > m_pers_path_store_archive;

    std::shared_ptr< FilterStash > m_filter_stash;

    std::string m_current_hint;
    time_t m_hint_time;
    RefCount< HintDB > m_hint_db;
    RefCount< PathnameWatcher > m_pathname_watcher;
    std::set< std::string > m_changed_paths;

    enum ssh_allow_t { SSH_ASK, SSH_NEVER, SSH_ALWAYS } m_ssh_allow;

    static mode_t m_process_umask;

    RefCount< MenuTreeUI > m_menu_ui;
    bool m_menu_active;

    int m_commandmenu_original_button_banknr;
    int m_commandmenu_original_path_banknr;

    static std::unique_ptr< PersistentKVP > m_pers_kvp_store;
    static KVPStore m_kvp_store;

    bool m_path_jump_in_progress;
    std::string m_path_jump_last_filter;

    std::unique_ptr< PrefixDB > m_pdb;

    bool m_key_chain_cand_active;

    TimedText m_button_help_text;

    std::list< std::future< ExistenceTest::existence_state_t > > m_existence_futures;

    std::string m_pending_config_open;
    WConfigInitialCommand m_pending_config_command;

    TimedText m_help_message;
    bool m_help_active = false;

    std::weak_ptr< WPUContext > m_current_wpu;

    static std::unique_ptr< FileCommandLog > m_command_log;
    static std::mutex m_command_log_mutex;

    size_t m_last_enomem_count = 0;
    size_t m_last_avfs_xz_memlimit_hit = 0;

    std::map< WorkerTypes::command_menu_node, MenuTreeNode * > m_menutree_element;

    std::shared_ptr< FlagReplacer::FlagProducerCallback > m_clockbar_flag_producer;
    std::shared_ptr< FlagReplacer > m_clockbar_flag_replacer;

    bool m_clockbar_dirty = false;
};

#endif

/* Local Variables: */
/* mode:c++ */
/* End: */
