/* dirsizeop.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2001-2021 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 "showdircacheop.h"
#include "listermode.h"
#include "virtualdirmode.hh"
#include "worker.h"
#include "worker_locale.h"
#include "aguix/stringgadget.h"
#include "aguix/button.h"
#include "aguix/fieldlistview.h"
#include "nwc_path.hh"
#include "argclass.hh"
#include <algorithm>

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

ShowDirCacheOp::ShowDirCacheOp() : FunctionProto()
{
}

ShowDirCacheOp::~ShowDirCacheOp()
{
}

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

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

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

int
ShowDirCacheOp::run( std::shared_ptr< WPUContext > wpu, ActionMessage *msg )
{
  ListerMode *lm1;
  if(msg->mode!=msg->AM_MODE_DNDACTION) {
    Lister *l1=msg->getWorker()->getActiveLister();
    if(l1!=NULL) {
      lm1 = l1->getActiveMode();
      if(lm1!=NULL) {
          showCacheList( msg );
      }
    }
  }
  return 0;
}

const char *
ShowDirCacheOp::getDescription()
{
  return catalog.getLocale( 1286);
}

void ShowDirCacheOp::showCacheList( ActionMessage *msg )
{
    ListerMode *lm1;
    Lister *l1 = msg->getWorker()->getActiveLister();

    if ( l1 == NULL ) return;

    lm1 = l1->getActiveMode();

    if ( lm1 == NULL ) return;

    VirtualDirMode *vdm = dynamic_cast< VirtualDirMode *>( lm1 );

    AWindow *win;
    int my_w, my_h, endmode, mw, mh;
    AGMessage *agmsg;
    FieldListView *cachelv;
    StringGadget *cachesg;
    int parents, cachebegin;
    AGUIX *aguix = Worker::getAGUIX();
    std::list< std::string > cache_entries;
    int row;

    if ( vdm != NULL ) {
        cache_entries = vdm->getNameOfCacheEntries();
    }
  
    win = new AWindow( aguix, 10, 10, 10, 10, catalog.getLocale( 523 ), AWindow::AWINDOW_DIALOG );
    win->create();

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

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

    AContainer *ac1_2 = ac1->add( new AContainer( win, 1, 2 ), 0, 1 );
    ac1_2->setMinSpace( 0 );
    ac1_2->setMaxSpace( 0 );
    ac1_2->setBorderWidth( 0 );
  
    cachelv = (FieldListView*)ac1_2->add( new FieldListView( aguix, 0, 0, 50, 50 ,0 ),
                                          0, 0, AContainer::CO_MIN );
    cachelv->setHBarState( 2 );
    cachelv->setVBarState( 2 );
  
    cachesg = (StringGadget*)ac1_2->add( new StringGadget( aguix, 0, 0, 50, "", 0 ),
                                         0, 1, AContainer::CO_INCW );
  
    AContainer *ac1_1 = ac1->add( new AContainer( win, 2, 1 ), 0, 2 );
    ac1_1->setMinSpace( 5 );
    ac1_1->setMaxSpace( -1 );
    ac1_1->setBorderWidth( 0 );
    Button *okb =(Button*)ac1_1->add( new Button( aguix,
                                                  0,
                                                  0,
                                                  catalog.getLocale( 11 ),
                                                  0 ), 0, 0, AContainer::CO_FIX );
    Button *cancelb = (Button*)ac1_1->add( new Button( aguix,
                                                       0,
                                                       0,
                                                       catalog.getLocale( 8 ),
                                                       0 ), 1, 0, AContainer::CO_FIX );

    std::string cwd = lm1->getCurrentDirectory();
    if ( ! cwd.empty() ) {
        char *tstr1 = dupstring( cwd.c_str() );
        for ( ;; ) {
            char *tstr2 = NWC::Path::parentDir( tstr1, NULL );
            if ( tstr2 == NULL ) break;
      
            // add tstr2 to the lv
            row = cachelv->addRow();
            cachelv->setText( row, 0 , tstr2 );
            cachelv->setPreColors( row, FieldListView::PRECOLOR_ONLYACTIVE );
      
            _freesafe( tstr1 );
            tstr1 = tstr2;
            if ( strcmp( tstr1, "/" ) == 0 ) break;
        }
        _freesafe( tstr1 );
    }
  
    parents = cachelv->getElements();

    std::string delim1( strlen( catalog.getLocale( 523 ) ), '-' );
    row = cachelv->addRow();
    cachelv->setText( row, 0, delim1 );
    cachelv->setPreColors( row, FieldListView::PRECOLOR_NOTSELORACT );
    row = cachelv->addRow();
    cachelv->setText( row, 0, catalog.getLocale( 523 ) );
    cachelv->setPreColors( row, FieldListView::PRECOLOR_NOTSELORACT );
    row = cachelv->addRow();
    cachelv->setText( row, 0, catalog.getLocale( 812 ) );
    cachelv->setPreColors( row, FieldListView::PRECOLOR_NOTSELORACT );
    row = cachelv->addRow();
    cachelv->setText( row, 0, delim1 );
    cachelv->setPreColors( row, FieldListView::PRECOLOR_NOTSELORACT );
  
    cachebegin = row + 1;

    if ( ! cache_entries.empty() ) {
        for ( auto &e : cache_entries ) {
            row = cachelv->addRow();
            cachelv->setText( row, 0, e );
            cachelv->setPreColors( row, FieldListView::PRECOLOR_ONLYACTIVE );
        }
    }

    // maximize cachelv and store sizes
    cachelv->maximizeX();
    cachelv->maximizeY();
    my_w = cachelv->getWidth();
    my_h = cachelv->getHeight();

    // now resize container to 80% screen size
    int rx, ry, rw, rh;

    aguix->getLargestDimensionOfCurrentScreen( &rx, &ry,
                                               &rw, &rh );

    mw = rw * 80 / 100;
    mh = rh * 80 / 100;
    ac1->resize( mw, mh );
    ac1->rearrange();

    // and check whether the cachelv needs less space
    // and set min parameter to match 80% or less
    if ( my_w < cachelv->getWidth() ) {
        ac1_2->setMinWidth( my_w, 0, 0 );
    } else {
        ac1_2->setMinWidth( cachelv->getWidth(), 0, 0 );
    }
    if ( my_h < cachelv->getHeight() ) {
        ac1_2->setMinHeight( my_h, 0, 0 );
    } else {
        ac1_2->setMinHeight( cachelv->getHeight(), 0, 0 );
    }

    win->contMaximize( true );
    win->setDoTabCycling( true );
    cachesg->takeFocus();

    if ( parents > 0 ) {
        cachelv->setActiveRow( 0 );
        cachesg->setText( cachelv->getText( 0, 0 ).c_str() );
    } else if ( ! cache_entries.empty() ) {
        cachelv->setActiveRow( cachebegin );
        cachesg->setText( cachelv->getText( cachebegin, 0 ).c_str() );
    }

    // catch msgs before mapping, this are mostly resize msgs
    while ( ( agmsg = aguix->GetMessage( win ) ) != NULL ) aguix->ReplyMessage( agmsg );

    win->show();

    for( endmode = -1; endmode == -1; ) {
        agmsg = aguix->WaitMessage( win );
        if ( agmsg != NULL ) {
            switch ( agmsg->type ) {
                case AG_CLOSEWINDOW:
                    if ( agmsg->closewindow.window == win->getWindow() ) endmode = 1;
                    break;
                case AG_BUTTONCLICKED:
                    if ( agmsg->button.button == okb ) endmode = 0;
                    else if ( agmsg->button.button == cancelb ) endmode = 1;
                    break;
                case AG_FIELDLV_ONESELECT:
                case AG_FIELDLV_MULTISELECT:
                    if ( agmsg->fieldlv.lv == cachelv ) {
                        row = cachelv->getActiveRow();
                        if ( cachelv->isValidRow( row ) == true ) {
                            if ( ( row < parents ) ||
                                 ( row >= cachebegin ) ) {
                                // this is one of the parents
                                cachesg->setText( cachelv->getText( row, 0 ).c_str() );
                            }
                        }
                    }
                    break;
                case AG_FIELDLV_DOUBLECLICK:
                    if ( agmsg->fieldlv.lv == cachelv &&
                         cachelv->isValidRow( agmsg->fieldlv.row ) == true ) {
                        if ( ( agmsg->fieldlv.row < parents ) ||
                             ( agmsg->fieldlv.row >= cachebegin ) ) {
                            // this is one of the parents
                            endmode = 0;
                        }
                    }
                    break;
                case AG_STRINGGADGET_CONTENTCHANGE:
                    if ( agmsg->stringgadget.sg == cachesg ) {
                        //TODO: U.U. merkt user ein Lag, deshalb koennte man ein Flag setzen und
                        // ausserhalb messen, wenn letzte Nachricht her ist und dann suchen
                        // Aber dann muss ich getMessage machen
                        const char *tstr = cachesg->getText();
                        for ( row = 0; row < cachelv->getElements(); row++ ) {
                            if ( strcmp( tstr, cachelv->getText( row, 0 ).c_str() ) == 0 ) {
                                if ( ( row >= 0 && row < parents ) ||
                                     ( row >= cachebegin && row < ( cachebegin + (int)cache_entries.size() ) ) ) {
                                    cachelv->setActiveRow( row );
                                    cachelv->showActive();
                                }
                                break;
                            }
                        }
                    }
                    break;
                case AG_STRINGGADGET_CANCEL:
                    if ( agmsg->stringgadget.sg == cachesg ) endmode = 1;
                    break;
                case AG_STRINGGADGET_OK:
                    if ( agmsg->stringgadget.sg == cachesg ) endmode = 0;
                    break;
                case AG_KEYPRESSED:
                    if ( win->isParent( agmsg->key.window, false ) == true ) {
                        switch ( agmsg->key.key ) {
                            case XK_Up:
                                row = cachelv->getActiveRow();
                                if ( cachelv->isValidRow( row ) == true ) {
                                    if ( ( row >= parents ) && ( row <= cachebegin ) ) {
                                        row = parents - 1;
                                    } else {
                                        row--;
                                    }
                                    if ( row >= 0 ) {
                                        cachelv->setActiveRow( row );
                                        cachelv->showActive();
                                        cachesg->setText( cachelv->getText( row, 0 ).c_str() );
                                    }
                                }
                                break;
                            case XK_Down:
                                row = cachelv->getActiveRow();
                                if ( cachelv->isValidRow( row ) == true ) {
                                    if ( ( row >= ( parents - 1 ) ) && ( row < cachebegin ) ) {
                                        row = cachebegin;
                                    } else {
                                        row++;
                                    }
                                    if ( cachelv->isValidRow( row ) == true ) {
                                        cachelv->setActiveRow( row );
                                        cachelv->showActive();
                                        cachesg->setText( cachelv->getText( row, 0 ).c_str() );
                                    }
                                }
                                break;
                            case XK_Prior:
                                row = cachelv->getActiveRow();
                                if ( cachelv->isValidRow( row ) == true ) {
                                    row -= cachelv->getMaxDisplayV() - 1;
                                    if ( ( row >= parents ) && ( row < cachebegin ) ) row = parents - 1;
                                    if ( row < 0 ) {
                                        if ( parents > 0 ) row = 0;
                                        else row = cachebegin;
                                    }
                                } else {
                                    if ( parents > 0 ) row = 0;
                                    else if ( ! cache_entries.empty() ) row = cachebegin;
                                }
                                if ( cachelv->isValidRow( row ) == true ) {
                                    cachelv->setActiveRow( row );
                                    cachelv->showActive();
                                    cachesg->setText( cachelv->getText( row, 0 ).c_str() );
                                }
                                break;
                            case XK_Next:
                                row = cachelv->getActiveRow();
                                if ( cachelv->isValidRow( row ) == true ) {
                                    row += cachelv->getMaxDisplayV() - 1;
                                    if ( ( row >= parents ) && ( row < cachebegin ) ) row = cachebegin;
                                    if ( row >= ( cachebegin + (int)cache_entries.size() ) ) {
                                        if ( ! cache_entries.empty() ) row = cachebegin + cache_entries.size() - 1;
                                        else row = parents - 1;
                                    }
                                } else {
                                    if ( parents > 0 ) row = 0;
                                    else if ( ! cache_entries.empty() ) row = cachebegin;
                                }
                                if ( cachelv->isValidRow( row ) == true ) {
                                    cachelv->setActiveRow( row );
                                    cachelv->showActive();
                                    cachesg->setText( cachelv->getText( row, 0 ).c_str() );
                                }
                                break;
                            case XK_Home:
                                row = -1;
                                if ( parents > 0 ) row = 0;
                                else if ( ! cache_entries.empty() ) row = cachebegin;
                                if ( cachelv->isValidRow( row ) == true ) {
                                    cachelv->setActiveRow( row );
                                    cachelv->showActive();
                                    cachesg->setText( cachelv->getText( row, 0 ).c_str() );
                                }
                                break;
                            case XK_End:
                                row = -1;
                                if ( ! cache_entries.empty() ) row = cachebegin + cache_entries.size() - 1;
                                else if ( parents > 0 ) row = parents - 1;
                                if ( cachelv->isValidRow( row ) == true ) {
                                    cachelv->setActiveRow( row );
                                    cachelv->showActive();
                                    cachesg->setText( cachelv->getText( row, 0 ).c_str() );
                                }
                                break;
                            case XK_Return:
                            case XK_KP_Enter:
                                if ( cancelb->getHasFocus() == false ) {
                                    endmode = 0;
                                }
                                break;
                            case XK_Escape:
                                endmode = 1;
                                break;
                        }
                    }
                    break;
            }
            aguix->ReplyMessage( agmsg );
        }
    }
    if ( endmode == 0 ) {
        // ok->take cachesg and enterdir
        std::string dir = cachesg->getText();

        if ( std::find( cache_entries.begin(),
                        cache_entries.end(),
                        dir ) != cache_entries.end() ) {
            std::list< RefCount< ArgClass > > args;

            args.push_back( RefCount< ArgClass >( new StringArg( dir ) ) );
            lm1->runCommand( "show_cache_entry", args );
        } else {
            std::list< RefCount< ArgClass > > args;

            args.push_back( RefCount< ArgClass >( new StringArg( dir ) ) );
            lm1->runCommand( "enter_dir", args );
        }
    }
    delete win;

    return;
}
