/*
*  Copyright (C) 2003 Roberto Raggi (roberto@kdevelop.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.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*  Library General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; see the file COPYING.LIB.  If not, write to
*  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
*  Boston, MA 02110-1301, USA.
*
*/

#include "addmethoddialog.h"
#include "cppsupportpart.h"
#include "backgroundparser.h"
#include "cppsupport_utils.h"
#include "domutil.h"

#include <kdevpartcontroller.h>
#include <kdevcreatefile.h>

#include <tdelocale.h>
#include <tdefiledialog.h>
#include <tdeparts/part.h>
#include <tdetexteditor/editinterface.h>
#include <kdebug.h>
#include <klineedit.h>

#include <tqregexp.h>
#include <tqfileinfo.h>
#include <tqcombobox.h>
#include <tqlistview.h>
#include <tqcheckbox.h>
#include <tqpushbutton.h>
#include <tqtoolbutton.h>
#include <tqtextstream.h>

AddMethodDialog::AddMethodDialog( CppSupportPart* cppSupport, ClassDom klass,
                                  TQWidget* parent, const char* name, bool modal, WFlags fl )
: AddMethodDialogBase( parent, name, modal, fl ), m_cppSupport( cppSupport ), m_klass( klass ), m_count( 0 )
{
	TQString fileName = m_klass->fileName();

	access->insertStringList( TQStringList() << "Public" << "Protected" << "Private" << "Signals" <<
	                          "Public Slots" << "Protected Slots" << "Private Slots" );

	storage->insertStringList( TQStringList() << "Normal" << "Static" << "Virtual" << "Pure Virtual" << "Friend" );

	// setup sourceFile combo
	TQMap<TQString, bool> m;
#if 0 /// \FIXME ROBE

	FunctionList l = m_klass->functionList();
	{
		for ( FunctionList::Iterator it = l.begin(); it != l.end(); ++it )
		{
			if ( ( *it ) ->hasImplementation() )
				m.insert( ( *it ) ->implementedInFile(), true );
		}
	}
#endif

	{
		TQStringList headers = TQStringList::split( ",", "h,H,hh,hxx,hpp,inl,tlh,diff,ui.h" );
		TQStringList fileList;
		TQMap<TQString, bool>::Iterator it = m.begin();
		while ( it != m.end() )
		{
			TQString ext = TQFileInfo( it.key() ).extension();
			if ( !headers.contains( ext ) )
				sourceFile->insertItem( it.key() );
			++it;
		}

		if ( sourceFile->count() == 0 )
		{
			TQFileInfo info( fileName );
			TQString impl = DomUtil::readEntry( *cppSupport->projectDom(), "/cppsupportpart/filetemplates/implementationsuffix", "cpp" );
			sourceFile->insertItem( info.dirPath( true ) + "/" + info.baseName() + impl );
		}
	}

	returnType->setAutoCompletion( true );
	returnType->insertStringList( TQStringList()
	                              << "void"
	                              << "char"
	                              << "wchar_t"
	                              << "bool"
	                              << "short"
	                              << "int"
	                              << "long"
	                              << "signed"
	                              << "unsigned"
	                              << "float"
	                              << "double" );

	returnType->insertStringList( typeNameList( m_cppSupport->codeModel() ) );

	updateGUI();
	addMethod();
}

AddMethodDialog::~AddMethodDialog()
{}

void AddMethodDialog::reject()
{
	TQDialog::reject();
}

TQString AddMethodDialog::accessID( FunctionDom fun ) const
{
	if ( fun->isSignal() )
		return TQString::fromLatin1( "Signals" );

	switch ( fun->access() )
	{
	case CodeModelItem::Public:
		if ( fun->isSlot() )
			return TQString::fromLatin1( "Public Slots" );
		return TQString::fromLatin1( "Public" );

	case CodeModelItem::Protected:
		if ( fun->isSlot() )
			return TQString::fromLatin1( "Protected Slots" );
		return TQString::fromLatin1( "Protected" );

	case CodeModelItem::Private:
		if ( fun->isSlot() )
			return TQString::fromLatin1( "Private Slots" );
		return TQString::fromLatin1( "Private" );
	}

	return TQString();
}

void AddMethodDialog::accept()
{
	m_cppSupport->partController() ->editDocument( KURL( m_klass->fileName() ) );
	KTextEditor::EditInterface* editIface = dynamic_cast<KTextEditor::EditInterface*>( m_cppSupport->partController() ->activePart() );
	if ( !editIface )
	{
		/// @todo show messagebox
		TQDialog::accept();
		return ;
	}

	int line, column;
	m_klass->getEndPosition( &line, &column );

	// compute the insertion point map
	TQMap<TQString, TQPair<int, int> > points;
	TQStringList accessList;

	const FunctionList functionList = m_klass->functionList();
	for ( FunctionList::ConstIterator it = functionList.begin(); it != functionList.end(); ++it )
	{
		int funEndLine, funEndColumn;
		( *it ) ->getEndPosition( &funEndLine, &funEndColumn );
		TQString access = accessID( *it );
		TQPair<int, int> funEndPoint = qMakePair( funEndLine, funEndColumn );

		if ( !points.contains( access ) || points[ access ] < funEndPoint )
		{
			accessList.remove( access );
			accessList.push_back( access ); // move 'access' at the end of the list

			points[ access ] = funEndPoint;
		}
	}

	int insertedLine = 0;

	accessList += newAccessList( accessList );

	for ( TQStringList::iterator it = accessList.begin(); it != accessList.end(); ++it )
	{
		TQListViewItem* item = methods->firstChild();
		while ( item )
		{
			TQListViewItem * currentItem = item;

			item = item->nextSibling();

			if ( currentItem->text( 1 ) != *it )
				continue;

			TQString access = ( *it ).lower();

			bool isInline = currentItem->text( 0 ) == "True";
			TQString str = isInline ? functionDefinition( currentItem ) : functionDeclaration( currentItem );

			TQPair<int, int> pt;
			if ( points.contains( *it ) )
			{
				pt = points[ *it ];
			}
			else
			{
			str.prepend( access + ":\n" );
				points[ *it ] = qMakePair( line - 1, 0 );
				pt = points[ *it ]; // end of class declaration
			}

			editIface->insertText( pt.first + insertedLine + 1, 0 /*pt.second*/, str );
			insertedLine += str.contains( TQChar( '\n' ) );
		}
	}

	m_cppSupport->backgroundParser() ->addFile( m_klass->fileName() );

	TQString str;
	TQListViewItem* item = methods->firstChild();
	while ( item )
	{
		TQListViewItem * currentItem = item;

		item = item->nextSibling();

		TQString str = functionDefinition( currentItem );
		if ( str.isEmpty() )
			continue;

		TQString implementationFile = currentItem->text( 5 );
		if ( currentItem->text( 0 ) == "True" )
			implementationFile = m_klass->fileName();

		TQFileInfo fileInfo( implementationFile );
		if ( !TQFile::exists( fileInfo.absFilePath() ) )
		{
			if ( KDevCreateFile * createFileSupp = m_cppSupport->extension<KDevCreateFile>( "TDevelop/CreateFile" ) )
				createFileSupp->createNewFile( fileInfo.extension(), fileInfo.dirPath( true ), fileInfo.baseName() );
		}

		m_cppSupport->partController() ->editDocument( KURL( implementationFile ) );
		editIface = dynamic_cast<KTextEditor::EditInterface*>( m_cppSupport->partController() ->activePart() );
		if ( !editIface )
			continue;

		bool isInline = currentItem->text( 0 ) == "True";
		if ( !isInline )
		{
			editIface->insertLine( editIface->numLines(), TQString::fromLatin1( "" ) );
			editIface->insertText( editIface->numLines() - 1, 0, str );
			m_cppSupport->backgroundParser() ->addFile( implementationFile );
		}
	}

	TQDialog::accept();
}

void AddMethodDialog::updateGUI()
{
	bool enable = methods->selectedItem() != 0;

	returnType->setEnabled( enable );
	declarator->setEnabled( enable );
	access->setEnabled( enable );
	storage->setEnabled( enable );
	isInline->setEnabled( enable );

	sourceFile->setEnabled( enable );
	browseButton->setEnabled( enable );

	deleteMethodButton->setEnabled( enable );

	if ( enable )
	{
		TQListViewItem * item = methods->selectedItem();
		item->setText( 0, isInline->isChecked() ? "True" : "False" );
		item->setText( 1, access->currentText() );
		item->setText( 2, storage->currentText() );
		item->setText( 3, returnType->currentText() );
		item->setText( 4, declarator->text() );
		item->setText( 5, sourceFile->currentText() );

		if ( isInline->isChecked() || storage->currentText() == "Friend" || storage->currentText() == "Pure Virtual" )
		{
			sourceFile->setEnabled( false );
			browseButton->setEnabled( false );
		}
	}
}

void AddMethodDialog::addMethod()
{
	TQListViewItem * item = new TQListViewItem( methods, "False", "Public", "Normal",
	                                          "void", TQString( "method_%1()" ).arg( ++m_count ),
	                                          sourceFile->currentText() );
	methods->setCurrentItem( item );
	methods->setSelected( item, true );

	returnType->setFocus();
}

void AddMethodDialog::deleteCurrentMethod()
{
	delete( methods->currentItem() );
	updateGUI();
}

void AddMethodDialog::currentChanged( TQListViewItem* item )
{
	if ( item )
	{
		TQString _isInline = item->text( 0 );
		TQString _access = item->text( 1 );
		TQString _storage = item->text( 2 );
		TQString _returnType = item->text( 3 );
		TQString _declarator = item->text( 4 );
		TQString _sourceFile = item->text( 5 );

		isInline->setChecked( _isInline == "True" ? true : false );
		access->setCurrentText( _access );
		storage->setCurrentText( _storage );
		returnType->setCurrentText( _returnType );
		declarator->setText( _declarator );
		sourceFile->setCurrentText( _sourceFile );
	}

	updateGUI();
}

void AddMethodDialog::browseImplementationFile()
{
	TQString fileName = KFileDialog::getOpenFileName();
	sourceFile->setCurrentText( fileName );
	updateGUI();
}

TQString AddMethodDialog::functionDeclaration( TQListViewItem * item ) const
{
	TQString str;
	TQTextStream stream( &str, IO_WriteOnly );

	TQString access = item->text( 1 ).lower();

	stream << "    "; /// @todo use AStyle
	if ( item->text( 2 ) == "Virtual" || item->text( 2 ) == "Pure Virtual" )
		stream << "virtual ";
	else if ( item->text( 2 ) == "Friend" )
		stream << "friend ";
	else if ( item->text( 2 ) == "Static" )
		stream << "static ";
	stream << item->text( 3 ) << " " << item->text( 4 );
	if ( item->text( 2 ) == "Pure Virtual" )
		stream << " = 0";
	stream << ";\n";

	return str;
}

TQString AddMethodDialog::functionDefinition( TQListViewItem* item ) const
{
	if ( item->text( 1 ) == "Signals" || item->text( 2 ) == "Pure Virtual" ||
	     item->text( 2 ) == "Friend" )
	{
		return TQString();
	}

	TQString className = m_klass->name();
	TQString fullName = m_klass->scope().join( "::" );
	if ( !fullName.isEmpty() )
		fullName += "::";
	fullName += className;

	TQString str;
	TQTextStream stream( &str, IO_WriteOnly );

	bool isInline = item->text( 0 ) == "True";

	TQString ind;
	if ( isInline )
		ind.fill( TQChar( ' ' ), 4 );

	stream << "\n"
		<< ind << "/*!\n"
		<< ind << "    \\fn " << fullName << "::" << item->text( 4 ) << "\n"
		<< ind << " */\n";

	stream
		<< ind << item->text( 3 ) << " " << ( isInline ? TQString::fromLatin1( "" ) : fullName + "::" )
		<< item->text( 4 ) << "\n"
		<< ind << "{\n"
		<< ind << "    /// @todo implement me\n"
		<< ind << "}\n";

	return str;
}

TQStringList AddMethodDialog::newAccessList( const TQStringList& accessList ) const
{
	TQStringList newAccessList;

	TQListViewItem* item = methods->firstChild();
	while ( item )
	{
		TQListViewItem * currentItem = item;

		item = item->nextSibling();

		TQString access = currentItem->text( 1 );
		if ( !( accessList.contains( access ) || newAccessList.contains( access ) ) )
			newAccessList.push_back( access );
	}

	return newAccessList;
}

#include "addmethoddialog.moc"
