/* view_newest_files_op.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2016-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 "view_newest_files_op.hh"
#include "aguix/aguix.h"
#include "aguix/awindow.h"
#include "aguix/text.h"
#include "aguix/fieldlistview.h"
#include "aguix/button.h"
#include "aguix/textstorage.h"
#include "aguix/textview.h"
#include "listermode.h"
#include "nwc_fsentry.hh"
#include "nmspecialsourceext.hh"
#include "worker_locale.h"
#include "worker.h"
#include "get_files_thread.hh"
#include "nwc_path.hh"
#include "virtualdirmode.hh"
#include "wconfig.h"

int ViewNewestFilesOp::s_vdir_number = 1;

const char *ViewNewestFilesOp::name = "ViewNewestFilesOp";

ViewNewestFilesOp::ViewNewestFilesOp() : FunctionProto()
{
    m_past_days = 0;
}

ViewNewestFilesOp::~ViewNewestFilesOp()
{
}

ViewNewestFilesOp *ViewNewestFilesOp::duplicate() const
{
    ViewNewestFilesOp *ta = new ViewNewestFilesOp();
    return ta;
}

bool ViewNewestFilesOp::isName(const char *str)
{
    if ( strcmp( str, name ) == 0 ) {
        return true;
    } else {
        return false;
    }
}

const char *ViewNewestFilesOp::getName()
{
    return name;
}

int ViewNewestFilesOp::run( std::shared_ptr< WPUContext > wpu, ActionMessage *msg )
{
    Lister *l1;
    ListerMode *lm1;
    
    m_aguix = msg->getWorker()->getAGUIX();

    l1 = msg->getWorker()->getActiveLister();
    if ( l1 == NULL ) {
        return 1;
    }

    msg->getWorker()->setWaitCursor();

    lm1 = l1->getActiveMode();

    std::string dirname;

    if ( lm1 ) {
        dirname = lm1->getCurrentDirectory();
    }

    if ( dirname.empty() ) {
        msg->getWorker()->unsetWaitCursor();
        return 0;
    }

    std::unique_ptr< DirFilterSettings > dir_filter;
    VirtualDirMode *vdm = dynamic_cast< VirtualDirMode *>( lm1 );
    std::unique_ptr< NWC::Dir > use_dir;

    if ( vdm ) {
        dir_filter = vdm->getDirFilterSettings();
        use_dir = vdm->getCurrentDir();
    } else {
        use_dir = std::make_unique< NWC::Dir >( dirname );
        use_dir->readDir( false );
    }

    m_base_dir = dirname;
    m_past_days = 0;

    std::unique_ptr< GetFilesThread > sth( new GetFilesThread( std::move( use_dir ) ) );

    time_t newest_mod_time = 0;
    std::list< std::pair< std::string, time_t > > files;
    time_t now = time( NULL );
    
    sth->setVisitCB( [ &newest_mod_time,
                       &files,
                       &dir_filter,
                       &dirname,
                       &now ]( NWC::File &file )
                     {
                         if ( ! dir_filter ||
                              dir_filter->check( dirname, &file ) == true ) {

                             // ignore files newer than the current time
                             if ( file.stat_lastmod() <= now ) {
                                 files.push_back( std::make_pair( file.getFullname(),
                                                                  file.stat_lastmod() ) );

                                 if ( file.stat_lastmod() > newest_mod_time ) {
                                     newest_mod_time = file.stat_lastmod();
                                 }
                             }
                         }
                     }
                     );
    sth->setVisitEnterDirCB( [ &dir_filter,
                               &dirname ]( NWC::Dir &dir )
                             {
                                 if ( ! dir_filter ||
                                      dir_filter->check( dirname, &dir ) == true ) {
                                     return true;
                                 }

                                 return false;
                             }
                             );
    
    sth->start();

    openWindow();

    m_win->show();

    AGMessage *agmsg;
    int endmode = 0;

    for ( ; endmode == 0 || endmode == 1; ) {
        bool wait_some_time = false;

        agmsg = NULL;

        if ( sth && sth->running() ) {
            agmsg = m_aguix->GetMessage( NULL );
            wait_some_time = true;
        } else {
            if ( sth ) {
                sth->join();
                sth = NULL;

                m_infotext->setText( catalog.getLocale( 1163 ) );

                sortResults( files );

                time_t l = calculateModLimit( newest_mod_time );
                
                updateResults( files, l );
            }
            
            if ( endmode == 1 ) {
                endmode = 2;
            } else {
                agmsg = m_aguix->WaitMessage( NULL );
            }
        }
        if ( agmsg != NULL ) {
            switch ( agmsg->type ) {
              case AG_CLOSEWINDOW:
                  endmode = -1;
                  if ( sth ) {
                      sth->signalCancel();
                  }
                  break;
              case AG_BUTTONCLICKED:
                  if ( agmsg->button.button == m_okb && endmode == 0 ) {
                      endmode = 1;

                      if ( sth ) {
                          m_okb->setText( 0, catalog.getLocale( 1165 ) );
                          m_okb->resize( m_okb->getMaximumWidth(),
                                         m_okb->getHeight() );
                      }
                  } else if ( agmsg->button.button == m_closeb ) {
                      endmode = -1;
                      if ( sth ) {
                          sth->signalCancel();
                      }
                  } else if ( agmsg->button.button == m_more_days_b && endmode == 0 ) {
                      m_past_days++;
                      updateTimeWindowText();

                      if ( ! sth ) {
                          time_t l = calculateModLimit( newest_mod_time );
                
                          updateResults( files, l );
                      }
                  } else if ( agmsg->button.button == m_less_days_b && endmode == 0 ) {
                      if ( m_past_days > 0 ) m_past_days--;
                      updateTimeWindowText();

                      if ( ! sth ) {
                          time_t l = calculateModLimit( newest_mod_time );
                
                          updateResults( files, l );
                      }
                  }
                  break;
              case AG_KEYPRESSED:
                  if ( agmsg->key.key == XK_Return && endmode == 0 ) {
                      endmode = 1;

                      if ( sth ) {
                          m_okb->setText( 0, catalog.getLocale( 1165 ) );
                          m_okb->resize( m_okb->getMaximumWidth(),
                                         m_okb->getHeight() );
                      }
                  } else if ( agmsg->key.key == XK_Escape ) {
                      endmode = -1;
                      if ( sth ) {
                          sth->signalCancel();
                      }
                  } else if ( agmsg->key.key == XK_Up && endmode == 0 ) {
                      m_past_days++;
                      updateTimeWindowText();

                      if ( ! sth ) {
                          time_t l = calculateModLimit( newest_mod_time );
                
                          updateResults( files, l );
                      }
                  } else if ( agmsg->key.key == XK_Down && endmode == 0 ) {
                      if ( m_past_days > 0 ) m_past_days--;
                      updateTimeWindowText();

                      if ( ! sth ) {
                          time_t l = calculateModLimit( newest_mod_time );
                
                          updateResults( files, l );
                      }
                  }
                  break;
            }
        } else if ( wait_some_time ) {
            waittime( 10 );
        }

        m_aguix->ReplyMessage( agmsg );
    }

    while ( sth && sth->running() ) {
	    waittime( 10 );
    }
    if ( sth ) {
	    sth->join();
	    sth = NULL;

        // just to be consistent with the panelize output
        sortResults( files );
    }
    
    if ( endmode > 0 ) {
        panelizeResults( msg->getWorker(), files, newest_mod_time );
    }

    m_win.reset();

    msg->getWorker()->unsetWaitCursor();
    return 0;
}

const char *ViewNewestFilesOp::getDescription()
{
    return catalog.getLocale( 1306 );
}

void ViewNewestFilesOp::openWindow()
{
    m_win = std::unique_ptr<AWindow>( new AWindow( m_aguix,
                                                   0, 0,
                                                   500, 400,
                                                   getDescription() ) );
    m_win->create();

    AContainer *cont0 = m_win->setContainer( new AContainer( m_win.get(), 1, 5 ), true );
    cont0->setMaxSpace( 5 );

    RefCount<AFontWidth> lencalc( new AFontWidth( m_aguix, NULL ) );
    m_help_ts = std::shared_ptr< TextStorageString >( new TextStorageString( catalog.getLocale( 1168 ), lencalc ) );
    TextView *help_tv = cont0->addWidget( new TextView( m_aguix,
                                                        0, 0, 50, 80, "", m_help_ts ),
                                          0, 0, AContainer::CO_INCW );
    help_tv->setLineWrap( true );
    help_tv->maximizeYLines( 10, 500 );
    help_tv->showFrame( false );
    cont0->readLimits();
    help_tv->show();
    help_tv->setAcceptFocus( false );

    TextView::ColorDef tv_cd = help_tv->getColors();
    tv_cd.setBackground( 0 );
    tv_cd.setTextColor( 1 );
    help_tv->setColors( tv_cd );

    m_infotext = cont0->addWidget( new Text( m_aguix, 0, 0, catalog.getLocale( 1164 ) ),
                                   0, 1, AContainer::CO_INCW );
    
    m_lv = dynamic_cast<FieldListView*>( cont0->add( new FieldListView( m_aguix, 0, 0, 
                                                                        500, 200, 0 ),
                                                     0, 2, AContainer::CO_MIN ) );
    m_lv->setNrOfFields( 2 );
    m_lv->setShowHeader( true );
    m_lv->setFieldText( 0, catalog.getLocale( 176 ) );
    m_lv->setFieldText( 1, catalog.getLocale( 163 ) );
    m_lv->setHBarState( 2 );
    m_lv->setVBarState( 2 );
    m_lv->setGlobalFieldSpace( 5 );
    m_lv->setDefaultColorMode( FieldListView::PRECOLOR_ONLYSELECT );

    AContainer *cont1 = cont0->add( new AContainer( m_win.get(), 4, 1 ), 0, 3 );
    cont1->setBorderWidth( 0 );
    cont1->setMinSpace( 5 );
    cont1->setMaxSpace( 5 );

    cont1->add( new Text( m_aguix, 0, 0, catalog.getLocale( 1160 ) ),
                0, 0, AContainer::CO_FIX );
    m_time_window_text = cont1->addWidget( new Text( m_aguix, 0, 0, "" ),
                                           1, 0, AContainer::CO_INCW );
    m_more_days_b = dynamic_cast<Button*>( cont1->add( new Button( m_aguix, 0, 0, catalog.getLocale( 1161 ), 0 ),
                                                      2, 0, AContainer::CO_FIX ) );
    m_less_days_b = dynamic_cast<Button*>( cont1->add( new Button( m_aguix, 0, 0, catalog.getLocale( 1162 ), 0 ),
                                                      3, 0, AContainer::CO_FIX ) );

    m_more_days_b->setBubbleHelpText( catalog.getLocale( 1166 ) );
    m_less_days_b->setBubbleHelpText( catalog.getLocale( 1167 ) );

    AContainer *cont2 = cont0->add( new AContainer( m_win.get(), 2, 1 ), 0, 4 );
    cont2->setBorderWidth( 0 );
    cont2->setMinSpace( 5 );
    cont2->setMaxSpace( -1 );

    m_okb = dynamic_cast<Button*>( cont2->add( new Button( m_aguix, 0, 0,
                                                           catalog.getLocale( 1034 ),
                                                           0 ),
                                               0, 0, AContainer::CO_FIX ) );
    m_closeb = dynamic_cast<Button*>( cont2->add( new Button( m_aguix, 0, 0,
                                                              catalog.getLocale( 8 ),
                                                              0 ),
                                                  1, 0, AContainer::CO_FIX ) );
   
    m_win->contMaximize( true );
    m_win->setDoTabCycling( true );

    updateTimeWindowText();
}

void ViewNewestFilesOp::updateResults( const std::list< std::pair< std::string, time_t > > &files,
                                       time_t mod_limit )
{
    m_lv->setSize( 0 );

    for ( auto p: files ) {
        if ( p.second >= mod_limit ) {
            int row = m_lv->addRow();

            std::string s = NWC::Path::get_extended_basename( m_base_dir, p.first );
            
            m_lv->setText( row, 0, s );

            s.clear();
            
            struct tm *timeptr = localtime( &p.second );
            wconfig->writeDateToString( s, timeptr );

            m_lv->setText( row, 1, s );
        }
    }

    m_lv->redraw();
    m_aguix->Flush();
}

time_t ViewNewestFilesOp::calculateModLimit( time_t newest_time )
{
    struct tm newest_date;

    localtime_r( &newest_time, &newest_date );

    newest_date.tm_sec = 0;
    newest_date.tm_min = 0;
    newest_date.tm_hour = 0;

    newest_date.tm_mday -= m_past_days;

    return mktime( &newest_date );
}

void ViewNewestFilesOp::panelizeResults( Worker *w,
                                         const std::list< std::pair< std::string, time_t > > &files,
                                         time_t newest_time )
{
    Lister *l1;

    time_t l = calculateModLimit( newest_time );

    l1 = w->getActiveLister();
    if ( l1 != NULL ) {
        l1->switch2Mode( 0 );

        VirtualDirMode *vdm = dynamic_cast< VirtualDirMode* >( l1->getActiveMode() );
        if ( vdm != NULL ) {
            vdm->newTab();

            std::string name = AGUIXUtils::formatStringToString( "viewnewest%d", s_vdir_number++ );

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

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

            const int rows = m_lv->getElements();
            int row = 0;
            bool some_selected = false;

            for ( row = 0; row < rows; row++ ) {
                if ( m_lv->getSelect( row ) ) {
                    some_selected = true;
                    break;
                }
            }

            row = 0;
            for ( auto p: files ) {
                if ( p.second >= l ) {

                    if ( ! some_selected ||
                         m_lv->getSelect( row ) ) {
                        NWC::FSEntry fse( p.first );
                        if ( fse.entryExists() ) {
                            d->add( fse );
                        }
                    }
                    row++;
                }
            }

            vdm->showDir( d );
        }
    }
}

void ViewNewestFilesOp::updateTimeWindowText()
{
    if ( m_past_days == 0 ) {
        m_time_window_text->setText( catalog.getLocale( 1159 ) );
    } else if ( m_past_days == 1 ) {
        std::string t = AGUIXUtils::formatStringToString( catalog.getLocale( 1107 ), 1 );
        m_time_window_text->setText( t.c_str() );
    } else {
        std::string t = AGUIXUtils::formatStringToString( catalog.getLocale( 1108 ), m_past_days );
        m_time_window_text->setText( t.c_str() );
    }
}

void ViewNewestFilesOp::sortResults( std::list< std::pair< std::string, time_t > > &files )
{
    files.sort( []( std::pair< std::string, time_t > &lhs,
                    std::pair< std::string, time_t > &rhs ) -> bool {
                    return lhs.first < rhs.first;
                });
}
