/***************************************************************************
                          batchrenamer.cpp  -  description
                             -------------------
    begin                : Sat Aug 18 2001
    copyright            : (C) 2001 by Dominik Seichter
    email                : domseichter@web.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.                                   *
 *                                                                         *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifndef VERSION
    #define VERSION "unknown"
#endif

// OS includes
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
// chmod:
#include <sys/types.h>
#include <sys/stat.h>

// TQt includes
#include <tqdir.h>
#include <tqregexp.h>

// TDE includes
#include <tdeapplication.h>
#include <tdeio/job.h>
#include <tdeio/netaccess.h>
#include <tdelocale.h>

// Own includes
#include "ProgressDialog.h"
#include "batchrenamer.h"
#include "fileoperation.h"
#include "pluginloader.h"
#include "kmylistview.h"

using namespace TDEIO;

BatchRenamer::BatchRenamer()
    : m_index( 0 )
{
    plug = PluginLoader::instance();
    m_counter_index = 0;
}

BatchRenamer::~BatchRenamer()
{
}

void BatchRenamer::processFiles( ProgressDialog* p, TQObject* object )
{
    delete object;
    t.start();

    m_counters.clear();

    for( unsigned int i = 0; i < m_files.count(); i++) 
    {
	m_counter_index = 0;

        if( m_mode == RENAME ) {// final Path = source Path
            m_files[i].dst.directory = m_files[i].src.directory;
            m_files[i].dst.url = m_files[i].src.url;
            m_files[i].dst.url.setFileName( TQString() );
        } else {
            m_files[i].dst.directory = dirname.path();
            m_files[i].dst.url = dirname;
        }

        if( i == 0 )
            p->setDestination( m_files[i].dst.url );
	else
	{
	    if( m_reset )
		findCounterReset( i );
	}
	
        m_files[i].dst.name = processString( text, m_files[i].src.name, i );
        if( !extext.isEmpty() )
            m_files[i].dst.extension = processString( extext, m_files[i].src.extension, i );

        (void)applyManualChanges( i );

        // Assemble filenames
        parseSubdirs( &m_files[i] );
        // TODO: DOM 
        // ESCAPE HERE
   
        m_files[i].src.name = BatchRenamer::buildFilename( &m_files[i].src, true );

        // Let's run the plugins that change the final filename,
        // i.e the encodingsplugin
        m_files[i].dst.name = parsePlugins( i, m_files[i].dst.name, TYPE_FINAL_FILENAME );

        m_files[i].dst.name = BatchRenamer::buildFilename( &m_files[i].dst, true );
                
        /*
         * take care of renamed directories and
         * correct the paths of their contents
         */
        if( m_files[i].dir && (m_mode == RENAME || m_mode == MOVE) ) {
            for( unsigned int c = i; c < m_files.count(); c++ ) {
                if( m_files[c].src.directory.left( m_files[i].src.name.length() + 1 )
                    == ( m_files[i].src.name + "/" ) ) {

                    m_files[c].src.directory.replace( 0, m_files[i].src.name.length(), m_files[i].dst.name );
                    m_files[c].src.url.setPath( BatchRenamer::buildFilename( &m_files[c].src, true ) );
                }
            }
        }
    }

    p->print(i18n("Filenames Processed after %1 seconds.").arg(t.elapsed()/1000));

    work( p );
}

TQString BatchRenamer::processString( TQString text, TQString oldname, int i )
{
    /*
     * Come on! Grep for this text and help me!
     *
     * note about krename escape sequences
     * for certain characters:
     *
     * Krename will have problems with files
     * which contain one of the following
     * unicode characters: 60000, 60001, 60002
     * 60003, 60004, 60005, 60006.
     *
     * This is not a good solution, if you have a
     * better one please tell me about it!
     */
     
    doEscape( oldname );
    /*
     * Call here all functions that handle
     * arguments that are single tokens (&,%,...).
     * or in [brackets]
     */
    text = findBrackets( oldname, text, i );
    text = findAndProcess( "$", text, oldname );
    text = findAndProcess( "%", text, oldname.lower() );
    text = findAndProcess( "&", text, oldname.upper() );
    text = findAndProcess( "\\", text, oldname.stripWhiteSpace() );
    text = findStar( oldname, text );
    text = findNumbers( text, m_files.count(), i );
    /*
     * text is used as argument token for plugins!
     */
    text = parsePlugins( i, text, TYPE_TOKEN );
    /*
     * Replace after Plugins !
     * Replace shoud be the last the
     * before re-escaping tokens !
     */
    text = findReplace( text );

    // convert special chars back (e.g. &,$)
    // TODO: this is to early, because 
    // parseSubdirs creates subdirectories
    // for "/" returned by plugins!!!!
    // text = unEscape( text );

    return text;
}

TQString BatchRenamer::parsePlugins( int i, const TQString& text, int type )
{
    TQPtrListIterator<PluginLoader::PluginLibrary> it( plug->libs );
    TQString ret = text;

    if( type == TYPE_FINAL_FILE )
        ret = "";
        
    for( ; it.current(); ++it )
        if( (*it)->usePlugin && (*it)->plugin->type() == type )
        {
            ret = (*it)->plugin->processFile( this, i, text, type );
            doEscape( ret );
        }

    return ret;
}

void BatchRenamer::createPreview( TQListView* list )
{
    KMyListViewItem* item1 = NULL;
    TQString tmp;

    m_counters.clear();
    for( unsigned int i = 0; i < m_files.count(); i++) 
    {            
	m_counter_index = 0;

	if( i && m_reset )
	    findCounterReset( i );

        m_files[i].dst.name = processString( text, m_files[i].src.name, i );
        if( !extext.isEmpty() )
            m_files[i].dst.extension = processString( extext, m_files[i].src.extension, i );

        bool modified = applyManualChanges( i );


        TQString sname = BatchRenamer::buildFilename( &m_files[i].src, false );

        // Let's run the plugins that change the final filename,
        // i.e the encodingsplugin
        m_files[i].dst.name = parsePlugins( i, m_files[i].dst.name, TYPE_FINAL_FILENAME );
        TQString dname = BatchRenamer::buildFilename( &m_files[i].dst, false );
        
        item1 = new KMyListViewItem( modified, list, item1, sname, dname );
    }
}

void BatchRenamer::work( ProgressDialog* p )
{
    // TODO: use CopyJob here

    FileOperation fop;
    TQFile* fundo ( NULL );
    TQTextStream* tundo ( NULL );

    if( undo ) {
        // Create header for undo script
        fundo = new TQFile( m_undoScript );
        if( fundo->open( IO_WriteOnly ) ) {
            tundo = new TQTextStream( fundo );
            writeUndoScript( tundo );
        } else {
            undo = false;
            p->error( i18n("Can't create undo script :") + fundo->name() );
            delete fundo;
        }
    }

    int error = 0;
    RenamedList* renamedFiles = new RenamedList[m_files.count()];
    p->setProgressTotalSteps( m_files.count() + 1 );

    /*
     * Give the user some information...
     */
    if( m_mode == COPY)
        p->print(i18n("Files will be copied to: %1").arg(m_files[0].dst.directory));
    else if( m_mode == MOVE )
        p->print(i18n("Files will be moved to: %1").arg(m_files[0].dst.directory));
    else if( m_mode == LINK )
        p->print(i18n("Symbolic links will be created in: %1").arg(m_files[0].dst.directory));
    else if( m_mode == RENAME )
        p->print(i18n("Input files will be renamed."));

    unsigned int i;
    for( i = 0; i < m_files.count(); i++) {
        p->setProgress( i+1 );

        if( p->wasCancelled() )
            break;

        KURL src = m_files[i].src.url;
        KURL dst = m_files[i].dst.url;
        dst.setPath( m_files[i].dst.name );

        renamedFiles[i].src = src;
        renamedFiles[i].dst = dst;
        renamedFiles[i].dir = m_files[i].dir;
        
        FileOperation fop;
        if( !fop.start( src, dst, m_mode, overwrite ) ) {
            p->error( fop.error() );
            renamedFiles[i].error = true;
            error++;
            continue;
        } else {
            renamedFiles[i].error = false;
        }

        // TODO: overwriting of files!
        /*
         * The renamed file should be on its correct location now,
         * so that we can call the last plugins (e.g. for changing permissions)
         *
         * Remember, the token argument is the filename for this type of plugins!
         *
         * If the return value is not empty an error has occured!
         * The plugin should return an error message in this case!
         */

        TQString eplug = parsePlugins( i, TQString(), TYPE_FINAL_FILE );
        if( !eplug.isEmpty() ) {
            p->error( eplug );
            error++;
        }

        /* Create the undo script now */
        if( undo )
            if( dst.isLocalFile() && src.isLocalFile() ) {
                // Plugins ???
                (*tundo) << "echo \"" << src.fileName()
                         << " -> " << dst.fileName() << "\"" << endl;
                (*tundo) << "mv --force -b --suffix=.krename_ \"" << m_files[i].dst.name
                         << "\" \"" << m_files[i].src.name << "\"" << endl;
            } else
                p->warning(i18n("Undo is not possible for remote file: %1").arg(dst.prettyURL()));

    }

    if( !p->wasCancelled() ) {
        TQPtrListIterator<PluginLoader::PluginLibrary> it( plug->libs );
        for( ; it.current(); ++it ) {
            if( (*it)->usePlugin )
                (*it)->plugin->finished();
        }
    }

    const TQString m = i18n("Renamed %1 files successfully.").arg(i-error);
    ( i - error ) ? p->print( m ) : p->warning( m );

    if( error > 0 ) 
        p->warning(i18n("%2 errors occurred!").arg(error));

    p->print(i18n("Elapsed time: %1 seconds").arg( t.elapsed()/1000 ), "kalarm");
    p->print(i18n("KRename finished the renaming process."), "krename");
    p->print(i18n("Press close to quit!"));
    p->setRenamedFiles( renamedFiles, m_files.count() );

    if( undo ) {
        (*tundo) << endl << "echo \"Finished undoing " << m_files.count() << " actions.\"" << endl;
        delete tundo;
        fundo->close();

        // Make fundo exuteable
        if (chmod(m_undoScript.local8Bit(), (mode_t)(S_IRUSR | S_IWUSR | S_IXUSR)))
            p->error( i18n("Can't set executable bit on undo script.") );
        delete fundo;
    }

    p->done( error, i-error, m_mode == MOVE || m_mode == RENAME );
    m_files.clear();
    delete this;
}

void BatchRenamer::escape( TQString & text, const TQString & token, const TQString & sequence )
{
    text.replace( token, sequence );
}

TQString & BatchRenamer::doEscape( TQString& text, bool filename )
{
    if( filename ) {
        BatchRenamer::escape( text, "&", TQChar( 60000 ) );
        BatchRenamer::escape( text, "$", TQChar( 60001 ) );
        BatchRenamer::escape( text, "%", TQChar( 60002 ) );
        BatchRenamer::escape( text, "#", TQChar( 60004 ) );
        BatchRenamer::escape( text, "[", TQChar( 60005 ) );
        BatchRenamer::escape( text, "]", TQChar( 60006 ) );
        BatchRenamer::escape( text, "\\", TQChar( 60007 ) );
        BatchRenamer::escape( text, "/", TQChar( 60008 ) );
        BatchRenamer::escape( text, "{", TQChar( 60009 ) );        
        BatchRenamer::escape( text, "}", TQChar( 60010 ) );        
        BatchRenamer::escape( text, "*", TQChar( 60011 ) );        
    } else {
        BatchRenamer::escape( text, "\\&", TQChar( 60000 ) );
        BatchRenamer::escape( text, "\\$", TQChar( 60001 ) );
        BatchRenamer::escape( text, "\\%", TQChar( 60002 ) );
        BatchRenamer::escape( text, "\\#", TQChar( 60004 ) );
        BatchRenamer::escape( text, "\\[", TQChar( 60005 ) );
        BatchRenamer::escape( text, "\\]", TQChar( 60006 ) );
        BatchRenamer::escape( text, "\\\\", TQChar( 60007 ) ); 
        BatchRenamer::escape( text, "\\/", TQChar( 60008 ) );
        BatchRenamer::escape( text, "\\{", TQChar( 60009 ) );        
        BatchRenamer::escape( text, "\\}", TQChar( 60010 ) );      
        BatchRenamer::escape( text, "\\*", TQChar( 60011 ) );          
    }

    return text;
}

TQString & BatchRenamer::unEscape( TQString & text )
{ 
    BatchRenamer::escape( text, TQChar( 60000 ), "&" );
    BatchRenamer::escape( text, TQChar( 60001 ), "$" );
    BatchRenamer::escape( text, TQChar( 60002 ), "%" );
    BatchRenamer::escape( text, TQChar( 60004 ), "#" );
    BatchRenamer::escape( text, TQChar( 60005 ), "[" );
    BatchRenamer::escape( text, TQChar( 60006 ), "]" );
    BatchRenamer::escape( text, TQChar( 60007 ), "\\" );
    // %252f == /, it seems that filenames on unix cannot contain
    // a /. So I use %252f, at least konqui displays it correctly
    // this was needed, so that plugins that return a slash do not cause errors
    BatchRenamer::escape( text, TQChar( 60008 ), "%2f" );
    BatchRenamer::escape( text, TQChar( 60009 ), "{" );        
    BatchRenamer::escape( text, TQChar( 60010 ), "}" );        
    BatchRenamer::escape( text, TQChar( 60011 ), "*" );

    return text;
}

int BatchRenamer::getCharacters( int n )
{
    TQString s;
    s.sprintf( "%i", n );
    return s.length();
}

TQString BatchRenamer::findAndProcess( const TQString & token, TQString text, const TQString & replace )
{
    /*
     * pos can here be -1 because
     * findRev is called with it as a
     * value !
     */
    text.replace( token, replace );
    return text;
}

TQString BatchRenamer::findNumbers( TQString text, int count, int i )
{
    // Rewritten in Version 0.8
    // Added numbers skipping in 1.3
    // Changed again in Version 1.8 to optimize it and fix a bug with skipping numbers
    int pos = 0, counter = 1;
    tCounterValues countervalues;
    countervalues.start = m_index;
    countervalues.step = m_step;

    if( text.contains( "#", FALSE ) <= 0 )
        return text;

    pos = text.find("#", pos);
    pos++;
    while( text[pos] == '#' ) {
        text.remove(pos, 1);
        counter++;
    }

    findNumberAppendix( text, pos, &countervalues.start, &countervalues.step );

    pos = text.find("#", 0);

    if( (signed int)m_counters.count() <= m_counter_index )
    {
	countervalues.value = countervalues.start - countervalues.step;
	// other wise the counter would start at:
	// start + step instead of start
	m_counters.append( countervalues );
    }

    do {
	m_counters[m_counter_index].value += m_counters[m_counter_index].step;
    } while( m_skip.contains( m_counters[m_counter_index].value ) );
    
    /*
    int v = start + (i*step) + m_skip_add[m_counter_index];
    for( unsigned int z = 0; z < m_skip.count(); z++ ) {
        if( m_skip[z] == v ) {
            m_skip_add[m_counter_index] += step;
            v += step;
        }
    }
    */

    TQString temp;
    temp.sprintf("%0*i", counter, m_counters[m_counter_index].value );
    text.replace( pos, 1, temp);

    ++m_counter_index;
    return findNumbers( text, count, i );
}

void BatchRenamer::findNumberAppendix( TQString & text, int pos, int* start, int* step )
{
    TQString appendix = TQString();
    int tmp = 0;
    int end = 0;
    bool ok = false;
    
    if( text[pos] == '{' && (end = text.find( "}", pos )) > -1)
    {
        //tqDebug("Found an appendix:" + appendix );
        appendix = text.mid( pos + 1, end - pos - 1);
        text.remove( pos, end - pos + 1 );
       
        tmp = appendix.section( ';', 0, 0 ).toInt( &ok ); // first section = start index
        if( ok )
            *start = tmp;
        
        tmp = appendix.section( ';', 1, 1 ).toInt( &ok ); // second section = stepping
        if( ok )
            *step = tmp;
    }
}

TQString BatchRenamer::findStar( const TQString & oldname, TQString text )
{
    int pos = -1;
    do {
        pos = text.findRev("*", pos);
        if( pos >= 0 ) {
            TQString tmp = oldname.lower();
            if( tmp[0].isLetter() )
                tmp[0] = tmp[0].upper();

            for( unsigned int i = 0; i < tmp.length(); i++ )
                if( tmp[i+1].isLetter() && !tmp[i].isLetter() &&
                    tmp[i] != '\'' && tmp[i] != '?' && tmp[i] != '`' )
                    tmp[i+1] = tmp[i+1].upper();

            text.replace( pos, 1, tmp);
        }
    } while( pos >= 0 );
    return text;
}

TQString BatchRenamer::findBrackets( TQString oldname, TQString text, int i )
{
    /*
     * looks for a statement in brackets [ ]
     * and calls findToken() with this statement.
     */

    int num, pos = -1, a;
    TQString token;

    if( text.contains("]", FALSE) <= 0 || text.isEmpty() )
        return text;

    num = text.contains("[", FALSE);
    if(num <= 0 )
        return text;

    pos = text.findRev("[", pos);
    a = text.find("]", pos );
    if( a < 0 && pos >= 0 )
        return text;

    if( pos < 0 && a >= 0 )
        return text;

     if( pos >= 0 && a >= 0 ) {
        token = text.mid( pos+1, (a-pos)-1 );
        
        // support [4-[length]]
        token = findBrackets( oldname, token, i );
        
        text.remove( pos, (a-pos)+1 );
        text.insert( pos, findToken( oldname, token, i ));
    }
    return findBrackets( oldname, text, i );
}

TQString BatchRenamer::processToken( TQString token, TQString oldname, int i )
{
    TQString tmp;

    /*
     * Call here all functions that handle
     * arguments in brackets.
     */
    tmp = findPartStrings( oldname, token );
    if( !tmp.isEmpty() )
        return tmp;

    tmp = findDirName( token, m_files[i].src.directory );
    if( !tmp.isEmpty() )
        return tmp;

    tmp = findLength( token, m_files[i].src.name );
    if( !tmp.isEmpty() )
        return tmp;
        
    Plugin* p = plug->findPlugin( token );
    if( p )
    {
        tmp = p->processFile( this, i, token, TYPE_BRACKET );
        if( !tmp.isNull() )
        {
            doEscape( tmp );
            return tmp;
        }
    }

    /*
     * Maybe I should remove this!
     * Krename simply ignores unknown tokens!
     * Usefull for the MP3 Plugin!
     */
    return TQString();
}

TQString BatchRenamer::findToken( TQString oldname, TQString token, int i )
{
    enum conversion { LOWER, UPPER, MIXED, STAR, STRIP, NONE, EMPTY, NUMBER };
    unsigned int numwidth = 0;
    
    conversion c = EMPTY;
    if( !token.left(1).compare("$") )
        c = NONE;
    else if( !token.left(1).compare("%") )
        c = LOWER;
    else if( !token.left(1).compare("&") )
        c = UPPER;
    else if( !token.left(1).compare("") )
        c = MIXED;
    else if( !token.left(1).compare("*") )
        c = STAR;
    else if( !token.left(1).compare("\\") )
        c = STRIP;
    else if( !token.left(1).compare("#") ) {
        while( !token.left(1).compare("#") ) {
            token.remove( 0, 1 );
            ++numwidth;
        }
        
        c = NUMBER;
    }

    if( c != EMPTY && c != NUMBER )
        token.remove( 0, 1 );
    
    TQString save = token;
    token = processToken( token, oldname, i );

    switch( c ) {
        case LOWER:
            token = token.lower();
            break;
        case UPPER:
            token = token.upper();
            break;
        case MIXED:
            token = token.lower();
            token.replace( 0, 1, token[0].upper());
            break;
        case STAR:
            token = findStar( token, "*" );
            break;
        case STRIP:
            token = token.stripWhiteSpace();
            break;
        case NUMBER:
            {
                bool b = false;
                int n = token.toInt( &b );
                if( b )
                    token = token.sprintf("%0*i", numwidth, n );
            }
            break;
        default:
            break;
    }

    doEscape( token );
    return token;
}

TQString BatchRenamer::findPartStrings( TQString oldname, TQString token )
{
    TQString first, second;
    int pos = -1;
    
    // parse things like [2;4{[dirname]}]
    if( token.contains( "{" ) >= 1 && token.contains( "}" ) >= 1 ) {
        int pos = token.find( "{" );
        oldname = token.mid( pos + 1, token.findRev( "}" ) - pos - 1 );
        token = token.left( pos );
    }

    if( token.contains("-") ) {
        pos = token.find( "-", 0 );
        first = token.left( pos );
        // ------- Code OK ^ !

        second = token.mid( pos+1, token.length() );

        // version < 1.7
        // return oldname.mid( first.toInt()-1, second.toInt()-first.toInt() +1 );
        // version > 1.7
        //return oldname.mid( first.toInt()-1, second.toInt()-first.toInt() );
        // version > 1.8

        bool ok;
        int sec = second.toInt( &ok );
        if( !ok || sec == 0 )
            sec = oldname.length();

        /*
         * x should not be larger than the old name
         * and not smaller than zero.
         */
        int x = sec-first.toInt();
        if( x > (signed int)oldname.length() || x < 0 )
            x = oldname.length()-first.toInt();

        /*
         * if I would comment my code I would understand this line :)
         * without this line, there is sometimes the last letter
         * of a filename missing.
         */
        if( x != -1 )
            x++;

        return oldname.mid( first.toInt()-1, x );
    } else if( token.contains(";") ) {
        pos = token.find( ";", 0 );

        first = token.left( pos );
        second = token.mid( pos+1, token.length() );

        return oldname.mid( first.toInt()-1, second.toInt() );
    } else {
        bool ok = false;
        int number = token.toInt( &ok );

        if( ok && (number <= (signed int)oldname.length() && number > 0 ) )
            return TQString(oldname[ number -1 ]);
        else
            return TQString();
    }
}

TQString BatchRenamer::findDirName( TQString token, TQString path )
{
    if( token.left( 7 ).lower() == "dirname" ) {
        if( path.right( 1 ) == "/" )
            path = path.left( path.length() - 1);

        int recursion = 1;
        if( token.length() > 7 ) {
            token = token.right( token.length() - 7 );
            recursion = token.contains( "." );
            if(  recursion != (signed int)token.length() )
                return TQString();

            recursion++;
        }

        return path.section( "/", recursion * -1, recursion * -1);
    }
    
    return TQString();
}

TQString BatchRenamer::findLength( const TQString & token, const TQString & name )
{
    if( token.lower().startsWith( "length" ) ) {
        int minus = 0;
        if( token[6] == '-' ) {
            bool n = false;
            minus = token.mid( 7, token.length() - 7 ).toInt( &n );
            if( !n )
                minus = 0;
        }
        
        return TQString::number( name.length() - minus );
    }
   
    return TQString();
}

TQString BatchRenamer::findReplace( TQString text )
{
    // Call for each element in replace strings doReplace with correct values
    for( unsigned int i = 0; i < m_replace.count(); i++ ) {
        replacestrings s = m_replace[i];
        text = doReplace( text, unEscape( s.find ), s.replace, s.reg );
    }

    return text;
}

TQString BatchRenamer::doReplace( TQString text, TQString find, TQString replace, bool reg )
{
    if( !reg ) 
    {
        // we use the escaped text here because the user might want 
        // to find a "&" and replace it
        text.replace( doEscape( find ), replace );
    } 
    else
    {
        // no doEscape() here for the regexp, because it would destroy our regular expression
        // other wise we will not find stuff like $, [ in the text 
        text = doEscape( unEscape( text ).replace( TQRegExp( find ), replace ) );
    }
    return text;
}

void BatchRenamer::writeUndoScript( TQTextStream* t )
{
    // write header comments
    (*t) << "#!/bin/bash" << endl
         << "# KRename Undo Script" << endl << "#" << endl
         << "# KRename was written by:" << endl
         << "# Dominik Seichter <domseichter@web.de>" << endl
         << "# http://krename.sourceforge.net" << endl << "#" << endl
         << "# Script generated by KRename Version: " << VERSION << endl << endl
         << "# This script must be started with the option --krename to work!" << endl;

    // write functions:
    (*t) << "echo \"KRename Undo Script\"" << endl
         << "echo \"http://krename.sourceforge.net\"" << endl
         << "echo \"\"" << endl;

    (*t) << "if test --krename = $1 ; then" << endl
         << "   echo \"\"" << endl
         << "else" << endl
         << "   echo \"You have to start this script\"" << endl
         << "   echo \"with the command line option\"" << endl
         << "   echo \"--krename\"" << endl
         << "   echo \"to undo a rename operation.\"" << endl
         << "   exit" << endl
         << "fi" << endl;
}

void BatchRenamer::parseSubdirs( data* f )
{
    int pos = 0;
    if( (pos = f->dst.name.findRev( "/", -1 ) ) > 0 ) {
        TQString dirs = f->dst.name.left( pos );
        f->dst.name = f->dst.name.right( f->dst.name.length() - pos - 1 );
        f->dst.directory += ( f->dst.directory.right( 1 ) == "/" ) ? "" : "/";

        // create the missing subdir now
        int i = 0;
        TQString d = TQString::null;
        for (d = dirs.section("/", i, i, TQString::SectionSkipEmpty);
             !d.isEmpty();
             i++, d = dirs.section("/", i, i, TQString::SectionSkipEmpty))
        {
            KURL url = f->dst.url;
            // it is important to unescape here
            // to support dirnames containing "&" or 
            // similar tokens
            url.addPath( unEscape( d ) );
            if (!NetAccess::exists(url, false, 0) && !NetAccess::mkdir(url, 0, -1))
                // TODO: GUI bug report
                tqDebug("Can't create %s", url.prettyURL().latin1() ); 

            f->dst.url.addPath( d );
            f->dst.directory.append( d + "/" );
        }
    }
}

TQString BatchRenamer::buildFilename( fileentry* entry, bool dir )
{
    TQString filename = ( dir ? entry->directory : TQString() ) + entry->name + ( entry->extension.isEmpty() ? TQString() : TQString(".") ) + entry->extension;
    // unescape here as filename is still escaped
    unEscape( filename );
    return filename;
}

bool BatchRenamer::applyManualChanges( int i )
{
    /*
     * The last step: make changes of
     * the user visible
     */

    if( !m_changes.isEmpty() )
        for( unsigned int z = 0; z < m_changes.count(); z++ ) {
            KURL file = m_changes[z].url;
            if( file == m_files[i].src.url ) {
                m_files[i].dst.name = m_changes[z].user;
                // the file extension is already included
                // in the users name
                m_files[i].dst.extension = TQString();
                return true;
            }
        }

    return false;
}

void BatchRenamer::findCounterReset( int i )
{
    int z;
    if( m_files[i-1].src.directory != m_files[i].src.directory )
	for( z=0;z<(int)m_counters.count();z++ )
	{
	    m_counters[z].value = m_counters[z].start - m_counters[z].step;
	}
}


