/* lister.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2001-2019 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 "lister.h"
#include "worker.h"
#include "worker_locale.h"
#include "wconfig.h"
#include "configheader.h"
#include "configparser.hh"
#include "aguix/acontainer.h"
#include "aguix/button.h"
#include "aguix/choosebutton.h"
#include "aguix/fieldlistview.h"
#include "aguix/awindow.h"
#include "aguix/icons.hh"
#include "listermode.h"
#include "datei.h"
#include "nmfilter.hh"

Lister::Lister( Worker *tparent, int side )
{
  this->parent = tparent;
  aguix = parent->getAGUIX();
  win = parent->getListerWin( this );

  m_cont = win->setContainer( new AContainer( win, 1, 2 ), true );
  m_cont->setBorderWidth( 0 );
  m_cont->setMinSpace( 0 );
  m_cont->setMaxSpace( 0 );
  m_mode_cont = NULL;

  m_button_cont = m_cont->add( new AContainer( win, 2, 1 ),
                                0, 0 );
  m_button_cont->setBorderWidth( 0 );
  m_button_cont->setMinSpace( 0 );
  m_button_cont->setMaxSpace( 0 );

  lvb = m_button_cont->addWidget( new Button( aguix, 0, 0,
                                              a_max( win->getWidth(), 50 ), "", "", 1, 1, 0, 0, 1 ),
                                  side == 0 ? 0 : 1, 0, AContainer::CO_INCW );
  m_configure_b = m_button_cont->addWidget( new Button( aguix, 0, 0, "", 1, 0, 2 ),
                                            side == 0 ? 1 : 0, 0, AContainer::CO_FIX );
  m_configure_b->resize( m_configure_b->getHeight(),
                         m_configure_b->getHeight() );
  m_configure_b->setAcceptFocus( false );

  m_button_cont->readLimits();

  m_configure_b->setBubbleHelpText( catalog.getLocale( 998 ) );
  m_configure_b->setIcon( aguix_icon_pref, aguix_icon_pref_len,
                          Button::ICON_LEFT );

  m_button_cont->setMinWidth( 50, side == 0 ? 0 : 1, 0 );
  lvb->setAcceptFocus( false );

  lvb->setBubbleHelpText( catalog.getLocale( 1124 ) );
  
  aguix->Flush();
  activemode=NULL;
  activemodeid=-1;
  hasFocus=false;
  int j=parent->getMaxModeNr();
  modes=new ListerMode*[j];
  for(int i=0;i<j;i++) {
    modes[i]=parent->getMode4ID(i,this);
  }
  timerclear( &lastlvbclick );

  win->updateCont();
}

Lister::~Lister()
{
  if(activemode!=NULL) activemode->off();
  int j=parent->getMaxModeNr();
  for(int i=0;i<j;i++) {
    delete modes[i];
  }
  delete [] modes;
  delete lvb;
  delete m_configure_b;
  delete m_cont;
}

void Lister::messageHandler(AGMessage *msg)
{
  bool subHandle=true;
  struct timeval acttime;
  if(msg!=NULL) {
    switch(msg->type) {
      case AG_SIZECHANGED:
        break;
      case AG_BUTTONCLICKED:
        if(msg->button.button==lvb) {
          if(msg->button.state==2) {
            configure();
          } else {
            makeActive();
            
            gettimeofday( &acttime, NULL );
            if ( aguix->isDoubleClick( &acttime, &lastlvbclick ) == true ) {
              if ( activemode != NULL ) activemode->lvbDoubleClicked();
              timerclear( &lastlvbclick );
            } else {
              lastlvbclick.tv_sec = acttime.tv_sec;
              lastlvbclick.tv_usec = acttime.tv_usec;
            }
          }
        } else if ( msg->button.button == m_configure_b ) {
            makeActive();

            if ( activemode != NULL ) {
                activemode->configure();
            }
        }
        break;
      case AG_MOUSECLICKED:
        if(msg->mouse.window==win->getWindow()) {
          makeActive();
        }
        break;
    }
  }

  if ( subHandle == true ) {
      if ( activemode != NULL ) {
          activemode->messageHandler( msg );
      }

      int j = parent->getMaxModeNr();
      for ( int i = 0; i < j; i++ ) {
          if ( modes[i] != NULL && modes[i] != activemode ) {
              modes[i]->messageHandlerInactive( msg );
          }
      }
  }
}

AGUIX *Lister::getAGUIX()
{
  return aguix;
}

AWindow *Lister::getAWindow()
{
  return win;
}
void Lister::setActiveMode(ListerMode *lm)
{
  activemode=lm;
}

void Lister::getGeometry(int *x2,int *y2,int *w2,int *h2)
{
  int tw,th;
  *x2=0;
  lvb->getSize(&tw,&th);
  *y2=th;
  *w2 = m_cont->getWidth( 0, 1 );
  *h2 = m_cont->getHeight( 0, 1 );
  if ( *h2 < 0 ) *h2 = 0;
}

ListerMode *Lister::getActiveMode()
{
  return activemode;
}

void Lister::setFocus(bool state)
{
  int fg,bg;
  hasFocus=state;
  if(state==true) {
      fg = aguix->getFaces().getColor( "lvb-active-fg" );
      bg = aguix->getFaces().getColor( "lvb-active-bg" );
    if(activemode!=NULL) activemode->activate();
  } else {
      fg = aguix->getFaces().getColor( "lvb-inactive-fg" );
      bg = aguix->getFaces().getColor( "lvb-inactive-bg" );
    if(activemode!=NULL) activemode->deactivate();
  }
  lvb->setFG(0,fg);
  lvb->setBG(0,bg);
  lvb->setFG(1,fg);
  lvb->setBG(1,bg);
  m_configure_b->setFG( 0, fg );
  m_configure_b->setBG( 0, bg );
}

bool Lister::getFocus()
{
  return hasFocus;
}

void Lister::switch2Mode(int nr)
{
  if(activemodeid==nr) return;
  if(activemode!=NULL) activemode->off();
  int j=parent->getMaxModeNr();
  if((nr>=0)&&(nr<j)) {
    activemode=modes[nr];
    activemodeid=nr;
    modes[nr]->on();
  } else {
    activemode=NULL;
    activemodeid=-1;
  }
}

void Lister::setName(const char *str)
{
  lvb->setText(0,str);
  lvb->setText(1,str);
}

void Lister::setStatebarText(const char *str)
{
  parent->setStatebarText(str);
}

void Lister::makeActive()
{
  if(hasFocus==true) return; // Wir sind schon aktiv
  Lister *ol=parent->getOtherLister(this);
  if(ol==NULL) return; // Hoppala, da lief was schief
  ol->setFocus(false);
  setFocus(true);
}

int Lister::getSide()
{
  return parent->getSide(this);
}

bool Lister::isActive()
{
  return hasFocus;
}

void Lister::configure()
{
  int nr,j,i;
  AWindow *win2 = new AWindow( aguix, 10, 10, 10, 10, catalog.getLocale( 16 ), AWindow::AWINDOW_DIALOG );
  const int cincw = AContainer::ACONT_MINH +
                    AContainer::ACONT_MINW +
                    AContainer::ACONT_MAXH;
  const int cincwnr = cincw +
                      AContainer::ACONT_NORESIZE;
  const int cfix = AContainer::ACONT_MINH +
                   AContainer::ACONT_MINW +
                   AContainer::ACONT_MAXH +
                   AContainer::ACONT_MAXW;

  win2->create();

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

  ac1->add( new Text( aguix, 0, 0, catalog.getLocale( 166 ) ), 0, 0, cincwnr );

  nr = parent->getMaxModeNr();

  auto lv = dynamic_cast< FieldListView *>( ac1->add( new FieldListView( aguix,
                                                                         0, 0,
                                                                         100,
                                                                         50,
                                                                         0 ),
                                                      0, 1,
                                                      AContainer::ACONT_MINH +
                                                      AContainer::ACONT_MINW ) );
  lv->setHBarState( 0 );
  lv->setVBarState( 0 );

  for ( i = 0; i < nr; i++ ) {
      int row = lv->addRow();

      lv->setText( row, 0, modes[i]->getLocaleName() );
      lv->setPreColors( row, FieldListView::PRECOLOR_ONLYACTIVE );

      if ( modes[i] == activemode ) lv->setActiveRow( row );
  }
  lv->maximizeX();
  lv->maximizeY();
  ac1->readLimits();

  AContainer *ac1_2 = ac1->add( new AContainer( win2, 3, 1 ), 0, 2 );
  ac1_2->setMinSpace( 5 );
  ac1_2->setMaxSpace( -1 );
  ac1_2->setBorderWidth( 0 );
  Button *okb =(Button*)ac1_2->add( new Button( aguix,
                                                0,
                                                0,
                                                catalog.getLocale( 997 ),
                                                0 ), 0, 0, cfix );
  auto configb = (Button*)ac1_2->add( new Button( aguix,
                                                  0,
                                                  0,
                                                  catalog.getLocale( 998 ),
                                                  0 ), 1, 0, cfix );
  Button *cancelb = (Button*)ac1_2->add( new Button( aguix,
						     0,
						     0,
						     catalog.getLocale( 633 ),
						     0 ), 2, 0, cfix );

  okb->takeFocus();
  win2->setDoTabCycling( true );
  win2->contMaximize( true );
  win2->show();
  AGMessage *msg;
  while((msg=aguix->GetMessage(NULL))!=NULL) aguix->ReplyMessage(msg);
  int ende=0;
  while(ende==0) {
    msg=aguix->WaitMessage(win2);
    if(msg!=NULL) {
      if(msg->type==AG_CLOSEWINDOW) {
        if(msg->closewindow.window==win2->getWindow()) ende=-1;
      } else if(msg->type==AG_BUTTONCLICKED) {
        if(msg->button.button==okb) ende=1;
        else if(msg->button.button==cancelb) ende=-1;
        else if ( msg->button.button == configb ) {
            int row = lv->getActiveRow();

            if ( row >= 0 && row < nr ) {
                modes[row]->configure();
            }
        }
      } else if(msg->type==AG_KEYPRESSED) {
        if ( msg->key.key == XK_Return ) {
            if ( cancelb->getHasFocus() == false ) {
                ende = 1;
            }
        } else if ( msg->key.key == XK_Escape ) {
            ende = -1;
        } else if ( msg->key.key == XK_c ) {
            int row = lv->getActiveRow();

            if ( row >= 0 && row < nr ) {
                modes[row]->configure();
            }
        } else if ( msg->key.key == XK_Up ) {
            int row = lv->getActiveRow();

            if ( row > 0 ) {
                lv->setActiveRow( row - 1 );
            }
        } else if ( msg->key.key == XK_Down ) {
            int row = lv->getActiveRow();

            if ( row + 1 < nr ) {
                lv->setActiveRow( row + 1 );
            }
        } else {
            j = msg->key.key - XK_1;
            if ( j >= 0 && j < nr ) {
                lv->setActiveRow( j );
            }
        }
      }
      aguix->ReplyMessage(msg);
    }
  }
  if ( ende == 1 ) {
      int row = lv->getActiveRow();

      if ( row >= 0 && row < nr ) {
          switch2Mode( row );
      }
  }
  delete win2;
}

Worker *Lister::getWorker()
{
  return parent;
}

void Lister::cyclicfunc(cyclicfunc_mode_t mode)
{
    if ( activemode != NULL ) {
        activemode->cyclicfunc( mode );
    }

    int j = parent->getMaxModeNr();
    for ( int i = 0; i < j; i++ ) {
        if ( modes[i] != NULL && modes[i] != activemode ) {
            modes[i]->cyclicfuncInactive( mode );
        }
    }
}

int Lister::saveState(Datei *fh)
{
  std::string str1;
  int i, j;

  if ( fh == NULL ) return 1;
  
  if ( activemode != NULL ) {
    fh->configPutPairString( "activemode", Worker::getNameOfMode( Worker::getID4Mode( activemode ) ) );
  }
  // Ueber alle Modes: ID speichern und dann den Mode speichern ueberlassen
  j = parent->getMaxModeNr();
  for ( i = 0; i < j; i++ ) {
    // put mode name in quotes so the scanner will return STRING
    str1 = "\"";
    str1 += Worker::getNameOfMode( Worker::getID4Mode( modes[i] ) );
    str1 += "\"";
    fh->configOpenSection( str1.c_str() );
    modes[i]->save( fh );
    fh->configCloseSection();
  }
  return 0;
}

static int load_deprecated_nm_section()
{
    int stop = 0;
    NM_Filter *tfi;
    std::string str1;
    std::list<NM_Filter> tfilters;

    for (; stop == 0; ) {
        switch ( worker_token ) {
            case HIDDENFILES_WCP:
            case SORTBY_WCP:
            case SORTFLAG_WCP:
            case SHOWFREESPACE_WCP:
            case UPDATETIME_WCP:
            case SSHALLOW_WCP:
            case QUICKSEARCHENABLED_WCP:
            case FILTEREDSEARCHENABLED_WCP:
            case WATCHMODE_WCP:
                readtoken();
                readtoken();
                readtoken();
                readtoken();
                break;
            case FIELD_WIDTH_WCP:
                readtoken();
                readtoken();
                readtoken();
                readtoken();
                readtoken();
                readtoken();
                break;
            case FILTER_WCP:
                readtoken();

                if ( worker_token != LEFTBRACE_WCP ) {
                    stop = 1;
                    break;
                }
                readtoken();
      
                tfi = new NM_Filter();
                if ( tfi->load() == 0 ) {
                } else {
                    delete tfi;
                    stop = 1;
                    break;
                }
                delete tfi;

                if ( worker_token != RIGHTBRACE_WCP ) {
                    stop = 1;
                    break;
                }
                readtoken();
                break;
            default:
                stop = 1;
                break;
        }
    }
  
    return 0;
}

int Lister::loadState()
{
  int i, j;
  int found_error = 0;
  int amode = -1;

  j = parent->getMaxModeNr();
  for (;;) {
    if ( worker_token == ACTIVEMODE_WCP ) {
      readtoken();
      
      if ( worker_token != '=' ) {
        found_error = 1;
        break;
      }
      readtoken();
      
      if ( worker_token == STRING_WCP ) {
          // check for old normalmode and assign to vdm
          if ( std::string( yylval.strptr ) == "NormalMode" ) {
              amode = 0;
          } else {
              for ( i = 0; i < j; i++ ) {
                  if ( modes[i]->isType( yylval.strptr ) == true ) {
                      amode = i;
                  }
              }
          }
      } else {
        found_error = 1;
        break;
      }
      readtoken();
      
      if ( worker_token != ';' ) {
        found_error = 1;
        break;
      }
      readtoken();     
    } else if ( worker_token == STRING_WCP ) {
        bool trigger_nm_load = false;

        if ( std::string( yylval.strptr ) == "NormalMode" ) {
            trigger_nm_load = true;
        } else {
            for ( i = 0; i < j; i++ ) {
                if ( modes[i]->isType( yylval.strptr ) == true ) {
                    break;
                }
            }
        }

        readtoken();
      
        if ( i < j || trigger_nm_load ) {
            if ( worker_token != LEFTBRACE_WCP ) {
                found_error = 1;
                break;
            }
            readtoken();

            if ( trigger_nm_load ) {
                load_deprecated_nm_section();
            } else {
                modes[i]->load();
            }
        
            if ( worker_token != RIGHTBRACE_WCP ) {
                found_error = 1;
                break;
            }
            readtoken();
        } else {
            found_error = 1;
            break;
        }
    } else {
      break;
    }
  }
  if ( amode >= 0 ) switch2Mode( amode );
  return found_error;
}

void  Lister::relayout()
{
  int fg,bg;
  if(hasFocus==true) {
      fg = aguix->getFaces().getColor( "lvb-active-fg" );
      bg = aguix->getFaces().getColor( "lvb-active-bg" );
  } else {
      fg = aguix->getFaces().getColor( "lvb-inactive-fg" );
      bg = aguix->getFaces().getColor( "lvb-inactive-bg" );
  }
  lvb->setFG(0,fg);
  lvb->setBG(0,bg);
  lvb->setFG(1,fg);
  lvb->setBG(1,bg);
  lvb->resize( lvb->getWidth(), aguix->getCharHeight() + 4 );
  m_configure_b->resize( aguix->getCharHeight() + 4,
                         aguix->getCharHeight() + 4 );
  m_configure_b->setFG( 0, fg );
  m_configure_b->setBG( 0, bg );
  m_button_cont->readLimits();
  m_button_cont->setMinWidth( 50, getSide() == 0 ? 0 : 1, 0 );
  win->updateCont();

  if(activemode!=NULL) activemode->relayout();
}

const char *Lister::getNameOfMode(int nr)
{
  if((nr<0)||(nr>=parent->getMaxModeNr())) nr=0;
  return modes[nr]->getType();
}

bool Lister::startdnd(DNDMsg *dm)
{
  int j=parent->getMaxModeNr(),i;
  for(i=0;i<j;i++) {
    if(modes[i]->startdnd(dm)==true) break;
  }
  return (i<j)?true:false;
}

void Lister::setContainer( AContainer *mode_cont )
{
    if ( m_mode_cont != NULL ) {
        m_cont->remove( m_mode_cont );
        m_mode_cont = NULL;
    }
    if ( mode_cont != NULL ) {
        m_cont->add( mode_cont, 0, 1 );
        m_mode_cont = mode_cont;
    }
    win->updateCont();
}

bool Lister::pathsChanged( const std::set< std::string > changed_paths )
{
    if ( activemode != NULL ) return activemode->pathsChanged( changed_paths );
    return false;
}

void Lister::finalizeBGOps()
{
    int j = parent->getMaxModeNr();
    for ( int i = 0; i < j; i++ ) {
        modes[i]->finalizeBGOps();
    }
}

void Lister::setLastPath( const std::string &path )
{
    m_last_path = path;
}

std::string Lister::getLastPath() const
{
    return m_last_path;
}
