/* virtualdirmode_regcommands.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2014-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
 */

#include "virtualdirmode.hh"
#include "worker.h"
#include "aguix/fieldlistviewdnd.h"
#include "aguix/button.h"
#include "aguix/acontainer.h"
#include "aguix/awindow.h"
#include "argclass.hh"
#include "wpucontext.h"
#include "worker_locale.h"
#include "nwcentryselectionstate.hh"
#include <cinttypes>
#include "aguix/akvp_pool.hh"

void VirtualDirMode::vdir_from_selected()
{
    if ( ! ce.get() ) return;

    auto sel_vdir = get_selected_entries_as_vdir();

    showDir( sel_vdir );
}

void VirtualDirMode::vdir_add_selected_from_other_side()
{
    std::list< NM_specialsourceExt > filelist;
    ListerMode *lm1 = NULL;
    Lister *ol = NULL;
  
    ol = parentlister->getWorker()->getOtherLister( parentlister );

    if ( ! ol ) return;
    
    lm1 = ol->getActiveMode();

    if ( ! lm1 ) return;
    
    lm1->getSelFiles( filelist, ListerMode::LM_GETFILES_ONLYSELECT, true );

    if ( ! ce.get() || ce->isRealDir() ) {
        std::string name = AGUIXUtils::formatStringToString( "sel_vdir%d", s_selvdir_number++ );

        if ( s_selvdir_number < 0 ) {
            // avoid negative and zero number
            s_selvdir_number = 1;
        }

        std::unique_ptr< NWC::Dir > selvdir( new NWC::VirtualDir( name ) );

        for ( auto &sse : filelist ) {
            const FileEntry *tfe = sse.entry();

            if ( tfe ) {
                selvdir->add( NWC::FSEntry( tfe->fullname ) );
            }
        }

        showDir( selvdir );
    } else {
        ce->beginModification( DMCacheEntryNWC::MOD_INSERTION );

        for ( auto &sse : filelist ) {
            const FileEntry *tfe = sse.entry();

            if ( tfe ) {
                ce->insertEntry( tfe->fullname );
            }
        }

        ce->endModification();

        setCurrentCE( ce );

        m_lv->showActive();
    }
}

void VirtualDirMode::vdir_from_script_stack( const std::list< RefCount< ArgClass > > &args )
{
    if ( args.empty() ) {
        return;
    }

    int value = -1;

    RefCount< ArgClass > a1 = args.front();
    if ( a1.get() != NULL ) {
        if ( auto a2 = dynamic_cast< IntArg *>( a1.get() ) ) {
            value = a2->getValue();
        } else if ( auto sa1 = dynamic_cast< StringArg *>( a1.get() ) ) {
            auto conv = sa1->convertToInt();

            if ( conv.second ) {
                value = conv.first.getValue();
            }
        }
    }

    if ( value < 0 ) {
        return;
    }

    std::string name = AGUIXUtils::formatStringToString( "sel_vdir%d", s_selvdir_number++ );

    if ( s_selvdir_number < 0 ) {
        // avoid negative and zero number
        s_selvdir_number = 1;
    }

    std::unique_ptr< NWC::Dir > selvdir( new NWC::VirtualDir( name ) );

    auto wpu = parentlister->getWorker()->getCurrentWPU();

    if ( wpu ) {
        for ( int i = 0; i < wpu->size( value ); i++ ) {
            selvdir->add( NWC::FSEntry( wpu->stackEntry( value, i ) ) );
        }
    }

    showDir( selvdir );
}

void VirtualDirMode::vdir_add_entry( const std::list< RefCount< ArgClass > > &args )
{
    if ( args.empty() ) {
        return;
    }

    std::string value;

    for ( auto &e : args ) {
        if ( e.get() != NULL ) {
            StringArg *a = dynamic_cast< StringArg *>( e.get() );

            if ( a ) {
                value = a->getValue();
            }
        }
    }

    if ( value.empty() ) {
        return;
    }

    if ( ! ce.get() || ce->isRealDir() ) {
        std::string name = AGUIXUtils::formatStringToString( "sel_vdir%d", s_selvdir_number++ );

        if ( s_selvdir_number < 0 ) {
            // avoid negative and zero number
            s_selvdir_number = 1;
        }

        std::unique_ptr< NWC::Dir > selvdir( new NWC::VirtualDir( name ) );

        selvdir->add( NWC::FSEntry( value ) );

        showDir( selvdir );
    } else {
        ce->beginModification( DMCacheEntryNWC::MOD_INSERTION );

        ce->insertEntry( value );

        ce->endModification();

        setCurrentCE( ce );

        m_lv->showActive();
    }
}

void VirtualDirMode::copy_files_to_clipboard( const std::list< RefCount< ArgClass > > &args )
{
    if ( ! ce.get() ) return;

    std::string value;

    for ( auto &e : args ) {
        if ( e.get() != NULL ) {
            StringArg *a = dynamic_cast< StringArg *>( e.get() );

            if ( a ) {
                value = a->getValue();
            }
        }
    }

    std::list< NM_specialsourceExt > filelist;

    getSelFiles( filelist, ListerMode::LM_GETFILES_SELORACT );

    std::vector< std::string > l;

    for ( auto &sse : filelist ) {
        const FileEntry *tfe = sse.entry();

        //TODO we could mark virtual files specially and add it to
        //clipboard with avfs:// prefix or something like that.
        if ( tfe ) {
            l.push_back( tfe->fullname );
        }
    }

    aguix->copyFileListToClipboard( l, value == "cut" ? true : false );
}

void VirtualDirMode::copy_filecontent_to_clipboard()
{
    if ( ! ce.get() ) return;

    std::list< NM_specialsourceExt > filelist;

    getSelFiles( filelist, ListerMode::LM_GETFILES_ONLYACTIVE );

    for ( auto &sse : filelist ) {
        const FileEntry *tfe = sse.entry();

        if ( ! tfe->isReg() ) break;

        std::string content;
        Datei d;
        if ( d.open( tfe->fullname, "r" ) == 0 ) {
            char buf[1024];
            for (;;) {
                ssize_t res = d.read( buf, sizeof( buf ) );
                if ( res < 1 ) break;
                content.append( buf, res );
            }
            int res = aguix->copyToClipboard( content, AGUIX::CLIPBOARD_BUFFER, tfe->getMimeType() );
            if ( res == -EFBIG ) {
                auto text = AGUIXUtils::formatStringToString( catalog.getLocale( 1609 ),
                                                              AKVPPool::getGlobalKVPPool()->getEntry( "clipboard.max_content_size" ).value.int_value );
                Worker::getRequester()->request( catalog.getLocale( 124 ),
                                                 text.c_str(),
                                                 catalog.getLocale( 11 ) );
            }
        }
        break;
    }
}

void VirtualDirMode::paste_files_from_clipboard()
{
    aguix->requestCut( m_lv->getWindow(),
                       false, true,
                       nullptr, this,
                       AGUIX::CLIPBOARD_REQUEST_FILE_LIST );
}

void VirtualDirMode::revert_selection( const std::list< RefCount< ArgClass > > &args )
{
    if ( m_selection_checkpoints.empty() ) return;

    std::string value;

    for ( auto &e : args ) {
        if ( e.get() != NULL ) {
            StringArg *a = dynamic_cast< StringArg *>( e.get() );

            if ( a ) {
                value = a->getValue();
            }
        }
    }

    if ( value.empty() ) {
        revertSelectionCheckpoint( m_selection_checkpoints.front() );
        return;
    }

    const char *value_str = value.c_str();
    char *end_str = NULL;
    long v = strtol( value_str, &end_str, 10 );

    if ( *end_str == '\0' ) {
        auto it = m_selection_checkpoints.begin();
        std::advance( it, v );

        if ( it != m_selection_checkpoints.end() ) {
            revertSelectionCheckpoint( *it );
        }

        return;
    }

    revert_selection_from_menu();
}

void VirtualDirMode::revert_selection_from_menu()
{
    auto win = std::make_unique< AWindow >( aguix, 10, 10, 10, 10, catalog.getLocale( 1474 ), AWindow::AWINDOW_DIALOG );
    win->create();
    win->setTransientForAWindow( aguix->getTransientWindow() );

    AContainer *ac1 = win->setContainer( new AContainer( win.get(), 1, 3 ), true );
    ac1->setMinSpace( 5 );
    ac1->setMaxSpace( 5 );
    ac1->setBorderWidth( 5 );

    ac1->addWidget( new Text( aguix, 0, 0, catalog.getLocale( 1475 ) ),
                    0, 0, AContainer::CO_INCWNR );

    auto *lv = ac1->addWidget( new FieldListView( aguix, 0, 0, 10, 10, 0 ),
                               0, 1, AContainer::CO_MIN );

    lv->setNrOfFields( 2 );
    lv->setGlobalFieldSpace( 5 );
    lv->setDefaultColorMode( FieldListView::PRECOLOR_ONLYACTIVE );
    lv->setShowHeader( true );
    lv->setFieldText( 0, catalog.getLocale( 1476 ) );
    lv->setFieldText( 1, catalog.getLocale( 1477 ) );
    lv->setHBarState( 2 );
    lv->setVBarState( 2 );
    lv->setAcceptFocus( true );
    lv->setDisplayFocus( true );

    for ( auto &e : m_selection_checkpoints ) {
        int row = lv->addRow();

        lv->setText( row, 0, e->first.c_str() );

        std::stringstream ss1;

        ss1 << e->second.size();

        lv->setText( row, 1, ss1.str().c_str() );
    }

    lv->maximizeX();
    lv->maximizeY();

    AContainer *ac1_buttons = ac1->add( new AContainer( win.get(), 2, 1 ), 0, 2 );
    ac1_buttons->setMinSpace( 5 );
    ac1_buttons->setMaxSpace( -1 );
    ac1_buttons->setBorderWidth( 0 );

    Button *okb = ac1_buttons->addWidget( new Button( aguix, 0, 0, catalog.getLocale( 11 ), 0 ),
                                          0, 0, AContainer::CO_FIX );
    Button *cancelb = ac1_buttons->addWidget( new Button( aguix, 0, 0, catalog.getLocale( 8 ), 0 ),
                                              1, 0, AContainer::CO_FIX );
    okb->setAcceptFocus( true );
    cancelb->setAcceptFocus( true );
 
    lv->takeFocus();

    ac1->readLimits();
    
    win->setDoTabCycling( true );
    win->contMaximize( true );
    win->centerScreen();
    win->show();

    AGMessage *msg;
    int endmode = -1;

    for(; endmode == -1; ) {
        msg = aguix->WaitMessage( win.get() );
        if ( msg != NULL ) {
            switch ( msg->type ) {
                case AG_CLOSEWINDOW:
                    if ( msg->closewindow.window == win->getWindow() ) {
                        endmode = 1;
                    }
                    break;
                case AG_BUTTONCLICKED:
                    if ( msg->button.button == okb ) {
                        endmode = 0;
                    } else if ( msg->button.button == cancelb ) {
                        endmode = 1;
                    }
                    break;
                case AG_KEYPRESSED:
                    if ( win->isParent( msg->key.window, false ) == true ) {
                        switch ( msg->key.key ) {
                            case XK_KP_Enter:
                            case XK_Return:
                                if ( cancelb->getHasFocus() == true ) {
                                    endmode = 1;
                                } else {
                                    endmode = 0;
                                }
                                break;
                            case XK_Escape:
                                endmode = 1;
                                break;
                            case XK_F1:
                                endmode = 0;
                                break;
                            case XK_F2:
                                endmode = 1;
                                break;
                        }
                    }
                    break;
                case AG_FIELDLV_DOUBLECLICK:
                    if ( msg->fieldlv.lv == lv ) {
                        endmode = 0;
                    }
                    break;
            }
            aguix->ReplyMessage( msg );
        }
    }

    if ( endmode == 0 ) {
        int row = lv->getActiveRow();
        if ( lv->isValidRow( row ) == true ) {
            auto it = m_selection_checkpoints.begin();
            std::advance( it, row );

            if ( it != m_selection_checkpoints.end() ) {
                revertSelectionCheckpoint( *it );
            }
        }
    }

    return;
}

void VirtualDirMode::set_custom_attribute( const std::list< RefCount< ArgClass > > &args )
{
    if ( args.size() != 2 ) return;

    auto it = args.begin();
    auto sa1 = std::dynamic_pointer_cast< StringArg >( *it );
    std::advance( it, 1 );
    auto sa2 = std::dynamic_pointer_cast< StringArg >( *it );

    if ( ! sa1 || ! sa2 ) return;

    if ( AGUIXUtils::starts_with( sa1->getValue(), "/" ) ) {
        setCustomAttribute( sa1->getValue(), "", -1, sa2->getValue() );
    } else {
        setCustomAttribute( "", sa1->getValue(), -1, sa2->getValue() );
    }
}

void VirtualDirMode::clear_custom_attributes()
{
    if ( ! ce.get() ) return;

    for ( size_t pos = 0; pos < ce->getSize(); pos++ ) {
        ce->modifyEntry( pos, []( auto &es ) {
            es.setCustomAttribute( "" );
        });
    }

    rebuildView();
}
