/* searchopbg.hh
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2011-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 SEARCHOPBG_HH
#define SEARCHOPBG_HH

#include "wdefines.h"
#include <string>
#include <list>
#include <utility>
#include "searchthread.hh"
#include "aguix/fieldlistview.h"
#include "aguix/backgroundmessagehandler.hh"
#include "generic_callback.hh"
#include "aguix/refcount.hh"

class SearchSettings;
class Text;
class ResultStore;
class Worker;
class PersistentStringList;
class TextStorageFile;
class AWidth;

class SearchOpBGTimeoutCB : public GenericCallback<void>
{
public:
    SearchOpBGTimeoutCB( class SearchOpBG *s );
    SearchOpBGTimeoutCB();

    void callback();
    void reset();
private:
    class SearchOpBG *m_s;
};

class SearchOpBG : public BackgroundMessageHandler
{
public:
    ~SearchOpBG();
    
    void createWindow();
    
    void handleAGUIXMessage( AGMessage &msg );

    void setInitialStartDir( const std::string &dir );
    void setShowPrevResults( bool nv );
    void setEditCommand( const std::string &str );

    void timeout_callback();

    void setInitialVDir( std::unique_ptr< NWC::Dir > d );

    static void clear_stored_results();
protected:
    friend class SearchOp;

    SearchOpBG( Worker *worker );
    SearchOpBG( const SearchOpBG &other );
    SearchOpBG &operator=( const SearchOpBG &other );

    AWindow *getAWindow();
private:
    Worker *m_worker;
    std::string m_initial_start_dir;
    bool m_show_prev_results;
    std::string m_edit_command;

    Button *m_newsearchb;
    Button *m_younger_cal_b;
    Button *m_older_cal_b;
    Button *m_size_help_b;
    Button *m_enterb;
    Button *m_viewb;
    Button *m_editb;
    Button *m_removeb;
    Button *m_clearstoreb;
    Text *m_state_text;
    Button *m_startb;
    Button *m_stopb;
    Button *m_closeb;
    AWindow *m_win;
    Button *m_panelize_b;

    void clearStoredResults();
    void buildLV();
    void buildStoreLV();
    void setResultStore( int pos, const SearchSettings *sets = NULL );

    void stopSearch();
    void gatherResults( int get_number_of_results = -1,
                        int time_limit_ms = -1);
    void startSearch();
    void newSearch();
    SearchSettings getCurrentSearchSettings();
    void storeSearchSettings();
    void restoreSearchSettings();
    void setCurrentResultStore( int pos, const SearchSettings *sets = NULL );
    void storeActiveRow( int row );

    void removeSelectedResults();
    void removeByBaseDir();
    std::unique_ptr< std::list< std::pair< std::string, int > > > getSelectedResults();
    SearchThread::SearchResult getResultForRow( int row );

    void viewResult();
    void editResult();
    void editFile( const std::string &filename, int line_number );

    void initColorDefs();

    class AContainer *_start_ac;
    class Text *_startdir_text;
    class StringGadget *_startdir_sg;
    class FieldListView *_storelv;
    bool _last_startcont_state;
    FieldListView::ColorDef filecol, dircol, resultcol;
  
    std::string _last_result;
    ResultStore *_rst;
    FieldListView *_resultlv;
    SearchThread *_sth;
    class StringGadget *_matchname_sg;
    class ChooseButton *_matchname_regexp_cb;
    class ChooseButton *_matchname_fullpath_cb;
    class ChooseButton *_matchname_case_cb;
    class StringGadget *_matchcontent_sg;
    class ChooseButton *_matchcontent_case_cb;
    class ChooseButton *_follow_symlinks_cb;
    class ChooseButton *_search_vfs_cb;
    class ChooseButton *_search_same_fs_cb;
    class ChooseButton *m_younger_cb, *m_older_cb;
    class StringGadget *m_younger_sg, *m_older_sg;
    class ChooseButton *m_size_geq_cb, *m_size_leq_cb;
    class StringGadget *m_size_geq_sg, *m_size_leq_sg;
    class Kartei *m_options_k1;
    class StringGadget *m_max_vfs_depth_sg;
    class ChooseButton *m_also_dirs_cb;
    class StringGadget *m_matchname_separator_sg = nullptr;
    class StringGadget *m_exclude_label_sg = nullptr;
    class StringGadget *m_match_expression_sg = nullptr;
    class AContainer *m_expression_help_ac = nullptr;
    std::vector< class Button * > m_expression_help_buttons;
    std::vector< std::string > m_expression_help_token;
    class Text *m_expression_valid_t = nullptr;
    class AContainer *m_expression_ac = nullptr;
    FieldListView *m_history_lv = nullptr;
    Button *m_history_remove_b = nullptr;
    Button *m_history_clear_b = nullptr;
    TextView *m_result_preview_tv = nullptr;
    RefCount< TextStorageFile > m_ts_file;
    RefCount< AWidth > m_lencalc;
    ChooseButton *m_result_preview_cb = nullptr;

    bool _search_started;  ///< flag indicates whether search was started but it's not the accurate status of the search thread

    void updateState( class Text &state_text );

    typedef enum { BUTTON_CONTINUE, BUTTON_SUSPEND, BUTTON_IDLE } buttonmode_t;
    void prepareButton( class Button &startb, buttonmode_t mode );
    void prepareStartCont( bool force = false );

    bool consumeResult( SearchThread::SearchResult &result, class FieldListView &lv );
    void prepareNewSearch();

    void enterResult( const std::string &fullname );

    RefCount< SearchOpBGTimeoutCB > m_timeout_cb;

    void checkForResults();
    void panelizeResults();

    void updateWindowTitle();

    void updateExpressionHelp();

    void updateHistory( StringGadget *sg, bool manual_change );

    void initHistory();
    void advanceHistoryItem( int direction );
    void removeHistoryItem( bool all );

    void previewResult();

    int m_update_ctr;
    int m_stored_result_last_changed;

    bool m_timeout_set;

    std::unique_ptr< NWC::Dir > m_initial_start_vdir;

    std::list< std::string > m_skip_prefixes;

    std::shared_ptr< PersistentStringList > m_current_match_history;

    static int s_search_number;
    static std::shared_ptr< PersistentStringList > m_match_name_history;
    static std::shared_ptr< PersistentStringList > m_match_content_history;
    static std::shared_ptr< PersistentStringList > m_match_expression_history;
};

#endif
