/* filereq.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2001-2014 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 "filereq.h"
#include "worker_locale.h"
#include "aguix/aguix.h"
#include "aguix/awindow.h"
#include "aguix/button.h"
#include "aguix/text.h"
#include "aguix/message.h"
#include "aguix/util.h"
#include "aguix/fieldlistview.h"
#include "aguix/stringgadget.h"
#include "aguix/request.h"
#include "verzeichnis.hh"
#include "fileentry.hh"
#include "simplelist.hh"
#include "datei.h"
#include "nwc_path.hh"

FileRequester::FileRequester(AGUIX *parent)
{
  aguix=parent;
  selfes=NULL;
  actdir=NULL;
  verz=NULL;
  last_entry_str = "";
}

FileRequester::~FileRequester()
{
  reset();
  if(actdir!=NULL) _freesafe(actdir);
  if(verz!=NULL) delete verz;
}

int FileRequester::request( const char *title,
                            const char *dir,
                            const char *oktext,
                            const char *canceltext,
                            const char *infotext)
{
  if((title==NULL)||(dir==NULL)||(oktext==NULL)||(canceltext==NULL)||(infotext==NULL)) return -1;
  if((actdir==NULL)&&(strlen(dir)==0)) return -1;
  reset();
  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;
  const int cmin = AContainer::ACONT_MINH +
                   AContainer::ACONT_MINW;

  AWindow *win = new AWindow( aguix, 10, 10, 300, 200, title, AWindow::AWINDOW_DIALOG );
  win->create();

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

  ac1->add( new Text( aguix, 0, 0, infotext ), 0, 0, cincwnr );

  AContainer *ac1_1 = ac1->add( new AContainer( win, 1, 2 ), 0, 1 );
  ac1_1->setMinSpace( 0 );
  ac1_1->setMaxSpace( 0 );
  ac1_1->setBorderWidth( 0 );

  lv = (FieldListView*)ac1_1->add( new FieldListView( aguix, 0, 0, 200, 150, 0 ), 0, 0, cmin );
  lv->setHBarState(2);
  lv->setVBarState(2);

  sg = (StringGadget*)ac1_1->add( new StringGadget( aguix, 0, 0, 100, dir, 0 ), 0, 1, cincw );

  AContainer *ac1_2 = ac1->add( new AContainer( win, 2, 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( 11 ),
                                                0 ), 0, 0, cfix );
  Button *cnb = (Button*)ac1_2->add( new Button( aguix,
						0,
						0,
						catalog.getLocale( 8 ),
						0 ), 1, 0, cfix );
  win->setDoTabCycling( true );
  win->contMaximize( true );
  win->show();
  AGMessage *msg;
  verz=new Verzeichnis();
  if(strlen(dir)>0) {
    setDir(dir);
  } else {
    setDir(actdir);
  }
  int endmode=-1;
  int lastrow = -1;
  Time lasttime=0;
  for(;endmode==-1;) {
    msg=aguix->WaitMessage(win);
    if(msg!=NULL) {
      switch(msg->type) {
        case AG_CLOSEWINDOW:
          if(msg->closewindow.window==win->getWindow()) endmode=0;
          break;
        case AG_BUTTONCLICKED:
          if(msg->button.button==okb) endmode=1;
          else if(msg->button.button==cnb) endmode=0;
          break;
        case AG_STRINGGADGET_DEACTIVATE:
          if(msg->stringgadget.sg==sg) {
            setDir(sg->getText());
          }
          break;
        case AG_FIELDLV_ONESELECT:
          if ( msg->fieldlv.lv == lv ) {
            if ( lv->isValidRow( lastrow ) == true  ) {
              if ( msg->fieldlv.row == lastrow ) {
                // eventl. Doppelklick
                if ( aguix->isDoubleClick( msg->fieldlv.time, lasttime ) == true ) {
                  
                  int p = lastrow;
                  Verzeichnis::verz_it fe_it1;

                  for ( fe_it1 = verz->begin();
                        fe_it1 != verz->end() && p > 0;
                        fe_it1++, p-- );

                  if ( fe_it1 != verz->end() ) {
                    FileEntry *fe = *fe_it1;
                    if ( fe->isDir() == true ) {
                      char *tstr = (char*)_allocsafe( strlen( actdir ) + 1 + strlen( fe->name ) + 2 );
                      sprintf( tstr, "%s/%s", actdir, fe->name );
                      setDir( tstr );
                      _freesafe( tstr );
                    } else {
                      endmode = 2;
                    }
                  }
                }
                if ( endmode == -1 ) {
                  lastrow = -1;
                  lasttime = 0;
                }
              } else {
                lastrow = msg->fieldlv.row;
                lasttime = msg->fieldlv.time;
              }
            } else {
              lastrow = msg->fieldlv.row;
              lasttime = msg->fieldlv.time;
            }
          }
          break;
        case AG_FIELDLV_MULTISELECT:
          if ( msg->fieldlv.lv == lv ) {
            lastrow = -1;
            lasttime = 0;
          }
          break;
      }
      aguix->ReplyMessage(msg);
    }
  }
  if(endmode==1) {
    // Ok
    selfes=new List();

    Verzeichnis::verz_it fe_it1 = verz->begin();
    if ( fe_it1 != verz->end() ) {
        fe_it1++;
        int row = 1;

        while ( fe_it1 != verz->end() ) {
            if ( lv->getSelect( row ) == true ) {
                selfes->addElement( (*fe_it1)->duplicate() );
            }
            fe_it1++;
            row++;
        }
    }
  } else if(endmode==2) {
    selfes=new List();

    Verzeichnis::verz_it fe_it1 = verz->begin();
    if ( fe_it1 != verz->end() ) {
        fe_it1++;
        int row = 1;

        while ( fe_it1 != verz->end() ) {
            if ( row == lastrow ) {
                selfes->addElement( (*fe_it1)->duplicate() );
                break;
            }
            fe_it1++;
            row++;
        }
    }
    
    endmode=1;
  }
  delete win;
  delete verz;
  verz=NULL;
  return endmode;
}

void FileRequester::reset()
{
  if(selfes!=NULL) {
    FileEntry *fe=(FileEntry*)selfes->getFirstElement();
    while(fe!=NULL) {
      delete fe;
      fe=(FileEntry*)selfes->getNextElement();
    }
    delete selfes;
    selfes=NULL;
  }
#if 0
  if ( last_entry_str != NULL ) {
    _freesafe( last_entry_str );
    last_entry_str = NULL;
  }
#else
  last_entry_str = "";
#endif
}

void FileRequester::setDir(const char *dir)
{
  char *tstr=NWC::Path::handlePath(dir);
  int row;
  
  verz->closeDir();
  if(verz->readDir(tstr)!=0) {
    if(actdir!=NULL) {
      if(verz->readDir(actdir)!=0) return;
    } else verz->readDir("/");
  }
  _freesafe(tstr);
  tstr=dupstring(verz->getDir());
  if(actdir!=NULL) _freesafe(actdir);
  actdir=tstr;
  sg->setText(actdir);
  verz->sort(0);

  lv->setSize( 0 );
  
  for ( Verzeichnis::verz_it fe_it1 = verz->begin();
        fe_it1 != verz->end();
        fe_it1++ ) {
    FileEntry *fe = *fe_it1;
    row = lv->addRow();
    lv->setText( row, 0, fe->name );
    if(fe->isDir()==false) {
      lv->setPreColors( row, FieldListView::PRECOLOR_ONLYSELECT );
    } else {
      lv->setFG( row, FieldListView::CC_NORMAL, 3 );
      lv->setFG( row, FieldListView::CC_SELECT, 2 );
      lv->setFG( row, FieldListView::CC_ACTIVE, 3 );
      lv->setFG( row, FieldListView::CC_SELACT, 2 );
      lv->setBG( row, FieldListView::CC_NORMAL, 0 );
      lv->setBG( row, FieldListView::CC_SELECT, 3 );
      lv->setBG( row, FieldListView::CC_ACTIVE, 0 );
      lv->setBG( row, FieldListView::CC_SELACT, 3 );
    }
  }
  lv->redraw();
}

FileEntry *FileRequester::getFirstFE()
{
  if(selfes==NULL) return NULL;
  return (FileEntry*)selfes->getFirstElement();
}

FileEntry *FileRequester::getNextFE()
{
  if(selfes==NULL) return NULL;
  return (FileEntry*)selfes->getNextElement();
}


#define FR_RE_TESTOKAY \
{ \
  bool okay = false; \
  int trow2; \
  if ( sg2 != NULL ) { \
    if ( strlen( sg2->getText() ) > 0 ) { \
      endmode = 1; \
      okay = true; \
    } \
  } else { \
    trow2 = lv->getActiveRow(); \
    if ( lv->isValidRow( trow2 ) == true ) { \
      if ( lv->getData( trow2, 0 ) == 0 ) {           \
        if ( lv->getText( trow2, 0 ).length() > 0 ) { \
          endmode = 1; \
          okay = true; \
        } \
      } \
    } \
  } \
  if ( okay == false ) { \
    req->request( catalog.getLocale( 124 ), \
                  ( allowEnterName == true ) ? "Please select a file or enter a name!" : "Please select a file!", \
                  catalog.getLocale( 11 ) ); \
  } \
}

/*
 * Works like normal request but only allow to select one entry
 * when allowEnterName is true, the user can enter an own name
 * ( e.g. for file creation)
 */
int FileRequester::request_entry( const char *title,
                                  const char *dir,
                                  const char *oktext,
                                  const char *canceltext,
                                  const char *infotext,
                                  bool allowEnterName )
{
  AWindow *win;
  AGMessage *msg;
  int endmode=-1;
  StringGadget *sg2;
  char *use_dir = NULL;
  int lastrow = -1, trow;
  Time lasttime=0;
  Requester *req;
  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;
  const int cmin = AContainer::ACONT_MINH +
                   AContainer::ACONT_MINW;
  
  if ( ( title == NULL ) ||
       ( oktext == NULL ) ||
       ( canceltext == NULL ) ||
       ( infotext == NULL ) ) return -1;

  reset();

  if ( actdir == NULL ) {
    if ( dir == NULL ) {
      use_dir = NWC::Path::handlePathExt( "." );
      if ( use_dir == NULL ) {
        use_dir = dupstring( "/" );
      }
    } else {
      if ( Datei::fileExistsExt( dir ) == Datei::D_FE_DIR ) {
        use_dir = dupstring( dir );
      } else {
        use_dir = dupstring( "/" );
      }
    }
  } else use_dir = dupstring( actdir );

  win = new AWindow( aguix, 10, 10, 300, 200, title, AWindow::AWINDOW_DIALOG );
  win->create();

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

  ac1->add( new Text( aguix, 0, 0, infotext ), 0, 0, cincwnr );
  
  AContainer *ac1_1 = ac1->add( new AContainer( win, 1, 3 ), 0, 1 );
  ac1_1->setMinSpace( 0 );
  ac1_1->setMaxSpace( 0 );
  ac1_1->setBorderWidth( 0 );

  sg = (StringGadget*)ac1_1->add( new StringGadget( aguix, 0, 0, 100, use_dir, 0 ), 0, 0, cincw );

  lv = (FieldListView*)ac1_1->add( new FieldListView( aguix, 0, 0, 200, 250, 0 ), 0, 1, cmin );
  lv->setHBarState( 2 );
  lv->setVBarState( 2 );
  
  if ( allowEnterName == true ) {
    sg2 = (StringGadget*)ac1_1->add( new StringGadget( aguix, 0, 0, 100, "", 0 ), 0, 2, cincw );
  } else {
    sg2 = NULL;
    ac1_1->setMinHeight( 0, 0, 2 );
    ac1_1->setMaxHeight( 0, 0, 2 );
  }
  
  AContainer *ac1_2 = ac1->add( new AContainer( win, 2, 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( 11 ),
                                                0 ), 0, 0, cfix );
  Button *cb = (Button*)ac1_2->add( new Button( aguix,
						0,
						0,
						catalog.getLocale( 8 ),
						0 ), 1, 0, cfix );

  okb->takeFocus();
  win->setDoTabCycling( true );
  win->contMaximize( true );
  win->show();
  
  verz = new Verzeichnis();
  setDirOneSelect( use_dir );
  _freesafe( use_dir );

  req = new Requester( aguix );

  for( ; endmode == -1; ) {
    msg = aguix->WaitMessage( win );
    if( msg != NULL ) {
      switch ( msg->type ) {
        case AG_CLOSEWINDOW:
          if ( msg->closewindow.window == win->getWindow() ) endmode = 0;
          break;
        case AG_BUTTONCLICKED:
          if ( msg->button.button == okb ) {
            FR_RE_TESTOKAY;
          } else if ( msg->button.button == cb ) endmode = 0;
          break;
        case AG_STRINGGADGET_DEACTIVATE:
          if ( msg->stringgadget.sg == sg ) {
            setDirOneSelect( sg->getText() );
            //if ( sg2 != NULL ) sg2->setText( "" );
          } else if ( msg->stringgadget.sg == sg2 ) {
            trow = lv->getActiveRow();
            if ( lv->isValidRow( trow ) == true ) {
              lv->setActiveRow( -1 );
            }
          }
          break;
        case AG_FIELDLV_ONESELECT:
          if ( msg->fieldlv.lv == lv ) {
            if ( lv->isValidRow( lastrow ) == true ) {
              if ( msg->fieldlv.row == lastrow ) {
                // eventl. Doppelklick
                if ( aguix->isDoubleClick( msg->fieldlv.time, lasttime ) == true ) {
                  
                  int p = lastrow;
                  Verzeichnis::verz_it fe_it1;

                  for ( fe_it1 = verz->begin();
                        fe_it1 != verz->end() && p > 0;
                        fe_it1++, p-- );

                  if ( fe_it1 != verz->end() ) {
                    FileEntry *fe = *fe_it1;

                    if ( fe->isDir() == true ) {
                      char *tstr = (char*)_allocsafe( strlen( actdir ) + 1 + strlen( fe->name ) + 2 );
                      sprintf( tstr, "%s/%s", actdir, fe->name );
                      setDirOneSelect( tstr );
                      _freesafe( tstr );
                      //if ( sg2 != NULL ) sg2->setText( "" );
                      lastrow = -1;
                      lasttime = 0;
                    } else {
                      endmode = 2;
                    }
                  } else {
                    lastrow = -1;
                    lasttime = 0;
                  }
                } else {
                  lastrow = msg->fieldlv.row;
                  lasttime = msg->fieldlv.time;
                }
              } else {
                lastrow = msg->fieldlv.row;
                lasttime = msg->fieldlv.time;
              }
            } else {
              lastrow = msg->fieldlv.row;
              lasttime = msg->fieldlv.time;
            }
            if ( lv->isValidRow( lastrow ) == true ) {
              if ( lv->getData( lastrow, 0 ) == 0 ) {
                // change sg2-text only for selected files
                if ( sg2 != NULL ) sg2->setText( lv->getText( lastrow, 0 ).c_str() );
              }
            }
          }
          break;
        case AG_FIELDLV_MULTISELECT:
          if ( msg->fieldlv.lv == lv ) {
            lastrow = -1;
            lasttime = 0;
            
            trow = lv->getActiveRow();
            if ( lv->isValidRow( trow ) == true ) {
              if ( lv->getData( trow, 0 ) == 0 ) {
                // change sg2-text only for selected files
                if ( sg2 != NULL ) sg2->setText( lv->getText( trow, 0 ).c_str() );
              }
            }
          }
          break;
        case AG_KEYPRESSED:
          if ( win->isParent( msg->key.window, false ) == true ) {
            switch ( msg->key.key ) {
              case XK_KP_Enter:
              case XK_Return:
                if ( cb->getHasFocus() == false ) {
                  FR_RE_TESTOKAY;
                }
                break;
              case XK_Escape:
                endmode = 0;
                break;
            }
          }
          break;
      }
      aguix->ReplyMessage( msg );
    }
  }
  if ( ( endmode == 1 ) || ( endmode == 2 ) ) {
    // okay or doubleclick
#if 0
    char *tstr = (char*)_allocsafe( strlen( actdir ) + 1 + strlen( sg2->getText() ) + 1 );
    if ( last_entry_str != NULL ) _freesafe( last_entry_str );
    last_entry_str = tstr;
#else
    last_entry_str = actdir;
    last_entry_str += "/";
    if ( sg2 != NULL ) {
      last_entry_str += sg2->getText();
    } else {
      trow = lv->getActiveRow();
      if ( lv->isValidRow( trow ) == true ) {
        if ( lv->getText( trow, 0 ).length() > 0 ) {
          if ( lv->getData( trow, 0 ) == 0 ) {
            last_entry_str += lv->getText( trow, 0 );
          } else last_entry_str = "";
        } else last_entry_str = "";
      } else last_entry_str = "";
    }
#endif
  }

  delete win;
  delete verz;
  verz = NULL;
  
  delete req;
  
  return endmode;
}

#undef FR_RE_TESTOKAY

void FileRequester::setDirOneSelect( const char *dir )
{
  char *tstr=NWC::Path::handlePath( dir );
  int row;
  
  verz->closeDir();
  if(verz->readDir(tstr)!=0) {
    if(actdir!=NULL) {
      if(verz->readDir(actdir)!=0) return;
    } else verz->readDir("/");
  }
  _freesafe(tstr);
  tstr=dupstring(verz->getDir());
  if(actdir!=NULL) _freesafe(actdir);
  actdir=tstr;
  sg->setText(actdir);
  verz->sort(0);

  lv->setSize( 0 );
  
  for ( Verzeichnis::verz_it fe_it1 = verz->begin();
        fe_it1 != verz->end();
        fe_it1++ ) {
    FileEntry *fe = *fe_it1;
    row = lv->addRow();
    lv->setText( row, 0, fe->name );
    if(fe->isDir()==false) {
      lv->setPreColors( row, FieldListView::PRECOLOR_ONLYACTIVE );
      lv->setData( row, 0 );
    } else {
      lv->setFG( row, FieldListView::CC_NORMAL, 3 );
      lv->setFG( row, FieldListView::CC_SELECT, 3 );
      lv->setFG( row, FieldListView::CC_ACTIVE, 2 );
      lv->setFG( row, FieldListView::CC_SELACT, 2 );
      lv->setBG( row, FieldListView::CC_NORMAL, 0 );
      lv->setBG( row, FieldListView::CC_SELECT, 0 );
      lv->setBG( row, FieldListView::CC_ACTIVE, 3 );
      lv->setBG( row, FieldListView::CC_SELACT, 3 );
      lv->setData( row, 1 );
    }
  }
  lv->redraw();
}

std::string FileRequester::getLastEntryStr()
{
  return last_entry_str;
}

