/***************************************************************************
    smb4kcore  -  The main core class of Smb4K.
                             -------------------
    begin                : Do Apr 8 2004
    copyright            : (C) 2004-2008 by Alexander Reinholdt
    email                : dustpuppy@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *   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., 59 Temple Place, Suite 330, Boston,   *
 *   MA  02111-1307 USA                                                    *
 ***************************************************************************/

// Qt includes
#include <QDir>
#include <QMap>
#include <QStringList>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDesktopWidget>

// KDE includes
#include <klocale.h>
#include <kurl.h>
#include <krun.h>
#include <kapplication.h>
#include <kdebug.h>
#include <kshell.h>
#include <kstandarddirs.h>
#include <kglobal.h>

// system includes
#include <stdlib.h>

// application specific includes
#include <smb4kcore.h>
#include <smb4kdefs.h>
#include <smb4kcoremessage.h>
#include <smb4kglobal.h>
#include <smb4ksambaoptionshandler.h>
#include <smb4ksettings.h>
#include <smb4kworkgroup.h>
#include <smb4khost.h>
#include <smb4kshare.h>

using namespace Smb4KGlobal;

// FIXME: Move to own files later.

class Smb4KCorePrivate
{
  public:
    /**
     * The Smb4KCore instance
     */
    Smb4KCore instance;
};

K_GLOBAL_STATIC( Smb4KCorePrivate, m_priv );


Smb4KCore::Smb4KCore() : QObject()
{
  // Set default values for settings that depend on the system
  // Smb4K is running on:
  setDefaultSettings();

  // Search for the programs that are needed by Smb4K:
  searchPrograms();

  m_scanner =         new Smb4KScanner( this );
  m_mounter =         new Smb4KMounter( this );
  m_bookmarkHandler = new Smb4KBookmarkHandler( this );
  m_print =           new Smb4KPrint( this );
  m_synchronizer =    new Smb4KSynchronizer( this );
  m_previewer =       new Smb4KPreviewer( this );
  m_search =          new Smb4KSearch( this );

  m_scanner_state =   SCANNER_STOP;
  m_mounter_state =   MOUNTER_STOP;
  m_print_state =     PRINT_STOP;
  m_syncer_state =    SYNCHRONIZER_STOP;
  m_previewer_state = PREVIEWER_STOP;
  m_search_state =    SEARCH_STOP;

  // Connections:
  connect( m_scanner,         SIGNAL( state( int ) ),
           this,              SLOT( slotSetScannerState( int ) ) );

  connect( m_mounter,         SIGNAL( state( int ) ),
           this,              SLOT( slotSetMounterState( int ) ) );

  connect( m_print,           SIGNAL( state( int ) ),
           this,              SLOT( slotSetPrinterHandlerState( int ) ) );

  connect( m_synchronizer,    SIGNAL( state( int ) ),
           this,              SLOT( slotSetSynchronizerState( int ) ) );

  connect( m_previewer,       SIGNAL( state( int ) ),
           this,              SLOT( slotSetPreviewerState( int ) ) );

  connect( m_search,          SIGNAL( state( int ) ),
           this,              SLOT( slotSetSearchState( int ) ) );

  connect( kapp,              SIGNAL( aboutToQuit() ),
           this,              SLOT( slotAboutToQuit() ) );
}


Smb4KCore::~Smb4KCore()
{
  // Do not call abort() here. This will most likely lead
  // to crashes.
}


Smb4KCore *Smb4KCore::self()
{
  return &m_priv->instance;
}


/****************************************************************************
   Returns a bool that tells the program whether a core process is running.
****************************************************************************/

bool Smb4KCore::isRunning()
{
  if ( self()->m_scanner->isRunning() || self()->m_mounter->isRunning() ||
       self()->m_print->isRunning() || self()->m_synchronizer->isRunning() ||
       self()->m_previewer->isRunning() || self()->m_search->isRunning() )
  {
    return true;
  }
  else
  {
    return false;
  }
}


/****************************************************************************
   Sets  the current state of the core.
****************************************************************************/

void Smb4KCore::setCurrentState( int state )
{
  if ( state != SCANNER_STOP && state != MOUNTER_STOP &&
       state != PRINT_STOP && state != SYNCHRONIZER_STOP &&
       state != PREVIEWER_STOP && state != SEARCH_STOP )
  {
    m_current_state = state;
  }
  else
  {
    if ( !m_scanner->isRunning() && !m_mounter->isRunning() &&
         !m_print->isRunning() && !m_synchronizer->isRunning() &&
         !m_previewer->isRunning() && !m_search->isRunning() )
    {
      m_current_state = CORE_STOP;
    }
    else
    {
      if ( m_scanner->isRunning() )
      {
        m_current_state = m_scanner_state;
      }
      else if ( m_print->isRunning() )
      {
        m_current_state = m_print_state;
      }
      else if ( m_mounter->isRunning() )
      {
        m_current_state = m_mounter_state;
      }
      else if ( m_synchronizer->isRunning() )
      {
        m_current_state = m_syncer_state;
      }
      else if ( m_previewer->isRunning() )
      {
        m_current_state = m_previewer_state;
      }
      else if ( m_search->isRunning() )
      {
        m_current_state = m_search_state;
      }
      else
      {
        // Do nothing
      }
    }
  }
}


/****************************************************************************
   Aborts any process of the core.
****************************************************************************/

void Smb4KCore::abort()
{
  self()->m_scanner->abort();
  self()->m_mounter->abort();
  self()->m_print->abort();
  self()->m_synchronizer->abort();
  self()->m_previewer->abort();
  self()->m_search->abort();
}


/****************************************************************************
   Opens the given URL.
****************************************************************************/

void Smb4KCore::open( Smb4KShare *share, OpenWith openWith )
{
  if ( !share || share->isInaccessible() )
  {
    return;
  }

  switch ( openWith )
  {
    case FileManager:
    {
      KUrl url;
      url.setPath( share->canonicalPath() );

      (void) new KRun( url, 0, 0, true );

      break;
    }
    case Konsole:
    {
      if ( Smb4KSettings::konsole().isEmpty() )
      {
        Smb4KCoreMessage::error( ERROR_COMMAND_NOT_FOUND, "konsole" );
      }
      else
      {
        KRun::runCommand( "konsole --workdir "+KShell::quoteArg( share->canonicalPath() ), 0 );
      }

      break;
    }
    default:
    {
      break;
    }
  }
}


void Smb4KCore::init()
{
  scanner()->init();
  mounter()->init();
}


/****************************************************************************
   Searches for the programs needed by Smb4K
****************************************************************************/

void Smb4KCore::searchPrograms()
{
  // Remove the group "Programs" and reread the
  // configuration:
  Smb4KSettings::self()->config()->deleteGroup( "Programs" );
  Smb4KSettings::self()->readConfig();

  QStringList missing;
  QString program;

  // Find mandatory executables
  QStringList paths;

  if ( !(program = KStandardDirs::findExe( "grep" )).isEmpty() )
  {
    Smb4KSettings::self()->grepItem()->setDefaultValue( program );

    if ( Smb4KSettings::grep().isEmpty() )
    {
      Smb4KSettings::self()->grepItem()->setDefault();
    }
    else
    {
      // Do nothing
    }

    program.clear();
  }
  else
  {
    missing << "grep";
  }

  if ( !(program = KStandardDirs::findExe( "awk" )).isEmpty() )
  {
    Smb4KSettings::self()->awkItem()->setDefaultValue( program );

    if ( Smb4KSettings::awk().isEmpty() )
    {
      Smb4KSettings::self()->awkItem()->setDefault();
    }
    else
    {
      // Do nothing
    }

    program.clear();
  }
  else
  {
    missing << "awk";
  }

  if ( !(program = KStandardDirs::findExe( "sed" )).isEmpty() )
  {
    Smb4KSettings::self()->sedItem()->setDefaultValue( program );

    if ( Smb4KSettings::sed().isEmpty() )
    {
      Smb4KSettings::self()->sedItem()->setDefault();
    }
    else
    {
      // Do nothing
    }

    program.clear();
  }
  else
  {
    missing << "sed";
  }

  if ( !(program = KStandardDirs::findExe( "xargs" )).isEmpty() )
  {
    Smb4KSettings::self()->xargsItem()->setDefaultValue( program );

    if ( Smb4KSettings::xargs().isEmpty() )
    {
      Smb4KSettings::self()->xargsItem()->setDefault();
    }
    else
    {
      // Do nothing
    }

    program.clear();
  }
  else
  {
    missing << "xargs";
  }

  if ( !(program = KStandardDirs::findExe( "rmdir" )).isEmpty() )
  {
    Smb4KSettings::self()->rmdirItem()->setDefaultValue( program );

    if ( Smb4KSettings::rmdir().isEmpty() )
    {
      Smb4KSettings::self()->rmdirItem()->setDefault();
    }
    else
    {
      // Do nothing
    }

    program.clear();
  }
  else
  {
    missing << "rmdir";
  }

  if ( !(program = KStandardDirs::findExe( "nmblookup" )).isEmpty() )
  {
    Smb4KSettings::self()->nmblookupItem()->setDefaultValue( program );

    if ( Smb4KSettings::nmblookup().isEmpty() )
    {
      Smb4KSettings::self()->nmblookupItem()->setDefault();
    }
    else
    {
      // Do nothing
    }

    program.clear();
  }
  else
  {
    missing << "nmblookup";
  }

  if ( !(program = KStandardDirs::findExe( "smbclient" )).isEmpty() )
  {
    Smb4KSettings::self()->smbclientItem()->setDefaultValue( program );

    if ( Smb4KSettings::smbclient().isEmpty() )
    {
      Smb4KSettings::self()->smbclientItem()->setDefault();
    }
    else
    {
      // Do nothing
    }

    program.clear();
  }
  else
  {
    missing << "smbclient";
  }

  if ( !(program = KStandardDirs::findExe( "smbspool" )).isEmpty() )
  {
    Smb4KSettings::self()->smbspoolItem()->setDefaultValue( program );

    if ( Smb4KSettings::smbspool().isEmpty() )
    {
      Smb4KSettings::self()->smbspoolItem()->setDefault();
    }
    else
    {
      // Do nothing
    }

    program.clear();
  }
  else
  {
    missing << "smbspool";
  }

  if ( !(program = KStandardDirs::findExe( "smbtree" )).isEmpty() )
  {
    Smb4KSettings::self()->smbtreeItem()->setDefaultValue( program );

    if ( Smb4KSettings::smbtree().isEmpty() )
    {
      Smb4KSettings::self()->smbtreeItem()->setDefault();
    }
    else
    {
      // Do nothing
    }

    program.clear();
  }
  else
  {
    missing << "smbtree";
  }

  if ( !(program = KStandardDirs::findExe( "net" )).isEmpty() )
  {
    Smb4KSettings::self()->netItem()->setDefaultValue( program );

    if ( Smb4KSettings::net().isEmpty() )
    {
      Smb4KSettings::self()->netItem()->setDefault();
    }
    else
    {
      // Do nothing
    }

    program.clear();
  }
  else
  {
    missing << "net";
  }

#ifdef __FreeBSD__
  if ( !(program = KStandardDirs::findExe( "smbutil" )).isEmpty() )
  {
    Smb4KSettings::self()->smbutilItem()->setDefaultValue( program );

    if ( Smb4KSettings::smbutil().isEmpty() )
    {
      Smb4KSettings::self()->smbutilItem()->setDefault();
    }
    else
    {
      // Do nothing
    }

    program.clear();
  }
  else
  {
    // Try some sbin directories before giving up:
    if ( !(program = KStandardDirs::findExe( "smbutil", "/sbin" )).isEmpty() ||
         !(program = KStandardDirs::findExe( "smbutil", "/usr/sbin" )).isEmpty() ||
         !(program = KStandardDirs::findExe( "smbutil", "/usr/local/sbin" )).isEmpty() )
    {
      Smb4KSettings::self()->smbutilItem()->setDefaultValue( program );

      if ( Smb4KSettings::smbutil().isEmpty() )
      {
        Smb4KSettings::self()->smbutilItem()->setDefault();
      }
      else
      {
        // Do nothing
      }

      program.clear();
    }
    else
    {
      missing << "smbutil";
    }
  }
#endif

  if ( !(program = KGlobal::dirs()->findResource( "exe", "smb4k_mount" )).isEmpty() )
  {
    Smb4KSettings::self()->smb4k_mountItem()->setDefaultValue( program );

    if ( Smb4KSettings::smb4k_mount().isEmpty() )
    {
      Smb4KSettings::self()->smb4k_mountItem()->setDefault();
    }
    else
    {
      // Do nothing
    }

    program.clear();
  }
  else
  {
    missing << "smb4k_mount";
  }

  if ( !(program = KGlobal::dirs()->findResource( "exe", "smb4k_umount" )).isEmpty() )
  {
    Smb4KSettings::self()->smb4k_umountItem()->setDefaultValue( program );

    if ( Smb4KSettings::smb4k_umount().isEmpty() )
    {
      Smb4KSettings::self()->smb4k_umountItem()->setDefault();
    }
    else
    {
      // Do nothing
    }

    program.clear();
  }
  else
  {
    missing << "smb4k_umount";
  }

  if ( !(program = KGlobal::dirs()->findResource( "exe", "smb4k_kill" )).isEmpty() )
  {
    Smb4KSettings::self()->smb4k_killItem()->setDefaultValue( program );

    if ( Smb4KSettings::smb4k_kill().isEmpty() )
    {
      Smb4KSettings::self()->smb4k_killItem()->setDefault();
    }
    else
    {
      // Do nothing
    }

    program.clear();
  }
  else
  {
    missing << "smb4k_kill";
  }

  if ( !(program = KGlobal::dirs()->findResource( "exe", "smb4k_sudowriter" )).isEmpty() )
  {
    Smb4KSettings::self()->smb4k_sudowriterItem()->setDefaultValue( program );

    if ( Smb4KSettings::smb4k_sudowriter().isEmpty() )
    {
      Smb4KSettings::self()->smb4k_sudowriterItem()->setDefault();
    }
    else
    {
      // Do nothing
    }

    program.clear();
  }
  else
  {
    missing << "smb4k_sudowriter";
  }

  if ( !(program = KGlobal::dirs()->findResource( "exe", "kdesu" )).isEmpty() )
  {
    Smb4KSettings::self()->kdesuItem()->setDefaultValue( program );

    if ( Smb4KSettings::kdesu().isEmpty() )
    {
      Smb4KSettings::self()->kdesuItem()->setDefault();
    }
    else
    {
      // Do nothing
    }

    program.clear();
  }
  else
  {
    missing << "kdesu";
  }

  if ( !missing.isEmpty() )
  {
    // Error out if one of the mandatory programs is
    // missing:

    Smb4KCoreMessage::error( ERROR_MISSING_PROGRAMS, missing.join( ", " ) );
    exit( EXIT_FAILURE );
  }
  else
  {
    if ( !(program = KStandardDirs::findExe( "sudo" )).isEmpty() )
    {
      Smb4KSettings::self()->sudoItem()->setDefaultValue( program );

      if ( Smb4KSettings::sudo().isEmpty() )
      {
        Smb4KSettings::self()->sudoItem()->setDefault();
      }
      else
      {
        // Do nothing
      }

      program.clear();
    }
    else
    {
      if ( Smb4KSettings::useForceUnmount() || Smb4KSettings::alwaysUseSuperUser() )
      {
        Smb4KCoreMessage::information( INFO_DISABLE_SUID_FEATURE, "sudo" );

        // Reset the super user settings:
        Smb4KSettings::self()->useForceUnmountItem()->setDefault();
        Smb4KSettings::self()->alwaysUseSuperUserItem()->setDefault();
      }
      else
      {
        // Do nothing
      }
    }

    if ( !(program = KStandardDirs::findExe( "dvips" )).isEmpty() )
    {
      Smb4KSettings::self()->dvipsItem()->setDefaultValue( program );

      if ( Smb4KSettings::dvips().isEmpty() )
      {
        Smb4KSettings::self()->dvipsItem()->setDefault();
      }
      else
      {
        // Do nothing
      }

      program.clear();
    }
    else
    {
      // Do nothing
    }

    if ( !(program = KStandardDirs::findExe( "enscript" )).isEmpty() )
    {
      Smb4KSettings::self()->enscriptItem()->setDefaultValue( program );

      if ( Smb4KSettings::enscript().isEmpty() )
      {
        Smb4KSettings::self()->enscriptItem()->setDefault();
      }
      else
      {
        // Do nothing
      }

      program.clear();
    }
    else
    {
      // Do nothing
    }

    if ( !(program = KStandardDirs::findExe( "rsync" )).isEmpty() )
    {
      Smb4KSettings::self()->rsyncItem()->setDefaultValue( program );

      if ( Smb4KSettings::rsync().isEmpty() )
      {
        Smb4KSettings::self()->rsyncItem()->setDefault();
      }
      else
      {
        // Do nothing
      }

      program.clear();
    }
    else
    {
      // Do nothing
    }

    if ( !(program = KStandardDirs::findExe( "konsole" )).isEmpty() )
    {
      Smb4KSettings::self()->konsoleItem()->setDefaultValue( program );

      if ( Smb4KSettings::konsole().isEmpty() )
      {
        Smb4KSettings::self()->konsoleItem()->setDefault();
      }
      else
      {
        // Do nothing
      }

      program.clear();
    }
    else
    {
      // Do nothing
    }

    // Write the configuration to disk:
    Smb4KSettings::self()->writeConfig();
  }
}


void Smb4KCore::setDefaultSettings()
{
  // Samba options that have to be dynamically imported from smb.conf:
  QMap<QString, QString> opts = Smb4KSambaOptionsHandler::self()->globalSambaOptions();

  if ( !opts["netbios name"].isEmpty() )
  {
    Smb4KSettings::self()->netBIOSNameItem()->setDefaultValue( opts["netbios name"] );

    if ( Smb4KSettings::netBIOSName().isEmpty() )
    {
      Smb4KSettings::self()->netBIOSNameItem()->setDefault();
    }
  }

  if ( !opts["workgroup"].isEmpty() )
  {
    Smb4KSettings::self()->domainNameItem()->setDefaultValue( opts["workgroup"] );

    if ( Smb4KSettings::domainName().isEmpty() )
    {
      Smb4KSettings::self()->domainNameItem()->setDefault();
    }
  }

  if ( !opts["socket options"].isEmpty() )
  {
    Smb4KSettings::self()->socketOptionsItem()->setDefaultValue( opts["socket options"] );

    if ( Smb4KSettings::socketOptions().isEmpty() )
    {
      Smb4KSettings::self()->socketOptionsItem()->setDefault();
    }
  }

  if ( !opts["netbios scope"].isEmpty() )
  {
    Smb4KSettings::self()->netBIOSScopeItem()->setDefaultValue( opts["netbios scope"] );

    if ( Smb4KSettings::netBIOSScope().isEmpty() )
    {
      Smb4KSettings::self()->netBIOSScopeItem()->setDefault();
    }
  }

  if ( !opts["name resolve order"].isEmpty() )
  {
    Smb4KSettings::self()->nameResolveOrderItem()->setDefaultValue( opts["name resolve order"] );

    if ( Smb4KSettings::nameResolveOrder().isEmpty() )
    {
      Smb4KSettings::self()->nameResolveOrderItem()->setDefault();
    }
  }

  if ( !opts["interfaces"].isEmpty() )
  {
    Smb4KSettings::self()->broadcastAddressItem()->setDefaultValue( opts["interfaces"] );

    if ( Smb4KSettings::broadcastAddress().isEmpty() )
    {
      Smb4KSettings::self()->broadcastAddressItem()->setDefault();
    }
  }
}


/////////////////////////////////////////////////////////////////////////////
//  SLOT IMPLEMENTATIONS
/////////////////////////////////////////////////////////////////////////////

void Smb4KCore::slotSetScannerState( int state )
{
  m_scanner_state = state;
  setCurrentState( state );
  emit runStateChanged();
}


void Smb4KCore::slotSetMounterState( int state )
{
  m_mounter_state = state;
  setCurrentState( state );
  emit runStateChanged();
}


void Smb4KCore::slotSetPrinterHandlerState( int state )
{
  m_print_state = state;
  setCurrentState( state );
  emit runStateChanged();
}


void Smb4KCore::slotSetSynchronizerState( int state )
{
  m_syncer_state = state;
  setCurrentState( state );
  emit runStateChanged();
}


void Smb4KCore::slotSetPreviewerState( int state )
{
  m_previewer_state = state;
  setCurrentState( state );
  emit runStateChanged();
}


void Smb4KCore::slotSetSearchState( int state )
{
  m_search_state = state;
  setCurrentState( state );
  emit runStateChanged();
}


void Smb4KCore::slotAboutToQuit()
{
  Smb4KSettings::self()->writeConfig();
}

#include "smb4kcore.moc"
