/***************************************************************************
 *   Copyright (C) 2003 by Jens Dagerbo                                    *
 *   jens.dagerbo@swipnet.se                                               *
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

//BEGIN Includes

#include <dcopclient.h>
#include <tdeapplication.h>
#include <tdeparts/part.h>
#include <tdeparts/componentfactory.h>
#include <klibloader.h>
#include <ktrader.h>
#include <kurl.h>
#include <kurlrequester.h>
#include <klineedit.h>
#include <kdebug.h>
#include <tdetexteditor/editinterface.h>
#include <tdetexteditor/editor.h>
#include <tdetexteditor/document.h>
#include <tdetexteditor/viewcursorinterface.h>
#include <tdetexteditor/selectioninterface.h>
#include <kdevpartcontroller.h>
#include <kdevcore.h>
#include <kdevmainwindow.h>
#include <kdevproject.h>
#include <kdevpartcontroller.h>
#include <kcombobox.h>
#include <tdelocale.h>
#include <kstdguiitem.h>

#include <tqlayout.h>
#include <tqpushbutton.h>
#include <tqlineedit.h>
#include <tqradiobutton.h>
#include <tqstringlist.h>
#include <tqptrlist.h>
#include <tqregexp.h>
#include <tqdialog.h>
#include <tqfile.h>
#include <tqdir.h>
#include <tqtextstream.h>
#include <tqdatastream.h>

#include <sys/types.h>
#include <unistd.h>

#include "replace_part.h"
#include "replace_widget.h"
#include "replacedlgimpl.h"
#include "replaceitem.h"
#include "replaceview.h"

//END Includes

ReplaceWidget::ReplaceWidget(ReplacePart *part)
        : TQWidget(0, "replace widget"), m_part( part ),
        m_dialog( new ReplaceDlgImpl( this, "replace widget" ) ),
        _terminateOperation( false )
{
    // setup outputview
    TQVBoxLayout * layout = new TQVBoxLayout( this );
    TQHBoxLayout * buttonlayout = new TQHBoxLayout( layout );

    _cancel = new KPushButton( KStdGuiItem::cancel(), this );
    _replace = new KPushButton( KGuiItem(i18n("Replace"),"filefind"), this );

    _cancel->setEnabled( false );
    _replace->setEnabled( false );

    buttonlayout->addWidget( _replace );
    buttonlayout->addWidget( _cancel );

    _listview = new ReplaceView( this );
    layout->addWidget( _listview );

    //  setup signals
    connect( m_dialog->find_button, TQ_SIGNAL( clicked() ), TQ_SLOT( find() ) );

    connect( _replace, TQ_SIGNAL( clicked() ), TQ_SLOT( replace() ) );
    connect( _cancel, TQ_SIGNAL( clicked() ), TQ_SLOT( clear() ) );
    connect( _listview, TQ_SIGNAL( editDocument( const TQString &, int ) ), TQ_SLOT( editDocument( const TQString &, int ) ) );

    connect( m_part->core(), TQ_SIGNAL( stopButtonClicked( KDevPlugin * ) ), TQ_SLOT( stopButtonClicked( KDevPlugin * ) ) );
}

//BEGIN Slots

void ReplaceWidget::showDialog()
{
	if ( ! m_part->project() )
		return; /// @todo feedback?

	TQString currentWord;
	KParts::ReadOnlyPart *part = dynamic_cast<KParts::ReadOnlyPart*> ( m_part->partController()->activePart() );
	if ( part )
	{
		if ( part->url().isLocalFile() )
		{
			calledUrl = part->url().path();
			cursorPos ( part, &calledCol, &calledLine );

			KTextEditor::EditInterface* editIface = dynamic_cast<KTextEditor::EditInterface*>( m_part->partController()->activePart() );
			TQString str = editIface->textLine ( calledCol );

			int i;
			uint start, end;
			for (i =calledLine; i< str.length();i++){
				if (  ! (str[i].isLetter() || str[i].isNumber() || str[i] == '_' ) ){
					break;
				}
			}
			end=i;
			for ( i = calledLine; i >= 0; --i ){
				if ( ! ( str[i].isLetter() ||str[i].isNumber()|| str[i] == '_' ) ){
					i+=1;
					break;
				}
			}
			start = i < 0 ? 0 : i;
			currentWord = str.mid ( start, end-start );
		}
	}
	m_dialog->show ( m_part->project()->projectDirectory() + "/" + m_part->project()->activeDirectory() + "/" );

	KTextEditor::SelectionInterface *sel_iface
	= dynamic_cast<KTextEditor::SelectionInterface*> ( m_part->partController()->activePart() );
	if ( sel_iface && sel_iface->hasSelection() )
	{
		m_dialog->find_combo->setCurrentText ( sel_iface->selection() );
	}
 	else
	{
		m_dialog->find_combo->setCurrentText ( currentWord );
	}
}


void ReplaceWidget::find()
{
    _listview->clear();
    m_part->mainWindow()->raiseView( this );
    m_part->mainWindow()->setViewAvailable( this, true );

    _listview->setReplacementData( m_dialog->expressionPattern(), m_dialog->replacementString() );

    if ( showReplacements() )
    {
        _cancel->setEnabled( true );
        _replace->setEnabled( true );
    }
    else
    {
        clear();
        m_part->mainWindow()->setViewAvailable( this, false );
    }
}

void ReplaceWidget::replace()
{
    makeReplacements();
    clear();
    m_part->mainWindow()->setViewAvailable( this, false );
}

void ReplaceWidget::clear()
{
    _listview->clear();

    _cancel->setEnabled( false );
    _replace->setEnabled( false );

    m_part->mainWindow()->setViewAvailable( this, false );
}

void ReplaceWidget::editDocument( TQString const & file, int line )
{
    m_part->partController()->editDocument( KURL( file ), line );
}

void ReplaceWidget::stopButtonClicked( KDevPlugin * which )
{
    if ( which != 0 && which != m_part )
        return;

    _terminateOperation = true;
}

//END Slots

bool ReplaceWidget::showReplacements()
{
    ReplaceItem::s_listview_done = false;

    m_part->core()->running( m_part, true );

    bool completed = true;
    _terminateOperation = false;

    TQStringList files = workFiles();
    TQStringList openfiles = openProjectFiles();

    TQStringList::ConstIterator it = files.begin();
    while ( it != files.end() )
    {
        if ( shouldTerminate() )
        {
            completed = false;
            break;
        }

        if ( openfiles.contains( *it ) )
        {
            if ( KTextEditor::EditInterface * ei = getEditInterfaceForFile( *it ) )
            {
                TQString buffer = ei->text();
                TQTextIStream stream( &buffer );
                _listview->showReplacementsForFile( stream, *it );
            }
        }
        else
        {
            TQFile file( *it );
            if ( file.open ( IO_ReadOnly ) )
            {
                TQTextStream stream( &file );
                _listview->showReplacementsForFile( stream, *it );
            }
        }
        ++it;

        tdeApp->processEvents( 100 );
    }

    m_part->core()->running( m_part, false );

    ReplaceItem::s_listview_done = true;

    return completed;
}


void ReplaceWidget::cursorPos( KParts::Part *part, uint * line, uint * col )
{
    if (!part || !part->inherits("KTextEditor::Document")) return;

    KTextEditor::ViewCursorInterface *iface = dynamic_cast<KTextEditor::ViewCursorInterface*>(part->widget());
    if (iface)
    {
        iface->cursorPositionReal( line, col );
    }
}

void ReplaceWidget::setCursorPos( KParts::Part *part, uint line, uint col )
{
    if (!part || !part->inherits("KTextEditor::Document")) return;

    KTextEditor::ViewCursorInterface *iface = dynamic_cast<KTextEditor::ViewCursorInterface*>(part->widget());
    if (iface)
    {
        iface->setCursorPositionReal( line, col );
    }
}


bool ReplaceWidget::makeReplacements()
{
    uint col=0;
    uint line=0;
    cursorPos( m_part->partController()->activePart(), &col, &line );

    m_part->core()->running( m_part, true );

    bool completed = true;
    _terminateOperation = false;

    TQStringList openfiles = openProjectFiles();
    TQStringList changedFiles;

    ReplaceItem const * fileitem = _listview->firstChild();
    while ( fileitem )
    {
        if ( fileitem->isOn() )
        {
            TQString currentfile = fileitem->file();

            if ( openfiles.contains( currentfile ) )
            {
                if ( KTextEditor::EditInterface * ei = getEditInterfaceForFile( currentfile ) )
                {
                    TQString ibuffer = ei->text();
                    TQString obuffer;
                    TQTextStream istream( &ibuffer, IO_ReadOnly );
                    TQTextStream ostream( &obuffer, IO_WriteOnly );

                    _listview->makeReplacementsForFile( istream, ostream, fileitem );

                    ei->setText( obuffer );
                }
            }
            else
            {
                TQFile file( currentfile );
                TQString buffer;

                if ( file.open( IO_ReadOnly ) )
                {
                    TQTextStream istream( &file );
                    TQTextStream buffer_stream( &buffer, IO_WriteOnly );

                    _listview->makeReplacementsForFile( istream, buffer_stream, fileitem );

                    file.close();

                    if ( file.open( IO_WriteOnly ) )
                    {
                        TQTextStream ostream( &file );
                        ostream << buffer;
                        file.close();
                    }
                }
            }
            changedFiles << relativeProjectPath( ( currentfile ) );
        }
        fileitem = fileitem->nextSibling();

        tdeApp->processEvents( 100 );
    }

    // Telling the project about the edited files
    if ( ! changedFiles.isEmpty() )
    {
        m_part->project()->changedFiles( changedFiles );
    }

    m_part->partController()->saveAllFiles();

    m_part->core()->running( m_part, false );

    if ( calledUrl != TQString() )
    {
        m_part->partController()->editDocument( calledUrl, calledLine );
        setCursorPos( m_part->partController()->activePart(), calledCol, calledLine );
    }
    else{
        setCursorPos( m_part->partController()->activePart(), col, line );
    }

    return completed;
}

//BEGIN Helpers

TQStringList ReplaceWidget::workFiles()
{
    if ( m_dialog->files_all_radio->isChecked() )
    {
        return allProjectFiles();
    }
    else if ( m_dialog->files_open_radio->isChecked() )
    {
        return openProjectFiles();
    }
    return subProjectFiles( m_dialog->path_urlreq->lineEdit()->text() );
}

TQString ReplaceWidget::relativeProjectPath( TQString path )
{
    TQString project = m_part->project()->projectDirectory() + "/";
    if ( path.left( project.length() ) == project )
    {
        path = path.mid( project.length() );
    }
    return path;
}

TQString ReplaceWidget::fullProjectPath( TQString path )
{
    TQString project = m_part->project()->projectDirectory() + "/";
    if ( path.left( project.length() ) != project )
    {
        path = project + path;
    }
    return path;
}


TQStringList ReplaceWidget::allProjectFiles()
{
    TQStringList allfiles = m_part->project()->allFiles();

    TQStringList::iterator it = allfiles.begin();
    while ( it != allfiles.end() )
    {
        *it = fullProjectPath( *it );
        ++it;
    }
    return allfiles;
}

TQStringList ReplaceWidget::subProjectFiles( TQString const & subpath )
{
    TQStringList projectfiles = allProjectFiles();

    TQStringList::Iterator it = projectfiles.begin();
    while ( it != projectfiles.end() )
    {
        if ( (*it).left( subpath.length() ) != subpath)
        {
            it = projectfiles.remove( it );
        }
        else
        {
            ++it;
        }
    }
    return projectfiles;
}

TQStringList ReplaceWidget::openProjectFiles()
{
    TQStringList projectfiles = allProjectFiles();
    TQStringList openfiles;

    if( const TQPtrList<KParts::Part> * partlist = m_part->
            partController()->parts() )
    {
        TQPtrListIterator<KParts::Part> it( *partlist );
        while ( KParts::Part* part = it.current() )
        {
            if ( KTextEditor::Editor * ed = dynamic_cast<KTextEditor::Editor *>( part ) )
            {
                TQString editorpath = ed->url().path();
                if ( projectfiles.contains( editorpath ) )
                {
                    openfiles.append( editorpath );
                }
            }
            ++it;
        }
    }
    return openfiles;
}

KTextEditor::EditInterface * ReplaceWidget::getEditInterfaceForFile( TQString const & file )
{
    if( const TQPtrList<KParts::Part> * partlist = m_part->
            partController()->parts() )
    {
        TQPtrListIterator<KParts::Part> it( *partlist );
        while ( KParts::Part* part = it.current() )
        {
            if ( KTextEditor::Editor * ed = dynamic_cast<KTextEditor::Editor *>( part ) )
            {
                if ( file == ed->url().path() )
                {
                    return dynamic_cast<KTextEditor::EditInterface *>( part );
                }
            }
            ++it;
        }
    }
    return 0;
}

bool ReplaceWidget::shouldTerminate()
{
    bool b = _terminateOperation;
    _terminateOperation = false;
    return b;
}

void ReplaceWidget::focusInEvent( TQFocusEvent * /* e*/ )
{
    _listview->setFocus();
}

//END Helpers

#include "replace_widget.moc"
