/* 
 *
 * $Id: k3bexternalbinmanager.cpp 619556 2007-01-03 17:38:12Z trueg $
 * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
 *
 * This file is part of the K3b project.
 * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
 *
 * 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.
 * See the file "COPYING" for the exact licensing terms.
 */

#include "k3bexternalbinmanager.h"

#include <kdebug.h>
#include <kprocess.h>
#include <tdeconfig.h>
#include <tdeversion.h>

#include <tqstring.h>
#include <tqregexp.h>
#include <tqfile.h>
#include <tqfileinfo.h>
#include <tqptrlist.h>

#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>



TQString K3bExternalBinManager::m_noPath = "";


// ///////////////////////////////////////////////////////////
//
// K3BEXTERNALBIN
//
// ///////////////////////////////////////////////////////////

K3bExternalBin::K3bExternalBin( K3bExternalProgram* p )
  : m_program(p)
{
}


bool K3bExternalBin::isEmpty() const
{
  return !version.isValid();
}


const TQString& K3bExternalBin::name() const
{
  return m_program->name();
}


bool K3bExternalBin::hasFeature( const TQString& f ) const
{
  return m_features.contains( f );
}


void K3bExternalBin::addFeature( const TQString& f )
{
  m_features.append( f );
}


const TQStringList& K3bExternalBin::userParameters() const
{
  return m_program->userParameters();
}



// ///////////////////////////////////////////////////////////
//
// K3BEXTERNALPROGRAM
//
// ///////////////////////////////////////////////////////////


K3bExternalProgram::K3bExternalProgram( const TQString& name )
  : m_name( name )
{
  m_bins.setAutoDelete( true );
}


K3bExternalProgram::~K3bExternalProgram()
{
}


const K3bExternalBin* K3bExternalProgram::mostRecentBin() const
{
  TQPtrListIterator<K3bExternalBin> it( m_bins );
  K3bExternalBin* bin = *it;
  ++it;
  while( *it ) {
    if( it.current()->version > bin->version )
      bin = *it;
    ++it;
  }
  return bin;
}


void K3bExternalProgram::addBin( K3bExternalBin* bin )
{
  if( !m_bins.contains( bin ) ) {
    // insertion sort
    // the first bin in the list is always the one used 
    // so we default to using the newest one
    K3bExternalBin* oldBin = m_bins.first();
    while( oldBin && oldBin->version > bin->version )
      oldBin = m_bins.next();

    m_bins.insert( oldBin ? m_bins.at() : m_bins.count(), bin );
  }
}

void K3bExternalProgram::setDefault( const K3bExternalBin* bin )
{
  if( m_bins.contains( bin ) )
    m_bins.take( m_bins.find( bin ) );

  // the first bin in the list is always the one used 
  m_bins.insert( 0, bin );
}


void K3bExternalProgram::setDefault( const TQString& path )
{
  for( TQPtrListIterator<K3bExternalBin> it( m_bins ); it.current(); ++it ) {
    if( it.current()->path == path ) {
      setDefault( it.current() );
      return;
    }
  }
}


void K3bExternalProgram::addUserParameter( const TQString& p )
{
  if( !m_userParameters.contains( p ) )
    m_userParameters.append(p);
}



// ///////////////////////////////////////////////////////////
//
// K3BEXTERNALBINMANAGER
//
// ///////////////////////////////////////////////////////////


K3bExternalBinManager::K3bExternalBinManager( TQObject* parent, const char* name )
  : TQObject( parent, name )
{
}


K3bExternalBinManager::~K3bExternalBinManager()
{
  clear();
}


bool K3bExternalBinManager::readConfig( TDEConfig* c )
{
  loadDefaultSearchPath();

  c->setGroup( "External Programs" );

  if( c->hasKey( "search path" ) )
    setSearchPath( c->readPathListEntry( "search path" ) );

  search();

  for ( TQMap<TQString, K3bExternalProgram*>::iterator it = m_programs.begin(); it != m_programs.end(); ++it ) {
    K3bExternalProgram* p = it.data();
    if( c->hasKey( p->name() + " default" ) ) {
      p->setDefault( c->readEntry( p->name() + " default" ) );
    }
    if( c->hasKey( p->name() + " user parameters" ) ) {
      TQStringList list = c->readListEntry( p->name() + " user parameters" );
      for( TQStringList::iterator strIt = list.begin(); strIt != list.end(); ++strIt )
	p->addUserParameter( *strIt );
    }
    if( c->hasKey( p->name() + " last seen newest version" ) ) {
      K3bVersion lastMax( c->readEntry( p->name() + " last seen newest version" ) );
      // now search for a newer version and use it (because it was installed after the last
      // K3b run and most users would probably expect K3b to use a newly installed version)
      const K3bExternalBin* newestBin = p->mostRecentBin();
      if( newestBin && newestBin->version > lastMax )
	p->setDefault( newestBin );
    }
  }

  return true;
}

bool K3bExternalBinManager::saveConfig( TDEConfig* c )
{
  c->setGroup( "External Programs" );
  c->writePathEntry( "search path", m_searchPath );

  for ( TQMap<TQString, K3bExternalProgram*>::iterator it = m_programs.begin(); it != m_programs.end(); ++it ) {
    K3bExternalProgram* p = it.data();
    if( p->defaultBin() )
      c->writeEntry( p->name() + " default", p->defaultBin()->path );

    c->writeEntry( p->name() + " user parameters", p->userParameters() );

    const K3bExternalBin* newestBin = p->mostRecentBin();
    if( newestBin )
      c->writeEntry( p->name() + " last seen newest version", newestBin->version );
  }

  return true;
}


bool K3bExternalBinManager::foundBin( const TQString& name )
{
  if( m_programs.find( name ) == m_programs.end() )
    return false;
  else
    return (m_programs[name]->defaultBin() != 0);
}


const TQString& K3bExternalBinManager::binPath( const TQString& name )
{
  if( m_programs.find( name ) == m_programs.end() )
    return m_noPath;

  if( m_programs[name]->defaultBin() != 0 )
    return m_programs[name]->defaultBin()->path;
  else
    return m_noPath;
}


const K3bExternalBin* K3bExternalBinManager::binObject( const TQString& name )
{
  if( m_programs.find( name ) == m_programs.end() )
    return 0;

  return m_programs[name]->defaultBin();
}


void K3bExternalBinManager::addProgram( K3bExternalProgram* p )
{
  m_programs.insert( p->name(), p );
}


void K3bExternalBinManager::clear()
{
  for( TQMap<TQString, K3bExternalProgram*>::Iterator it = m_programs.begin(); it != m_programs.end(); ++it )
    delete it.data();
  m_programs.clear();
}


void K3bExternalBinManager::search()
{
  if( m_searchPath.isEmpty() )
    loadDefaultSearchPath();

  for( TQMap<TQString, K3bExternalProgram*>::iterator it = m_programs.begin(); it != m_programs.end(); ++it ) {
    it.data()->clear();
  }

  // do not search one path twice
  TQStringList paths;
  for( TQStringList::const_iterator it = m_searchPath.begin(); it != m_searchPath.end(); ++it ) {
    TQString p = *it;
    if( p[p.length()-1] == '/' )
      p.truncate( p.length()-1 );
    if( !paths.contains( p ) && !paths.contains( p + "/" ) )
      paths.append(p);
  }

  // get the environment path variable
  char* env_path = ::getenv("PATH");
  if( env_path ) {
    TQStringList env_pathList = TQStringList::split(":", TQString::fromLocal8Bit(env_path));
    for( TQStringList::const_iterator it = env_pathList.begin(); it != env_pathList.end(); ++it ) {
      TQString p = *it;
      if( p[p.length()-1] == '/' )
	p.truncate( p.length()-1 );
      if( !paths.contains( p ) && !paths.contains( p + "/" ) )
	paths.append(p);
    }
  }


  for( TQStringList::const_iterator it = paths.begin(); it != paths.end(); ++it )
    for( TQMap<TQString, K3bExternalProgram*>::iterator pit = m_programs.begin(); pit != m_programs.end(); ++pit )
      pit.data()->scan(*it);

  // TESTING
  // /////////////////////////
  const K3bExternalBin* bin = program("cdrecord")->defaultBin();

  if( !bin ) {
    kdDebug() << "(K3bExternalBinManager) Probing cdrecord failed" << endl;
  }
  else {
    kdDebug() << "(K3bExternalBinManager) Cdrecord " << bin->version << " features: "
	      << bin->features().join( ", " ) << endl;

    if( bin->version >= K3bVersion("1.11a02") )
      kdDebug() << "(K3bExternalBinManager) "
		<< bin->version.majorVersion() << " " << bin->version.minorVersion() << " " << bin->version.patchLevel()
		<< " " << bin->version.suffix()
		<< " seems to be cdrecord version >= 1.11a02, using burnfree instead of burnproof" << endl;
    if( bin->version >= K3bVersion("1.11a31") )
      kdDebug() << "(K3bExternalBinManager) seems to be cdrecord version >= 1.11a31, support for Just Link via burnfree "
		<< "driveroption" << endl;
  }
}


K3bExternalProgram* K3bExternalBinManager::program( const TQString& name ) const
{
  if( m_programs.find( name ) == m_programs.end() )
    return 0;
  else
    return m_programs[name];
}


void K3bExternalBinManager::loadDefaultSearchPath()
{
  static const char* defaultSearchPaths[] = { "/usr/bin/",
					      "/usr/local/bin/",
					      "/usr/sbin/",
					      "/usr/local/sbin/",
					      "/opt/schily/bin/",
					      "/sbin",
					      0 };

  m_searchPath.clear();
  for( int i = 0; defaultSearchPaths[i]; ++i ) {
    m_searchPath.append( defaultSearchPaths[i] );
  }
}


void K3bExternalBinManager::setSearchPath( const TQStringList& list )
{
  loadDefaultSearchPath();

  for( TQStringList::const_iterator it = list.begin(); it != list.end(); ++it ) {
    if( !m_searchPath.contains( *it ) )
      m_searchPath.append( *it );
  }
}


void K3bExternalBinManager::addSearchPath( const TQString& path )
{
  if( !m_searchPath.contains( path ) )
    m_searchPath.append( path );
}



const K3bExternalBin* K3bExternalBinManager::mostRecentBinObject( const TQString& name )
{
  if( K3bExternalProgram* p = program( name ) )
    return p->mostRecentBin();
  else
    return 0;
}

#include "k3bexternalbinmanager.moc"

