/***************************************************************************
                         virt_vfs.cpp  -  description
                            -------------------
   begin                : Fri Dec 5 2003
   copyright            : (C) 2003 by Shie Erlich & Rafi Yanai
   email                : 
***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <unistd.h>
#include <time.h>
 
#include <tdefileitem.h>
#include <tdeglobalsettings.h>
#include <kurl.h>
#include <tdemessagebox.h>
#include <tdelocale.h>
#include <kdirsize.h>
#include <kstandarddirs.h>

#include "krpermhandler.h"
#include "../krusader.h"
#include "../defaults.h"
#include "virt_vfs.h"

#define VIRT_VFS_DB "virt_vfs.db"

TQDict<KURL::List> virt_vfs::virtVfsDict;
TDEConfig* virt_vfs::virt_vfs_db=0;

virt_vfs::virt_vfs( TQObject* panel, bool quiet ) : vfs( panel, quiet ) {
	// set the writable attribute
	isWritable = true;

	virtVfsDict.setAutoDelete( true );
	if ( virtVfsDict.isEmpty() ) {
		restore();
	}

	vfs_type = VIRT;
}

virt_vfs::~virt_vfs() {}

bool virt_vfs::populateVfsList( const KURL& origin, bool /*showHidden*/ ) {
	vfs_origin = origin;
	vfs_origin.adjustPath(-1);
	path = origin.path( -1 ).mid( 1 );
	if ( path.isEmpty() ) path = "/";

	KURL::List* urlList = virtVfsDict[ path ];
	if ( !urlList ) {
		urlList = new KURL::List();
		virtVfsDict.insert( path, urlList );
		virtVfsDict[ "/" ] ->append( KURL::fromPathOrURL( "virt:/" + path ) );
	}
	
	if ( urlList->isEmpty() ) return true;
	KURL::List::iterator it;
	for ( it = urlList->begin() ; it != urlList->end() ; /*++it*/ ) {
		KURL url = *it;
		// translate url->vfile and remove urls that no longer exist from the list
		vfile* vf = stat(url);
		if ( !vf ) {
			it = urlList->remove( it );
			// the iterator is advanced automaticly
			continue;
		}
		foundVfile( vf );
		++it;
	}
	save();
	return true;
}

void virt_vfs::vfs_addFiles( KURL::List *fileUrls, TDEIO::CopyJob::CopyMode /*mode*/, TQObject* /*toNotify*/, TQString /*dir*/, PreserveMode /*pmode*/ ) {
	if ( path == "/" ) {
		if ( !quietMode )
			KMessageBox::error( krApp, i18n( "You can't copy files directly to the 'virt:/' directory.\nYou can create a sub directory and copy your files into it." ), i18n( "Error" ) );
		return ;
	}
	
	KURL::List* urlList = virtVfsDict[ path ];
	for( unsigned i=0; i != fileUrls->count(); i++ ) {
		if( !urlList->contains( (*fileUrls)[ i ] ) )
			urlList->push_back( (*fileUrls)[ i ] );
	}

	vfs_refresh();
}

void virt_vfs::vfs_delFiles( TQStringList *fileNames ) {
	if ( path == "/" ) {
		for ( uint i = 0 ; i < fileNames->count(); ++i ) {
			TQString filename = ( *fileNames ) [ i ];
			virtVfsDict[ "/" ] ->remove( TQString("virt:/")+filename );
			virtVfsDict.remove( filename );
		}
		vfs_refresh();
		return ;
	}

	KURL::List filesUrls;
	KURL url;

	// names -> urls
	for ( uint i = 0 ; i < fileNames->count(); ++i ) {
		TQString filename = ( *fileNames ) [ i ];
		filesUrls.append( vfs_getFile( filename ) );
	}
	TDEIO::Job *job;

	// delete of move to trash ?
	krConfig->setGroup( "General" );
	if ( krConfig->readBoolEntry( "Move To Trash", _MoveToTrash ) ) {
#if KDE_IS_VERSION(3,4,0)
		job = TDEIO::trash( filesUrls, true );
#else
		job = new TDEIO::CopyJob( filesUrls, TDEGlobalSettings::trashPath(), TDEIO::CopyJob::Move, false, true );
#endif
		connect( job, TQT_SIGNAL( result( TDEIO::Job* ) ), krApp, TQT_SLOT( changeTrashIcon() ) );
	} else
		job = new TDEIO::DeleteJob( filesUrls, false, true );

	// refresh will remove the deleted files...
	connect( job, TQT_SIGNAL( result( TDEIO::Job* ) ), this, TQT_SLOT( vfs_refresh( TDEIO::Job* ) ) );
}

void virt_vfs::vfs_removeFiles( TQStringList *fileNames ) {
	if ( path == "/" )
		return; 
	
	// removing the URLs from the collection
	for ( uint i = 0 ; i < fileNames->count(); ++i ) {
		KURL::List* urlList = virtVfsDict[ path ];
		if( urlList )
			urlList->remove( vfs_getFile( ( *fileNames ) [ i ] ) );
	}
	
	vfs_refresh();
}

KURL::List* virt_vfs::vfs_getFiles( TQStringList* names ) {
	KURL url;
	KURL::List* urls = new KURL::List();
	for ( TQStringList::Iterator name = names->begin(); name != names->end(); ++name ) {
		url = vfs_getFile( *name );
		urls->append( url );
	}
	return urls;
}

KURL virt_vfs::vfs_getFile( const TQString& name ) {
	vfile * vf = vfs_search( name );
	if ( !vf ) return KURL(); // empty

	KURL url = vf->vfile_getUrl();
	if ( vf->vfile_isDir() ) url.adjustPath( + 1 );
	return url;
}

void virt_vfs::vfs_mkdir( const TQString& name ) {
	if ( path != "/" ) {
		if ( !quietMode )
			KMessageBox::error( krApp, i18n( "Creating new directories is allowed only in the 'virt:/' directory." ), i18n( "Error" ) );
		return ;
	}
	KURL::List* temp = new KURL::List();
	virtVfsDict.insert( name, temp );
	virtVfsDict[ "/" ] ->append( TQString( "virt:/" )+name );

	vfs_refresh();
}

void virt_vfs::vfs_rename( const TQString& fileName, const TQString& newName ) {
	KURL::List fileUrls;
	KURL url , dest;

	vfile* vf = vfs_search( fileName );
	if ( !vf ) return ; // not found

	if ( path == "/" ) {
		virtVfsDict[ "/" ] ->append( TQString( "virt:/" ) + newName  );
		virtVfsDict[ "/" ] ->remove( TQString( "virt:/" ) + fileName );
		virtVfsDict.insert( newName, virtVfsDict.take( fileName ) );
		vfs_refresh();
		return ;
	}

	url = vf->vfile_getUrl();
	fileUrls.append( url );

	dest = fromPathOrURL( newName );
	// add the new url to the list
	// the the list is refreshed only existing files remain -
	// so we don't have to worry if the job was successful
	virtVfsDict[ path ] ->append( dest );

	TDEIO::Job *job = new TDEIO::CopyJob( fileUrls, dest, TDEIO::CopyJob::Move, true, false );
	connect( job, TQT_SIGNAL( result( TDEIO::Job* ) ), this, TQT_SLOT( vfs_refresh( TDEIO::Job* ) ) );
}

void virt_vfs::slotStatResult( TDEIO::Job* job ) {
	if( !job || job->error() ) entry = TDEIO::UDSEntry();
	else entry = static_cast<TDEIO::StatJob*>(job)->statResult();
	busy = false;
}

vfile* virt_vfs::stat( const KURL& url ) {
	if( url.protocol() == "virt" ){
		TQString path = url.path().mid(1);
		if( path.isEmpty() ) path = "/";
		vfile * temp = new vfile( path, 0, "drwxr-xr-x", time( 0 ), false, getuid(), getgid(), "inode/directory", "", 0 );
		temp->vfile_setUrl( url );
		return temp;
	}
	KFileItem* kfi;
	if ( url.isLocalFile() ) {
		kfi = new KFileItem( KFileItem::Unknown, KFileItem::Unknown, url, true );
	}
	else {
		busy = true;
		TDEIO::StatJob* statJob = TDEIO::stat( url, false );
		connect( statJob, TQT_SIGNAL( result( TDEIO::Job* ) ), this, TQT_SLOT( slotStatResult( TDEIO::Job* ) ) );
		while ( busy && vfs_processEvents() );
		if( entry.isEmpty()  ) return 0; // statJob failed
		
		kfi = new KFileItem(entry, url, true );
	}
	
	if ( !kfi->time( TDEIO::UDS_MODIFICATION_TIME ) ){
		 delete kfi;
		 return 0; // file not found		
	}
	
	vfile *temp;

	// get file statistics
	TQString name;
	if( url.isLocalFile() )
		name = url.path();
	else
		name = url.prettyURL();

	TDEIO::filesize_t size = kfi->size();
	time_t mtime = kfi->time( TDEIO::UDS_MODIFICATION_TIME );
	bool symLink = kfi->isLink();
	mode_t mode = kfi->mode() | kfi->permissions();
	TQString perm = KRpermHandler::mode2TQString( mode );
// set the mimetype
	TQString mime = TQString();
	TQString symDest = "";
	if ( symLink ) {
		symDest = kfi->linkDest();
		if ( kfi->isDir() ) perm[ 0 ] = 'd';
	}

	// create a new virtual file object
	if ( kfi->user().isEmpty() )
		temp = new vfile( name, size, perm, mtime, symLink, getuid(), getgid(), mime, symDest, mode );
	else {
		TQString currentUser = url.user();
		if ( currentUser.contains( "@" ) )  /* remove the FTP proxy tags from the username */
			currentUser.truncate( currentUser.find( '@' ) );
		if ( currentUser.isEmpty() )
			currentUser = KRpermHandler::uid2user( getuid() );
		temp = new vfile( name, size, perm, mtime, symLink, kfi->user(), kfi->group(), currentUser, mime, symDest, mode );
	}

	temp->vfile_setUrl( kfi->url() );
	delete kfi;
	return temp;
}

TDEConfig*  virt_vfs::getVirtDB(){
	if( !virt_vfs_db ){
		virt_vfs_db = new TDEConfig(VIRT_VFS_DB,false,"data");
	}
	return virt_vfs_db; 
}

bool virt_vfs::save(){
	TDEConfig* db = getVirtDB();
	
	db->setGroup("virt_db");
	TQDictIterator<KURL::List> it( virtVfsDict ); // See TQDictIterator
	for( ; it.current(); ++it ){
		KURL::List::iterator url;
		TQStringList entry;
		for ( url = it.current()->begin() ; url != it.current()->end() ; ++url ) {
			entry.append( (*url).prettyURL() );
		}
		db->writeEntry(it.currentKey(),entry);
	}
	
	db->sync();
	
	return true;
}

bool virt_vfs::restore(){
	TDEConfig* db = getVirtDB();
	db->setGroup("virt_db");
	
	TQMap<TQString, TQString> map = db->entryMap("virt_db");
	TQMap<TQString, TQString>::Iterator it;
	KURL::List* urlList;
	for ( it = map.begin(); it != map.end(); ++it ) {
		urlList = new KURL::List( db->readListEntry(it.key()) );
		virtVfsDict.insert( it.key(),urlList );
	}

	if( !virtVfsDict["/" ]){
		urlList = new KURL::List();
		virtVfsDict.insert( "/", urlList );	
	}
		
	return true;
}

void virt_vfs::vfs_calcSpace( TQString name , TDEIO::filesize_t* totalSize, unsigned long* totalFiles, unsigned long* totalDirs, bool* stop ) {
	if ( stop && *stop ) return ;
	if( path == "/" ) {
		KURL::List* urlList = virtVfsDict[ name ];
		if ( urlList )
			for( unsigned i=0; (i != urlList->size()) && !(*stop); i++ )
				calculateURLSize( (*urlList)[ i ], totalSize, totalFiles, totalDirs, stop );
		return;        
	}                
	return vfs::vfs_calcSpace( name, totalSize, totalFiles, totalDirs, stop );
}

#include "virt_vfs.moc"
