/***************************************************************************
 *   Copyright (C) 1999-2001 by Bernd Gehrmann                             *
 *   bernd@kdevelop.org                                                    *
 *                                                                         *
 *   Copyright (C) 2003 by Hamish Rodda                                    *
 *   meddie@yoyo.its.monash.edu.au                                         *
 *                                                                         *
 *   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 "appoutputwidget.h"

#include <tqregexp.h>
#include <tqbuttongroup.h>
#include <tqcheckbox.h>
#include <tqradiobutton.h>
#include <tqfile.h>
#include <tqtextstream.h>
#include <tqclipboard.h>

#include <tdelocale.h>
#include <kdebug.h>
#include <kstatusbar.h>
#include <tdeapplication.h>
#include <tdeconfig.h>
#include <tdepopupmenu.h>
#include <klineedit.h>
#include <tdefiledialog.h>

#include "appoutputviewpart.h"
#include "filterdlg.h"
#include "kdevpartcontroller.h"
#include "kdevmainwindow.h"
#include "kdevproject.h"

AppOutputWidget::AppOutputWidget(AppOutputViewPart* part)
    : ProcessWidget(0, "app output widget"), m_part(part)
{
	connect(this, TQT_SIGNAL(executed(TQListBoxItem*)), TQT_SLOT(slotRowSelected(TQListBoxItem*)));
	connect(this, TQT_SIGNAL(rightButtonClicked( TQListBoxItem *, const TQPoint & )), 
		TQT_SLOT(slotContextMenu( TQListBoxItem *, const TQPoint & )));
	TDEConfig *config = kapp->config();
	config->setGroup("General Options");
	setFont(config->readFontEntry("OutputViewFont"));
  setSelectionMode(TQListBox::Extended);
}

void AppOutputWidget::clearViewAndContents()
{
	m_contentList.clear();
	clear();
}

AppOutputWidget::~AppOutputWidget()
{}


void AppOutputWidget::childFinished(bool normal, int status)
{
    if( !stdoutbuf.isEmpty() )
        insertStdoutLine("");
    if( !stderrbuf.isEmpty() )
        insertStderrLine("");

    ProcessWidget::childFinished(normal, status);
}


void AppOutputWidget::slotRowSelected(TQListBoxItem* row)
{
	static TQRegExp assertMatch("ASSERT: \\\"([^\\\"]+)\\\" in ([^\\( ]+) \\(([\\d]+)\\)");
	static TQRegExp lineInfoMatch("\\[([^:]+):([\\d]+)\\]");
	static TQRegExp rubyErrorMatch("([^:\\s]+\\.rb):([\\d]+):?.*$");

	if (row) {
		if (assertMatch.exactMatch(row->text())) {
			m_part->partController()->editDocument(KURL( assertMatch.cap(2) ), assertMatch.cap(3).toInt() - 1);
			m_part->mainWindow()->statusBar()->message(i18n("Assertion failed: %1").arg(assertMatch.cap(1)), 10000);
			m_part->mainWindow()->lowerView(this);

		} else if (lineInfoMatch.search(row->text()) != -1) {
			m_part->partController()->editDocument(KURL( lineInfoMatch.cap(1) ), lineInfoMatch.cap(2).toInt() - 1);
			m_part->mainWindow()->statusBar()->message(row->text(), 10000);
			m_part->mainWindow()->lowerView(this);
		} else if (rubyErrorMatch.search(row->text()) != -1) {
			TQString file;
			if (rubyErrorMatch.cap(1).startsWith("/")) {
				file = rubyErrorMatch.cap(1);
			} else {
				file = m_part->project()->projectDirectory() + "/" + rubyErrorMatch.cap(1);
			}
			m_part->partController()->editDocument(KURL(rubyErrorMatch.cap(1)), rubyErrorMatch.cap(2).toInt() - 1);
			m_part->mainWindow()->statusBar()->message(row->text(), 10000);
			m_part->mainWindow()->lowerView(this);
		}
	}
}


void AppOutputWidget::insertStdoutLine(const TQCString &line)
{
// 	kdDebug(9004) << k_funcinfo << line << endl;

	if ( !m_part->isViewVisible() )
	{
		m_part->showView();
	}

        TQString sline;
        if( !stdoutbuf.isEmpty() )
        {
            sline = TQString::fromLocal8Bit( stdoutbuf+line );
            stdoutbuf.truncate( 0 );
        }else
        {
            sline = TQString::fromLocal8Bit( line );
        }

	m_contentList.append(TQString("o-")+sline);
	if ( filterSingleLine( sline ) )
	{
            ProcessWidget::insertStdoutLine( sline.local8Bit() );
	}
}


void AppOutputWidget::insertStderrLine(const TQCString &line)
{
// 	kdDebug(9004) << k_funcinfo << line << endl;

	if ( !m_part->isViewVisible() )
	{
		m_part->showView();
	}


        TQString sline;
        if( !stderrbuf.isEmpty() )
        {
            sline = TQString::fromLocal8Bit( stderrbuf+line );
            stderrbuf.truncate( 0 );
        }else
        {
            sline = TQString::fromLocal8Bit( line );
        }

	m_contentList.append(TQString("e-")+sline);
	if ( filterSingleLine( sline ) )
	{
            ProcessWidget::insertStderrLine( sline.local8Bit() );
	}
}

void AppOutputWidget::editFilter()
{
	FilterDlg dlg( this );
	dlg.caseSensitive->setChecked( m_filter.m_caseSensitive );
	dlg.regularExpression->setChecked( m_filter.m_isRegExp );
	dlg.filterString->setText( m_filter.m_filterString );

	if ( dlg.exec() == TQDialog::Accepted )
	{
		m_filter.m_caseSensitive = dlg.caseSensitive->isChecked();
		m_filter.m_isRegExp = dlg.regularExpression->isChecked();
		m_filter.m_filterString = dlg.filterString->text();

		m_filter.m_isActive = !m_filter.m_filterString.isEmpty();

		reinsertAndFilter();
	}

}
bool AppOutputWidget::filterSingleLine(const TQString & line)
{
	if ( !m_filter.m_isActive ) return true;

	if ( m_filter.m_isRegExp )
	{
		return ( line.find( TQRegExp( m_filter.m_filterString, m_filter.m_caseSensitive, false ) ) != -1 );
	}
	else
	{
		return ( line.find( m_filter.m_filterString, 0, m_filter.m_caseSensitive ) != -1 );
	}	
}

void AppOutputWidget::reinsertAndFilter()
{
	//copy the first item from the listbox
	//if a programm was started, this contains the issued command
	TQString issuedCommand;
	if ( count() > 0 ) 
	{
		setTopItem(0);
		issuedCommand = item(topItem())->text();
	}

	clear();

	//write back the issued command
	if ( !issuedCommand.isEmpty() )
	{
		insertItem( new ProcessListBoxItem( issuedCommand, ProcessListBoxItem::Diagnostic ) );
	}

	//grep through the list for items matching the filter...
	TQStringList strListFound;
	if ( m_filter.m_isActive )
	{	
		if ( m_filter.m_isRegExp )
		{
			strListFound = m_contentList.grep( TQRegExp(m_filter.m_filterString, m_filter.m_caseSensitive, false ) );
		}
		else
		{
			strListFound = m_contentList.grep( m_filter.m_filterString, m_filter.m_caseSensitive );
		}
	}
	else
	{
		strListFound = m_contentList;
	}

	//... and reinsert the found items into the listbox
	for ( TQStringList::Iterator it = strListFound.begin(); it != strListFound.end(); ++it )
	{
		if ((*it).startsWith("o-"))
		 {
			(*it).remove(0,2);
			insertItem(new ProcessListBoxItem(*it, ProcessListBoxItem::Normal));
		}
		else if ((*it).startsWith("e-"))
		{
			(*it).remove(0,2);
			insertItem(new ProcessListBoxItem(*it, ProcessListBoxItem::Error));
		}
	}
}

void AppOutputWidget::clearFilter()
{
	m_filter.m_isActive = false;
	reinsertAndFilter();
}

void AppOutputWidget::slotContextMenu( TQListBoxItem *, const TQPoint &p )
{
	TDEPopupMenu popup(this, "filter output");

	int id = popup.insertItem( i18n("Clear output"), this, TQT_SLOT(clearViewAndContents()) );
	popup.setItemEnabled( id, m_contentList.size() > 0 );

  popup.insertItem( i18n("Copy selected lines"), this, TQT_SLOT(copySelected()) );
  popup.insertSeparator();

	popup.insertItem( i18n("Save unfiltered"), this, TQT_SLOT(saveAll()) );
	id = popup.insertItem( i18n("Save filtered output"), this, TQT_SLOT(saveFiltered()) );
	popup.setItemEnabled( id, m_filter.m_isActive );
	popup.insertSeparator();

	id = popup.insertItem( i18n("Clear filter"), this, TQT_SLOT(clearFilter()) );
	popup.setItemEnabled( id, m_filter.m_isActive );

	popup.insertItem( i18n("Edit filter"), this, TQT_SLOT(editFilter() ) );

	popup.insertSeparator();
	popup.insertItem( i18n("Hide view"), this, TQT_SLOT(hideView()) );

	popup.exec(p);
}

void AppOutputWidget::hideView()
{
	m_part->hideView();
}

void AppOutputWidget::saveAll()
{
	saveOutputToFile( false );
}

void AppOutputWidget::saveFiltered()
{
	saveOutputToFile( true );
}

void AppOutputWidget::saveOutputToFile(bool useFilter)
{
	TQString filename = KFileDialog::getSaveFileName();

	if ( filename.isEmpty() ) return;

	TQStringList contents;
	if ( useFilter && m_filter.m_isActive )
	{	
		if ( m_filter.m_isRegExp )
		{
			contents = m_contentList.grep( TQRegExp(m_filter.m_filterString, m_filter.m_caseSensitive, false ) );
		}
		else
		{
			contents = m_contentList.grep( m_filter.m_filterString, m_filter.m_caseSensitive );
		}
	}
	else
	{
		contents = m_contentList;
	}

	TQFile file( filename );
	if ( file.open( IO_WriteOnly ) )
	{
		TQTextStream ostream( &file );
		TQStringList::ConstIterator it = contents.begin();
		while( it != contents.end() )
		{
			TQString line = *it;
			if ( line.startsWith("o-") || line.startsWith("e-") )
			{
				line.remove( 0, 2 );
			}
			ostream << line << endl;
			++it;
		}
		file.close();
	}
}

void AppOutputWidget::copySelected()
{
  uint n = count();
  TQString buffer;
  for (uint i = 0; i < n; i++)
  {
    if (isSelected(i))
      buffer += item(i)->text() + "\n";
  }
  kapp->clipboard()->setText(buffer, TQClipboard::Clipboard);
}

void AppOutputWidget::addPartialStderrLine(const TQCString & line)
{
    stderrbuf += line;
}

void AppOutputWidget::addPartialStdoutLine(const TQCString & line)
{
    stdoutbuf += line;
}

#include "appoutputwidget.moc"
