/* nmextlist.cc
 * This file belongs to Worker, a file manager for UN*X/X11.
 * Copyright (C) 2001-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 "nmextlist.hh"
#include "listermode.h"
#include "functionproto.h"
#include "verzeichnis.hh"
#include "simplelist.hh"
#include "fileentry.hh"
#include "nmexternfe.hh"
#include "dnd.h"
#include "nmspecialsourceext.hh"
#include "datei.h"
#include "nwc_dir.hh"
#include "nwc_file.hh"

NMExtList::NMExtList()
{
  extlist = new List();
  take_dirs = false;
}

NMExtList::~NMExtList()
{
  NM_extern_fe *efe;

  if ( extlist == NULL ) return;
  efe = (NM_extern_fe*)extlist->getFirstElement();
  while ( efe != NULL ) {
    delete efe;
    efe = (NM_extern_fe*)extlist->getNextElement();
  }
  delete extlist;
}

void NMExtList::setTakeDirs( bool nv )
{
  take_dirs = nv;
}

int NMExtList::initEnum()
{
  return extlist->initEnum();
}

void NMExtList::closeEnum( int id )
{
  extlist->closeEnum( id );
}

NM_extern_fe *NMExtList::getFirstElement( int id )
{
    if ( m_list_done ) {
        NM_extern_fe *e = (NM_extern_fe*)extlist->getFirstElement( id );
        if ( e ) {
            m_first_fullname = e->getFullname( false );
        } else {
            m_first_fullname.clear();
        }
        return e;
    }

    if ( ! m_ondemand_mode ) return NULL;

    if ( extlist->size() == 0 ) {
        gatherNextElement();
    }

    NM_extern_fe *e = (NM_extern_fe*)extlist->getFirstElement( id );
    if ( e ) {
        m_first_fullname = e->getFullname( false );
    } else {
        m_first_fullname.clear();
    }
    return e;
}

NM_extern_fe *NMExtList::getNextElement( int id )
{
    if ( m_list_done ) {
        return (NM_extern_fe*)extlist->getNextElement( id );
    }

    if ( ! m_ondemand_mode ) return NULL;

    if ( ! extlist->hasNextElement( id ) ) {
        gatherNextElement();
    }

    return (NM_extern_fe*)extlist->getNextElement( id );
}

int NMExtList::removeElement( NM_extern_fe *efe )
{
  if ( efe == NULL ) return 1;
  extlist->removeElement( efe );
  m_list_modified = true;
  return 0;
}

void NMExtList::createExtList( ListerMode *lm, bool rec )
{
  if ( lm == NULL ) return;

  std::list< NM_specialsourceExt > splist;

  lm->getSelFiles( splist, ListerMode::LM_GETFILES_SELORACT );

  createExtList( splist, rec );

  m_list_done = true;
}

class ExtListDirWalk : public NWC::DirWalkCallBack
{
public:
    ExtListDirWalk( ListerMode *lm,
                    std::list< NM_specialsourceExt > &splist ) : m_listermode( lm ),
                                                                 m_splist( splist )
    {
    }

    int visit( NWC::File &file, NWC::Dir::WalkControlObj &cobj )
    {
        std::unique_ptr< NM_specialsourceExt > ss1;

        FileEntry fe( file );

        ss1 = std::unique_ptr< NM_specialsourceExt >( new NM_specialsourceExt( &fe ) );

        if ( ss1 != NULL ) {
            m_splist.push_back( *ss1 );
        }
        return 0;
    }

    int visitEnterDir( NWC::Dir &dir, NWC::Dir::WalkControlObj &cobj )
    {
        std::unique_ptr< NM_specialsourceExt > ss1;

        FileEntry fe( dir );

        ss1 = std::unique_ptr< NM_specialsourceExt >( new NM_specialsourceExt( &fe ) );

        if ( ss1 != NULL ) {
            m_splist.push_back( *ss1 );
        }
        return 0;
    }
    int visitLeaveDir( NWC::Dir &dir, NWC::Dir::WalkControlObj &cobj ) { return 0; }
    int prepareDirWalk( const NWC::Dir &dir, NWC::Dir::WalkControlObj &cobj,
                        std::list< RefCount< NWC::FSEntry > > &return_add_entries,
                        std::list< RefCount< NWC::FSEntry > > &return_skip_entries ) { return 0; }
private:
    ListerMode *m_listermode;
    std::list< NM_specialsourceExt > &m_splist;
};

/*
 * createExtList
 *
 * will create a list of all involved entries depending on what type
 * of message is given.
 * At the moment it handles SPECIAL and DND
 */
void NMExtList::createExtList( ListerMode *lm, ActionMessage *msg, bool rec )
{
  if ( lm == NULL ) return;

  std::list< NM_specialsourceExt > splist;
  std::unique_ptr<NM_specialsourceExt> ss1;
  const DNDMsg *dm;

  // mark as done for now, undo if necessary
  m_list_done = true;

  switch ( msg->mode ) {
    case ActionMessage::AM_MODE_SPECIAL:
        if ( msg->getFE() ) {
            ss1 = std::unique_ptr< NM_specialsourceExt >( new NM_specialsourceExt( msg->getFE() ) );

            if ( ss1 != NULL ) {
                splist.push_back( *ss1 );
            }
        }
        break;
    case ActionMessage::AM_MODE_SPECIFIC_ENTRIES:
        {
            RefCount< NWC::VirtualDir > vd = msg->getEntriesToConsider();
            if ( vd.get() != NULL ) {
                ExtListDirWalk w( lm, splist );
                vd->walk( w );
            }
        }
        break;
    case ActionMessage::AM_MODE_DNDACTION:
        dm = msg->dndmsg;

        if ( dm->getFE() ) {
            ss1 = std::unique_ptr< NM_specialsourceExt >( new NM_specialsourceExt( dm->getFE() ) );
            if ( ss1 != NULL ) {
                splist.push_back( *ss1 );
            }
        }
      break;
    case ActionMessage::AM_MODE_ON_DEMAND:
        m_ondemand_mode = true;
        m_ondemand_lm = lm;
        m_ondemand_rec = rec;

        // in on-demand mode, the list is not done yet
        m_list_done = false;
        break;
    case ActionMessage::AM_MODE_ONLYACTIVE:
      break;
    default:
      break;
  }

  createExtList( splist, rec );
}

int NMExtList::create_externfe_rec( const char *fullname, bool rec, const FileEntry *fe )
{
  NM_extern_fe *efe;
  Verzeichnis *tverz;
  
  if((Datei::fileExistsExt(fullname)==Datei::D_FE_DIR)&&(rec==true)) {
    // create all subentries
    tverz=new Verzeichnis();
    if ( tverz->readDir( fullname ) == 0 && tverz->dirOpened() == true ) {
        for ( Verzeichnis::verz_it tfe_it1 = tverz->begin();
              tfe_it1 != tverz->end();
              tfe_it1++ ) {
            FileEntry *tfe = *tfe_it1;
            if ( strcmp( tfe->name, ".." ) != 0 ) {
                create_externfe_rec( tfe->fullname, rec, NULL );
            }
        }
    }
    delete tverz;
    // now append the dir
    efe = new NM_extern_fe( fullname, fe);
    efe->setDirFinished(1);  // this indicates the end of the recursive dir
                             // so it can be deactivated in the LV
    extlist->addElement(efe);
  } else {
    efe = new NM_extern_fe( fullname, fe );
    extlist->addElement(efe);
  }
  return 0;
}

int NMExtList::createExtList( std::list< NM_specialsourceExt > &splist, bool rec )
{
    for ( auto &ss1 : splist ) {
        if ( ss1.entry() ) {
            create_externfe_rec( ss1.entry()->fullname, rec, ss1.entry() );
        }
    }

    return 0;
}

void NMExtList::resetModifedFlag()
{
    m_list_modified = false;
}

bool NMExtList::modified() const
{
    return m_list_modified;
}

bool NMExtList::empty()
{
    if ( extlist->size() != 0 ) return false;

    if ( m_list_done ) return true;

    gatherNextElement();

    return extlist->size() == 0;
}

void NMExtList::gatherNextElement()
{
    if ( ! m_ondemand_mode ) return;
    if ( m_list_done ) return;

    if ( ! m_ondemand_lm ) return;

    if ( m_ondemand_state_counter == 0 ) {
        // get active
        std::list< NM_specialsourceExt > splist;

        m_ondemand_lm->getSelFiles( splist, ListerMode::LM_GETFILES_ONLYACTIVE );

        for ( auto &ss1 : splist ) {
            if ( ss1.entry() ) {
                create_externfe_rec( ss1.entry()->fullname,
                                     m_ondemand_rec,
                                     ss1.entry() );
                m_ondemand_active_entry = ss1.entry()->fullname;
            }
        }

    } else {
        // get rest, filter active
        std::list< NM_specialsourceExt > splist;

        m_ondemand_lm->getSelFiles( splist, ListerMode::LM_GETFILES_ONLYSELECT );

        for ( auto &ss1 : splist ) {
            if ( ss1.entry() ) {
                if ( m_ondemand_active_entry != ss1.entry()->fullname ) {
                    create_externfe_rec( ss1.entry()->fullname,
                                         m_ondemand_rec,
                                         ss1.entry() );
                }
            }
        }

        m_list_done = true;
    }

    m_ondemand_state_counter++;
}

const std::string &NMExtList::getFirstFullname() const
{
    return m_first_fullname;
}
