/***************************************************************************
                         cppcodecompletion.cpp  -  description
                            -------------------
   begin                : Sat Jul 21 2001
   copyright            : (C) 2001 by Victor R�er
   email                : victor_roeder@gmx.de
   copyright            : (C) 2002,2003 by Roberto Raggi
   email                : roberto@kdevelop.org
   copyright            : (C) 2005 by Adam Treat
   email                : manyoso@yahoo.com
***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 "cppcodecompletion.h"
#include "cppcodecompletionconfig.h"
#include "backgroundparser.h"
#include "ast.h"
#include "ast_utils.h"
#include "codeinformationrepository.h"
#include "parser.h"
#include "lexer.h"
#include "tree_parser.h"
#include "cpp_tags.h"
#include "cppsupport_utils.h"
#include "tag_creator.h"

#include <kapplication.h>
#include <kdebug.h>
#include <klocale.h>
#include <kmainwindow.h>
#include <kmessagebox.h>
#include <kparts/part.h>
#include <kstatusbar.h>
#include <ktexteditor/document.h>

#include <qdatastream.h>
#include <qfile.h>
#include <qmap.h>
#include <qregexp.h>
#include <qstatusbar.h>
#include <qstring.h>
#include <qstringlist.h>
#include <qpair.h>
#include <qvaluestack.h>

#include <kdevpartcontroller.h>
#include <kdevmainwindow.h>
#include <kdevproject.h>
#include <kdevcoderepository.h>

class SimpleVariable
{
public:
	SimpleVariable()
	{}
	SimpleVariable( const SimpleVariable& source )
			: name( source.name ), 
			type( source.type ), 
			ptrList( source.ptrList )
	{}
	~SimpleVariable()
	{}

	SimpleVariable& operator = ( const SimpleVariable& source )
	{
		name = source.name;
		type = source.type;
		ptrList = source.ptrList;
		return *this;
	}

	QString name;
	QStringList type;
	QStringList ptrList;
};

class SimpleContext
{
public:
	SimpleContext( SimpleContext* prev = 0 )
			: m_prev( prev )
	{}

	virtual ~SimpleContext()
	{
		if ( m_prev )
		{
			delete( m_prev );
			m_prev = 0;
		}
	}

	SimpleContext* prev() const
	{
		return m_prev;
	}

	void attach( SimpleContext* ctx )
	{
		m_prev = ctx;
	}

	void detach()
	{
		m_prev = 0;
	}

	const QValueList<SimpleVariable>& vars() const
	{
		return m_vars;
	}

	void add( const SimpleVariable& v )
	{
		m_vars.append( v );
	}
	
	void add( const QValueList<SimpleVariable>& vars )
	{
		m_vars += vars;
	}

	SimpleVariable findVariable( const QString& varname )
	{
		SimpleContext * ctx = this;
		while ( ctx )
		{
			const QValueList<SimpleVariable>& vars = ctx->vars();
			for ( int i = vars.count() - 1; i >= 0; --i )
			{
				SimpleVariable v = vars[ i ];
				if ( v.name == varname )
					return v;
			}
			ctx = ctx->prev();
		}
		return SimpleVariable();
	}

private:
	QValueList<SimpleVariable> m_vars;
	SimpleContext* m_prev;
};

struct RecoveryPoint
{
	int kind;
	QStringList scope;
	QValueList<QStringList> imports;

	int startLine, startColumn;
	int endLine, endColumn;

	RecoveryPoint()
			: kind( 0 ), startLine( 0 ), startColumn( 0 ),
			endLine( 0 ), endColumn( 0 )
	{}

private:
	RecoveryPoint( const RecoveryPoint& source );
	void operator = ( const RecoveryPoint& source );
};

struct CppCodeCompletionData
{
	QPtrList<RecoveryPoint> recoveryPoints;
	QStringList classNameList;

	CppCodeCompletionData()
	{
		recoveryPoints.setAutoDelete( true );
	}

	RecoveryPoint* findRecoveryPoint( int line, int column )
	{
		if ( recoveryPoints.count() == 0 )
			return 0;

		QPair<int, int> pt = qMakePair( line, column );

		QPtrListIterator<RecoveryPoint> it( recoveryPoints );
		RecoveryPoint* recPt = 0;

		while ( it.current() )
		{
			QPair<int, int> startPt = qMakePair( it.current() ->startLine, it.current() ->startColumn );
			QPair<int, int> endPt = qMakePair( it.current() ->endLine, it.current() ->endColumn );

			if ( pt < startPt )
				break;

			recPt = it.current();

			++it;
		}

		return recPt;
	}

};

static QString toSimpleName( NameAST* name )
{
	if ( !name )
		return QString::null;

	QString s;
	QPtrList<ClassOrNamespaceNameAST> l = name->classOrNamespaceNameList();
	QPtrListIterator<ClassOrNamespaceNameAST> nameIt( l );
	while ( nameIt.current() )
	{
		if ( nameIt.current() ->name() )
		{
			s += nameIt.current() ->name() ->text() + "::";
		}
		++nameIt;
	}

	if ( name->unqualifiedName() && name->unqualifiedName() ->name() )
		s += name->unqualifiedName() ->name() ->text();

	return s;
}

bool operator < ( const KTextEditor::CompletionEntry& e1, const KTextEditor::CompletionEntry& e2 )
{
	return e1.text < e2.text;
}

static QValueList<KTextEditor::CompletionEntry> unique( const QValueList<KTextEditor::CompletionEntry>& entryList )
{

	QValueList< KTextEditor::CompletionEntry > l;
	QMap<QString, bool> map;
	QValueList< KTextEditor::CompletionEntry >::ConstIterator it = entryList.begin();
	while ( it != entryList.end() )
	{
		KTextEditor::CompletionEntry e = *it++;
		QString key = ( e.type + " " +
		                e.prefix + " " +
		                e.text + " " +
		                e.postfix + " " ).simplifyWhiteSpace().stripWhiteSpace();
		if ( map.find( key ) == map.end() )
		{
			map[ key ] = TRUE;
			l << e;
		}
	}
	return l;
}

static QStringList unique( const QStringList& entryList )
{

	QStringList l;
	QMap<QString, bool> map;
	QStringList::ConstIterator it = entryList.begin();
	while ( it != entryList.end() )
	{
		QString e = *it++;
		if ( map.find( e ) == map.end() )
		{
			map[ e ] = TRUE;
			l << e;
		}
	}
	return l;
}

CppCodeCompletion::CppCodeCompletion( CppSupportPart* part )
		: d( new CppCodeCompletionData ),
		//Matches on includes
		m_includeRx( "^\\s*#\\s*include\\s+[\"<]" ),
		//Matches on C++ and C style comments as well as literal strings
		m_cppCodeCommentsRx("(//([^\n]*)(\n|$)|/\\*.*\\*/|\"([^\\\\]|\\\\.)*\")"),
		//Matches on alpha chars and '.'
		m_codeCompleteChRx("([A-Z])|([a-z])|(\\.)"),
		//Matches on "->" and "::"
		m_codeCompleteCh2Rx("(->)|(\\:\\:)")

{
	m_cppCodeCommentsRx.setMinimal( true );
		
	m_pSupport = part;

	m_activeCursor = 0;
	m_activeEditor = 0;
	m_activeCompletion = 0;
	m_ccTimer = new QTimer( this );
	m_ccLine = 0;
	m_ccColumn = 0;
	connect( m_ccTimer, SIGNAL( timeout() ), this, SLOT( slotTimeout() ) );

	computeFileEntryList();

	CppSupportPart* cppSupport = m_pSupport;
	connect( cppSupport->project(), SIGNAL( addedFilesToProject( const QStringList& ) ), 
	         this, SLOT( computeFileEntryList() ) );
	connect( cppSupport->project(), SIGNAL( removedFilesFromProject( const QStringList& ) ), 
	         this, SLOT( computeFileEntryList() ) );

	m_bArgHintShow = false;
	m_bCompletionBoxShow = false;
	m_blockForKeyword = false;
	m_demandCompletion = false;
	m_completionMode = NormalCompletion;

	m_repository = new CodeInformationRepository( cppSupport->codeRepository() );
	setupCodeInformationRepository();

	if ( part->partController() ->parts() )
	{
		QPtrListIterator<KParts::Part> it( *part->partController() ->parts() );
		while ( KParts::Part * part = it.current() )
		{
			integratePart( part );
			++it;
		}
	}

	if ( part->partController() ->activePart() )
		slotActivePartChanged( part->partController() ->activePart() );

	connect( part->partController( ), SIGNAL( partAdded( KParts::Part* ) ),
	         this, SLOT( slotPartAdded( KParts::Part* ) ) );
	connect( part->partController( ), SIGNAL( activePartChanged( KParts::Part* ) ),
	         this, SLOT( slotActivePartChanged( KParts::Part* ) ) );

	connect( part, SIGNAL( fileParsed( const QString& ) ), 
	         this, SLOT( slotFileParsed( const QString& ) ) );
}

CppCodeCompletion::~CppCodeCompletion( )
{
	delete m_repository;
	delete d;
}

void CppCodeCompletion::slotTimeout()
{
	if ( !m_activeCursor || !m_activeEditor || !m_activeCompletion )
		return ;

	uint nLine, nCol;
	m_activeCursor->cursorPositionReal( &nLine, &nCol );

	if ( nLine != m_ccLine || nCol != m_ccColumn )
		return ;

	QString textLine = m_activeEditor->textLine( nLine );
	QChar ch = textLine[ nCol ];
	if ( ch.isLetterOrNumber() || ch == '_' )
		return ;

	completeText();
}

void CppCodeCompletion::slotArgHintHidden()
{
	//kdDebug(9007) << "CppCodeCompletion::slotArgHintHidden()" << endl;
	m_bArgHintShow = false;
}

void CppCodeCompletion::slotCompletionBoxHidden()
{
	//kdDebug( 9007 ) << "CppCodeCompletion::slotCompletionBoxHidden()" << endl;
	m_bCompletionBoxShow = false;
}

void CppCodeCompletion::integratePart( KParts::Part* part )
{
	if ( !part || !part->widget() )
		return ;

	KTextEditor::Document* doc = dynamic_cast<KTextEditor::Document*>( part );
	if ( doc )
	{
		kdDebug( 9007 ) << k_funcinfo << "integrate document: " << doc << endl;

		if ( m_pSupport && m_pSupport->codeCompletionConfig() ->automaticCodeCompletion() )
		{
			kdDebug( 9007 ) << k_funcinfo << "enabling code completion" << endl;
			connect( part, SIGNAL( textChanged() ), this, SLOT( slotTextChanged() ) );
			connect( part->widget(), SIGNAL( completionDone() ), this,
			         SLOT( slotCompletionBoxHidden() ) );
			connect( part->widget(), SIGNAL( completionAborted() ), this,
			         SLOT( slotCompletionBoxHidden() ) );
			connect( part->widget(), SIGNAL( argHintHidden() ), this,
			         SLOT( slotArgHintHidden() ) );
		}

	}
}

void CppCodeCompletion::slotPartAdded( KParts::Part *part )
{
	integratePart( part );
}

void CppCodeCompletion::slotActivePartChanged( KParts::Part *part )
{
	if ( !part )
		return ;

	kdDebug( 9007 ) << k_funcinfo << endl;

	m_activeFileName = QString::null;

	KTextEditor::Document* doc = dynamic_cast<KTextEditor::Document*>( part );
	if ( !doc )
		return ;

	m_activeFileName = doc->url().path();

	// if the interface stuff fails we should disable codecompletion automatically
	m_activeEditor = dynamic_cast<KTextEditor::EditInterface*>( part );
	if ( !m_activeEditor )
	{
		kdDebug( 9007 ) << "Editor doesn't support the EditDocumentIface" << endl;
		return ;
	}

	m_activeCursor = dynamic_cast<KTextEditor::ViewCursorInterface*>( part->widget() );
	if ( !m_activeCursor )
	{
		kdDebug( 9007 ) << "The editor doesn't support the CursorDocumentIface!" << endl;
		return ;
	}

	m_activeCompletion = dynamic_cast<KTextEditor::CodeCompletionInterface*>( part->widget() );
	if ( !m_activeCompletion )
	{
		kdDebug( 9007 ) << "Editor doesn't support the CompletionIface" << endl;
		return ;
	}

	kdDebug( 9007 ) << k_funcinfo << "-- end" << endl;
}

void CppCodeCompletion::slotTextChanged()
{
	m_ccTimer->stop();

	if ( !m_activeCursor )
		return ;

	unsigned int nLine, nCol;
	m_activeCursor->cursorPositionReal( &nLine, &nCol );

	QString strCurLine = m_activeEditor->textLine( nLine );
	QString ch = strCurLine.mid( nCol - 1, 1 );
	QString ch2 = strCurLine.mid( nCol - 2, 2 );
	
	// Tell the completion box to _go_away_ when the completion char
	// becomes empty or whitespace and the box is already showing.
	// !!WARNING!! This is very hackish, but KTE doesn't offer a way
	// to tell the completion box to _go_away_
	if ( ch.simplifyWhiteSpace().isEmpty() && 
	     !strCurLine.simplifyWhiteSpace().contains("virtual") &&
	     m_bCompletionBoxShow )
	{
		QValueList<KTextEditor::CompletionEntry> entryList;
		m_bCompletionBoxShow = true;
		m_activeCompletion->showCompletionBox( entryList, 0 );
	}
	
	m_ccLine = 0;
	m_ccColumn = 0;
	
	bool argsHint = m_pSupport->codeCompletionConfig() ->automaticArgumentsHint();
	bool codeComplete = m_pSupport->codeCompletionConfig() ->automaticCodeCompletion();
	bool headComplete = m_pSupport->codeCompletionConfig() ->automaticHeaderCompletion();
	
	// m_codeCompleteChRx completes on alpha chars and '.'
	// m_codeCompleteCh2Rx completes on "->" and "::"

	if ( ( argsHint && ch == "(" ) ||
	     ( strCurLine.simplifyWhiteSpace().contains("virtual") ) ||
	     ( codeComplete && ( m_codeCompleteChRx.search( ch ) != -1 || 
	                         m_codeCompleteCh2Rx.search( ch2 ) != -1 ) ) ||
	     ( headComplete && ( ch == "\"" || ch == "<" ) && m_includeRx.search( strCurLine ) != -1 ) )
	{
		int time;
		m_ccLine = nLine;
		m_ccColumn = nCol;
		if ( ch == "(" )
			time = m_pSupport->codeCompletionConfig() ->argumentsHintDelay();
		else
			time = m_pSupport->codeCompletionConfig() ->codeCompletionDelay();
		m_ccTimer->start( time, true );
	}
}

enum { T_ACCESS, T_PAREN, T_BRACKET, T_IDE, T_UNKNOWN };

int CppCodeCompletion::expressionAt( const QString& contents, int index )
{
	kdDebug( 9007 ) << k_funcinfo << endl;

	/* C++ style comments present issues with finding the expr so I'm
		matching for them and replacing them with empty C style comments
		of the same length for purposes of finding the expr. */

	QString text = contents;

	int pos = 0;
	while ( (pos = m_cppCodeCommentsRx.search( text, pos )) != -1 ) 
	{
		if ( m_cppCodeCommentsRx.cap( 1 ).startsWith( "//" ) ) 
		{
			QString before = m_cppCodeCommentsRx.cap( 1 );
			QString after;
			after.fill(' ', before.length() - 5 );
			after.prepend( "/*" );
			after.append( "*/" );
			text.replace( pos, before.length() - 1, after );
			pos += after.length();
		}
		else
		{
			pos += m_cppCodeCommentsRx.matchedLength();
		}
	}

	int last = T_UNKNOWN;
	int start = index;
	while ( index > 0 )
	{
		while ( index > 0 && text[ index ].isSpace() )
		{
			--index;
		}

		QChar ch = text[ index ];
		QString ch2 = text.mid( index - 1, 2 );
		if ( ( last != T_IDE ) && ( ch.isLetterOrNumber() || ch == '_' ) )
		{
			while ( index > 0 && ( text[ index ].isLetterOrNumber() || text[ index ] == '_' ) )
			{
				--index;
			}
			last = T_IDE;
		}
		else if ( last != T_IDE && ch == ')' )
		{
			int count = 0;
			while ( index > 0 )
			{
				QChar ch = text[ index ];
				if ( ch == '(' )
				{
					++count;
				}
				else if ( ch == ')' )
				{
					--count;
				}
				else if ( count == 0 )
				{
					--index;
					last = T_PAREN;
					break;
				}
				--index;
			}
		}
		else if ( ch == ']' )
		{
			int count = 0;
			while ( index > 0 )
			{
				QChar ch = text[ index ];
				if ( ch == '[' )
				{
					++count;
				}
				else if ( ch == ']' )
				{
					--count;
				}
				else if ( count == 0 )
				{
					--index;
					last = T_BRACKET;
					break;
				}
				--index;
			}
		}
		else if ( ch == '.' )
		{
			--index;
			last = T_ACCESS;
		}
		else if ( ch2 == "::" )
		{
			index -= 2;
			last = T_ACCESS;
		}
		else if ( ch2 == "->" )
		{
			index -= 2;
			last = T_ACCESS;
		}
		else
		{
			if ( start > index )
			{
				++index;
			}
			last = T_UNKNOWN;
			break;
		}
	}
	return index;
}

QStringList CppCodeCompletion::splitExpression( const QString& text )
{
#define ADD_CURRENT()\
 if( current.length() ) { l << current; /*kdDebug(9007) << "add word " << current << endl;*/ current = ""; }

	QStringList l;
	uint index = 0;
	QString current;
	while ( index < text.length() )
	{
		QChar ch = text[ index ];
		QString ch2 = text.mid( index, 2 );

		if ( ch == '.' )
		{
			current += ch;
			ADD_CURRENT();
			++index;
		}
		else if ( ch == '(' )
		{
			int count = 0;
			while ( index < text.length() )
			{
				QChar ch = text[ index ];
				if ( ch == '(' )
				{
					++count;
				}
				else if ( ch == ')' )
				{
					--count;
				}
				else if ( count == 0 )
				{
					break;
				}
				current += ch;
				++index;
			}
		}
		else if ( ch == '[' )
		{
			int count = 0;
			while ( index < text.length() )
			{
				QChar ch = text[ index ];
				if ( ch == '[' )
				{
					++count;
				}
				else if ( ch == ']' )
				{
					--count;
				}
				else if ( count == 0 )
				{
					break;
				}
				current += ch;
				++index;
			}
		}
		else if ( ch2 == "->" )
		{
			current += ch2;
			ADD_CURRENT();
			index += 2;
		}
		else
		{
			current += text[ index ];
			++index;
		}
	}
	ADD_CURRENT();
	return l;
}

QStringList CppCodeCompletion::evaluateExpression( QString expr, SimpleContext* ctx )
{
	d->classNameList = typeNameList( m_pSupport->codeModel() );

	bool global = false;
	if ( expr.startsWith( "::" ) )
	{
		expr = expr.mid( 2 );
		global = true;
	}
	
	QStringList exprList = splitExpression( expr );
	QStringList type = evaluateExpressionInternal( exprList, QStringList(), ctx );
	
	QMap<QString, QString> typedefs = typedefMap( m_pSupport->codeModel() );
	
	QStringList::iterator it = type.begin();
	for ( ; it != type.end(); ++it )
	{
		if ( typedefs.contains( ( *it ) ) )
			( *it ) = typedefs[ ( *it ) ];
	}
	
	m_pSupport->mainWindow() ->statusBar() ->message( i18n( "Type of %1 is %2" ).arg( expr ).arg( type.join( "::" ) ), 1000 );

	QMap<QString, bool> visited;
	bool isTypedef = true;

	while ( isTypedef && !type.isEmpty() )
	{
		// check if the evaluateded type is a typedef
		QValueList<Catalog::QueryArgument> args;
		args << Catalog::QueryArgument( "kind", Tag::Kind_Typedef );
		args << Catalog::QueryArgument( "name", type.last() );
		QValueList<Tag> tags = m_repository->query( args );
		isTypedef = !tags.isEmpty() && !visited.contains( type.last() );

		if ( isTypedef )
		{
			visited[ type.last() ] = true;

			bool found = false;
			for ( QValueList<Tag>::Iterator it = tags.begin(); it != tags.end(); ++it )
			{
				Tag& tag = *it;

				QStringList tp = type;
				tp.pop_back();

				QString typeScope = tp.join( "::" );
				QString tagScope = tag.scope().join( "::" );

				//kdDebug(9007) << "=========> typeScope: " << typeScope << endl;
				//kdDebug(9007) << "=========> tagScope: " << tagScope << endl;

				if ( typeScope.endsWith( tagScope ) )
				{
					type = typeName( tag.attribute( "t" ).toString() );
					found = true;
					break;
				}
			}

			if ( !found )
				type = typeName( tags[ 0 ].attribute( "t" ).toString() );
		}
	}

	return type;
}

QStringList CppCodeCompletion::evaluateExpressionInternal( QStringList & exprList, const QStringList & scope, SimpleContext * ctx )
{
	//kdDebug(9007) << "evaluateExpression " << exprList << " in " << scope << endl;

	if ( exprList.isEmpty() )
		return scope;

	QString currentExpr = exprList.front().stripWhiteSpace();
	exprList.pop_front();
	
	MemberAccessOp accessOp;
	if ( currentExpr.endsWith( "." ) )
	{	
		accessOp = DotOp;
		currentExpr.truncate( currentExpr.length() - 1 );
	}
	else if ( currentExpr.endsWith( "->" ) )
	{
		accessOp = ArrowOp;
		currentExpr.truncate( currentExpr.length() - 2 );
	}
	else
		accessOp = NoOp;
		
	int leftParen = currentExpr.find( "(" );

	if ( leftParen != -1 )
		currentExpr = currentExpr.left( leftParen ).stripWhiteSpace();

	if ( currentExpr.contains( "::" ) )
	{
		if ( currentExpr.endsWith( "::" ) )
			currentExpr.truncate( currentExpr.length() - 2 );

		QStringList type = typeName( currentExpr );
		if ( !type.isEmpty() )
		{
			if ( leftParen != -1 )
			{
				QString name = type.back();
				type.pop_back();
				type = typeOf( name, type, accessOp );
			}

			if ( !type.isEmpty() )
				return evaluateExpressionInternal( exprList, type );
		}
	}

	if ( ctx )
	{
		// find the variable type in the current context
		SimpleVariable var = ctx->findVariable( currentExpr );
		QStringList type = var.type;
		
		if ( !type.isEmpty() && correctAccessOp( var.ptrList, accessOp ) )
			return evaluateExpressionInternal( exprList, type );

		QStringList t_this = ctx->findVariable( "this" ).type;
		if ( !t_this.isEmpty() )
		{
			QStringList type = typeOf( currentExpr, t_this, accessOp );
			if ( !type.isEmpty() )
				return evaluateExpressionInternal( exprList, type );

			return QStringList();
		}
	}

	QStringList type = typeOf( currentExpr, scope, accessOp );
	if ( !type.isEmpty() )
		return evaluateExpressionInternal( exprList, type );

	return QStringList();
}

bool CppCodeCompletion::correctAccessOp( QStringList ptrList, MemberAccessOp accessOp )
{
	if ( m_demandCompletion || accessOp == NoOp )
		return true;
	
	//Remove the vars that don't correspond to the member access operator
	//that we are using.  
	
	///@todo: Take into account the de-reference operator...
	bool arrowOp = accessOp == ArrowOp && ptrList.count() && ptrList[0] == "*";
	bool dotOp =  accessOp == DotOp && ( !ptrList.count() || ptrList[0] == "&" );
	return arrowOp || dotOp;
}

void CppCodeCompletion::completeText( bool invokedOnDemand /*= false*/ )
{
	kdDebug( 9007 ) << "CppCodeCompletion::completeText()" << endl;

	if ( !m_pSupport || !m_activeCursor || !m_activeEditor || !m_activeCompletion )
		return ;
	
	m_demandCompletion = invokedOnDemand;
	
	unsigned int line, column;
	m_activeCursor->cursorPositionReal( &line, &column );

	int nLine = line, nCol = column;

	QString strCurLine = m_activeEditor->textLine( nLine );

	QString ch = strCurLine.mid( nCol - 1, 1 );
	QString ch2 = strCurLine.mid( nCol - 2, 2 );

	if ( m_includeRx.search( strCurLine ) != -1 )
	{
		if ( !m_fileEntryList.isEmpty() )
		{
			m_bCompletionBoxShow = true;
			m_activeCompletion->showCompletionBox( m_fileEntryList, column - m_includeRx.matchedLength() );
		}
		return ;
	}

	bool showArguments = false;
	bool isInstance = true;
	m_completionMode = NormalCompletion;
		
	if ( ch2 == "->" || ch == "." || ch == "(" )
	{
		int pos = ch2 == "->" ? nCol - 3 : nCol - 2;
		QChar c = strCurLine[ pos ];
		while ( pos > 0 && c.isSpace() )
			c = strCurLine[ --pos ];

		if ( !( c.isLetterOrNumber() || c == '_' || c == ')' ) )
			return ;
	}

	if ( ch == "(" )
	{
		--nCol;
		while ( nCol > 0 && strCurLine[ nCol ].isSpace() )
			--nCol;

		showArguments = TRUE;
	}

	QStringList type, this_type;
	QString expr, word;

	DeclarationAST::Node recoveredDecl;
	TypeSpecifierAST::Node recoveredTypeSpec;

	SimpleContext* ctx = 0;

	m_pSupport->backgroundParser() ->lock ()
	;

	if ( RecoveryPoint * recoveryPoint = d->findRecoveryPoint( line, column ) )
	{
		QString textLine = m_activeEditor->textLine( recoveryPoint->startLine );
		kdDebug( 9007 ) << "node-kind = " << nodeTypeToString( recoveryPoint->kind ) << endl;
		kdDebug( 9007 ) << "startLine = " << textLine << endl;

		if ( recoveryPoint->kind == NodeType_FunctionDefinition )
		{

			QString textToReparse = getText( recoveryPoint->startLine, recoveryPoint->startColumn,
			                                 line, showArguments ? column - 1 : column );
// 			kdDebug(9007) << "-------------> please reparse only text" << endl << textToReparse << endl
// 			             << "--------------------------------------------" << endl;

			Driver d;
			Lexer lexer( &d );
			/// @todo setup the lexer(i.e. adds macro, special words, ...

			lexer.setSource( textToReparse );
			Parser parser( &d, &lexer );

			parser.parseDeclaration( recoveredDecl );
/*			kdDebug(9007) << "recoveredDecl = " << recoveredDecl.get() << endl;*/
			if ( recoveredDecl.get() )
			{

				bool isFunDef = recoveredDecl->nodeType() == NodeType_FunctionDefinition;
				kdDebug( 9007 ) << "is function definition = " << isFunDef << endl;

				int endLine, endColumn;
				recoveredDecl->getEndPosition( &endLine, &endColumn );
				kdDebug( 9007 ) << "endLine = " << endLine << ", endColumn " << endColumn << endl;

				/// @todo check end position

				if ( isFunDef )
				{
					FunctionDefinitionAST * def = static_cast<FunctionDefinitionAST*>( recoveredDecl.get() );

					/// @todo remove code duplication

					QString contents = textToReparse;
					int start_expr = expressionAt( contents, contents.length() - 1 );

					// kdDebug(9007) << "start_expr = " << start_expr << endl;
					if ( start_expr != int( contents.length() ) - 1 )
						expr = contents.mid( start_expr, contents.length() - start_expr ).stripWhiteSpace();

					if ( expr.startsWith( "SIGNAL" ) || expr.startsWith( "SLOT" ) )
					{
						m_completionMode = expr.startsWith( "SIGNAL" ) ? SignalCompletion : SlotCompletion;

						showArguments = false;
						int end_expr = start_expr - 1;
						while ( end_expr > 0 && contents[ end_expr ].isSpace() )
							--end_expr;

						if ( contents[ end_expr ] != ',' )
						{
							expr = QString::null;
						}
						else
						{
							--end_expr;
							start_expr = expressionAt( contents, end_expr );
							expr = contents.mid( start_expr, end_expr - start_expr + 1 ).stripWhiteSpace();
						}
					}
					else
					{
						int idx = expr.length() - 1;
						while ( expr[ idx ].isLetterOrNumber() || expr[ idx ] == '_' )
							--idx;

						if ( idx != int( expr.length() ) - 1 )
						{
							++idx;
							word = expr.mid( idx ).stripWhiteSpace();
							expr = expr.left( idx ).stripWhiteSpace();
						}
					}

					ctx = computeContext( def, endLine, endColumn );
					DeclaratorAST* d = def->initDeclarator() ->declarator();
					NameAST* name = d->declaratorId();
					QString scope = recoveryPoint->scope.join( "::" );

					QStringList nested;

					QPtrList<ClassOrNamespaceNameAST> l;
					if ( name )
					{
						l = name->classOrNamespaceNameList();
					}
					//		    QPtrList<ClassOrNamespaceNameAST> l = name->classOrNamespaceNameList();
					QPtrListIterator<ClassOrNamespaceNameAST> nameIt( l );
					while ( nameIt.current() )
					{
						if ( nameIt.current() ->name() )
						{
							nested << nameIt.current() ->name() ->text();
						}
						++nameIt;
					}

					QString s = nested.join( "::" );

					if ( !scope.isEmpty() )
					{
						scope += QString::fromLatin1( "::" ) + s;
					}
					else
					{
						scope += s;
					}

					if ( scope.endsWith( "::" ) )
					{
						scope = scope.left( scope.length() - 2 );
					}

					if ( scope.startsWith( "::" ) )
					{
						scope = scope.mid( 2 );
					}

					if ( !scope.isEmpty() )
					{
						SimpleVariable var;
						this_type = typeName( scope );
						var.type = this_type;
						var.name = "this";
						ctx->add( var );
					}

					type = evaluateExpression( expr, ctx );
				}
			}
			else
			{
				kdDebug( 9007 ) << "no valid declaration to recover!!!" << endl;
			}
		}
		else if ( recoveryPoint->kind == NodeType_ClassSpecifier )
		{
			QString textToReparse = getText( recoveryPoint->startLine, recoveryPoint->startColumn,
			                                 recoveryPoint->endLine, recoveryPoint->endColumn, line );
// 			kdDebug(9007) << "-------------> please reparse only text" << endl << textToReparse << endl
// 			             << "--------------------------------------------" << endl;

			Driver d;
			Lexer lexer( &d );
			/// @todo setup the lexer(i.e. adds macro, special words, ...

			lexer.setSource( textToReparse );
			Parser parser( &d, &lexer );

			parser.parseClassSpecifier( recoveredTypeSpec );
/*			kdDebug(9007) << "recoveredDecl = " << recoveredTypeSpec.get() << endl;*/
			if ( recoveredTypeSpec.get() )
			{

				ClassSpecifierAST * clazz = static_cast<ClassSpecifierAST*>( recoveredTypeSpec.get() );

				QString keyword = getText( line, 0, line, column ).simplifyWhiteSpace();
	
				kdDebug(9007) << "===========================> keyword is: " << keyword << endl;
	
				if ( keyword == "virtual" )
				{
					BaseClauseAST *baseClause = clazz->baseClause();
					if ( baseClause )
					{
						QPtrList<BaseSpecifierAST> baseList = baseClause->baseSpecifierList();
						QPtrList<BaseSpecifierAST>::iterator it = baseList.begin();
	
						for ( ; it != baseList.end(); ++it )
							type.append( ( *it )->name()->text() );
						
						ctx = new SimpleContext();
						
						showArguments = false;
						m_completionMode = VirtualDeclCompletion;
	
						kdDebug(9007) << "------> found virtual keyword for class specifier '" 
									<< clazz->text() << "'" << endl;
					}
				}
				else if ( QString("virtual").find( keyword ) != -1 )
					m_blockForKeyword = true;
				else
					m_blockForKeyword = false;
			}
		}
	}

	if ( !recoveredDecl.get() && !recoveredTypeSpec.get() )
	{
		TranslationUnitAST * ast = m_pSupport->backgroundParser() ->translationUnit( m_activeFileName );
		if ( AST * node = findNodeAt( ast, line, column ) )
		{
			kdDebug( 9007 ) << "------------------- AST FOUND --------------------" << endl;
			kdDebug( 9007 ) << "node-kind = " << nodeTypeToString( node->nodeType() ) << endl;

			if ( FunctionDefinitionAST * def = functionDefinition( node ) )
			{
				kdDebug( 9007 ) << "------> found a function definition" << endl;

				int startLine, startColumn;
				def->getStartPosition( &startLine, &startColumn );

				QString contents = getText( startLine, startColumn, line, showArguments ? column - 1 : column );


				/// @todo remove code duplication
				int start_expr = expressionAt( contents, contents.length() - 1 );

				// kdDebug(9007) << "start_expr = " << start_expr << endl;
				if ( start_expr != int( contents.length() ) - 1 )
					expr = contents.mid( start_expr, contents.length() - start_expr ).stripWhiteSpace();

				if ( expr.startsWith( "SIGNAL" ) || expr.startsWith( "SLOT" ) )
				{
					m_completionMode = expr.startsWith( "SIGNAL" ) ? SignalCompletion : SlotCompletion;

					showArguments = false;
					int end_expr = start_expr - 1;
					while ( end_expr > 0 && contents[ end_expr ].isSpace() )
						--end_expr;

					if ( contents[ end_expr ] != ',' )
					{
						expr = QString::null;
					}
					else
					{
						--end_expr;
						start_expr = expressionAt( contents, end_expr );
						expr = contents.mid( start_expr, end_expr - start_expr + 1 ).stripWhiteSpace();
					}
				}
				else
				{
					int idx = expr.length() - 1;
					while ( expr[ idx ].isLetterOrNumber() || expr[ idx ] == '_' )
						--idx;

					if ( idx != int( expr.length() ) - 1 )
					{
						++idx;
						word = expr.mid( idx ).stripWhiteSpace();
						expr = expr.left( idx ).stripWhiteSpace();
					}
				}

				ctx = computeContext( def, line, column );

				QStringList scope;
				scopeOfNode( def, scope );
				this_type = scope;

				if ( scope.size() )
				{
					SimpleVariable var;
					var.type = scope;
					var.name = "this";
					ctx->add( var );
					//kdDebug(9007) << "add variable " << var.name << " with type " << var.type << endl;
				}

				type = evaluateExpression( expr, ctx );
			}
		}
	}
	m_pSupport->backgroundParser() ->unlock();

	if ( !ctx )
		return ;

	if ( ch2 == "::" || expr.isEmpty() )
	{
		isInstance = false;
	}

	kdDebug( 9007 ) << "===========================> type is: " << type.join( " " ) << endl;
	kdDebug( 9007 ) << "===========================> word is: " << word << endl;

	if ( !showArguments )
	{
		QValueList<KTextEditor::CompletionEntry> entryList;

		if ( type.isEmpty() && expr.isEmpty() )
		{
			computeCompletionEntryList( entryList, ctx, isInstance );
			computeCompletionEntryList( entryList, m_pSupport->codeModel() ->globalNamespace(), isInstance );

			if ( m_pSupport->codeCompletionConfig() ->includeGlobalFunctions() )
				computeCompletionEntryList( entryList, QStringList(), false );

			if ( this_type.size() )
				computeCompletionEntryList( entryList, this_type, isInstance );

		}
		else if ( !type.isEmpty() )
		{
			computeCompletionEntryList( entryList, type, isInstance );
		}

		QStringList trueMatches;

		if ( invokedOnDemand )
		{
			// find matching words
			QValueList<KTextEditor::CompletionEntry>::Iterator it;
			for ( it = entryList.begin(); it != entryList.end(); ++it )
			{
				if ( (*it).text.startsWith( word ) )
				{
					trueMatches << (*it).text;

					// if more than one entry matches, abort immediately
					if ( trueMatches.size() > 1 )
						break;
				}
			}
		}

		if ( invokedOnDemand && trueMatches.size() == 1 )
		{
			// erbsland: get the cursor position now, because m_ccLine and m_ccColumn
			//           are not set until the first typed char.
			unsigned int nLine, nCol;
			m_activeCursor->cursorPositionReal( &nLine, &nCol );
			// there is only one entry -> complete immediately
			m_activeEditor->insertText( nLine, nCol,
				trueMatches[0].right( trueMatches[0].length() - word.length() ) );
		}
		else if ( entryList.size() )
		{
			entryList = unique( entryList );
			qHeapSort( entryList );
			
			m_bCompletionBoxShow = true;
			m_activeCompletion->showCompletionBox( entryList, word.length() );
		}
	}
	else
	{
		QStringList signatureList;

		if ( type.isEmpty() && expr.isEmpty() )
		{
			computeSignatureList( signatureList, word, m_pSupport->codeModel() ->globalNamespace() ->functionList() );
			computeSignatureList( signatureList, word, QStringList() );

			if ( !word.isEmpty() )
			{
				QStringList fakeType;
				fakeType << word;
				computeSignatureList( signatureList, word, fakeType );
			}
		}
		else if ( !type.isEmpty() )
		{
			computeSignatureList( signatureList, word, type );
		}

		if ( !this_type.isEmpty() && expr.isEmpty() )
			computeSignatureList( signatureList, word, this_type );

		if ( !signatureList.isEmpty() )
		{
			signatureList = unique( signatureList );
			qHeapSort( signatureList );
			m_bArgHintShow = true;
			m_activeCompletion->showArgHint( signatureList, "()", "," );
		}
	}

	delete( ctx );
	ctx = 0;
}

void CppCodeCompletion::slotFileParsed( const QString& fileName )
{
	if ( fileName != m_activeFileName || !m_pSupport || !m_activeEditor )
		return ;

	m_pSupport->backgroundParser() ->lock ();
	computeRecoveryPoints();
	m_pSupport->backgroundParser() ->unlock();
}

QStringList CppCodeCompletion::typeOf( const QString& name, const QStringList& scope, MemberAccessOp accessOp )
{
	QStringList type;

	if ( name.isEmpty() )
		return type;

	QString key = findClass( scope.join( "::" ) );
	ClassDom klass = findContainer( key );
	if ( klass )
	{
		return typeOf( name, klass, accessOp );
	}
	else
	{
		type = typeOf( name, m_pSupport->codeModel() ->globalNamespace(), accessOp );
		if ( !type.isEmpty() )
			return type;
	}

	QValueList<Catalog::QueryArgument> args;
	QTime t;

	t.start();
	args << Catalog::QueryArgument( "scope", scope );
	/*    if( name.length() >=2 )
	        args << Catalog::QueryArgument( "prefix", name.left(2) );*/
	args << Catalog::QueryArgument( "name", name );
	QValueList<Tag> tags( m_repository->query( args ) );
	kdDebug( 9007 ) << "type of " << name << " in " << scope.join( "::" ) << " takes " << t.elapsed() << endl;
	type = typeOf( tags, accessOp );

	if ( type.isEmpty() )
	{
		// try with parents
		t.restart();
		QValueList<Tag> parents( m_repository->getBaseClassList( scope.join( "::" ) ) );
		kdDebug( 9007 ) << "get parents of " << scope.join( "::" ) << " takes " << t.elapsed() << endl;
		QValueList<Tag>::Iterator it = parents.begin();
		while ( it != parents.end() )
		{
			Tag & tag = *it;
			++it;

			CppBaseClass<Tag> info( tag );

			QStringList newScope = typeName( info.baseClass() );
			type = typeOf( name, newScope, accessOp );
			if ( !type.isEmpty() )
				break;
		}
	}

	return type;
}

void CppCodeCompletion::setupCodeInformationRepository( )
{}

QStringList CppCodeCompletion::typeName( const QString& str )
{
	if ( str.isEmpty() )
		return QStringList();

	Driver d;
	Lexer lex( &d );
	lex.setSource( str );
	Parser parser( &d, &lex );

	TypeSpecifierAST::Node typeSpec;
	if ( parser.parseTypeSpecifier( typeSpec ) )
	{
		NameAST * name = typeSpec->name();

		QPtrList<ClassOrNamespaceNameAST> l = name->classOrNamespaceNameList();
		QPtrListIterator<ClassOrNamespaceNameAST> it( l );

		QString type;
		while ( it.current() )
		{
			if ( it.current() ->name() )
			{
				type += it.current() ->name() ->text() + "::";
			}
			++it;
		}

		if ( name->unqualifiedName() && name->unqualifiedName() ->name() )
		{
			type += name->unqualifiedName() ->name() ->text();
		}

		return QStringList::split( "::", type );
	}

	return QStringList();
}

SimpleContext* CppCodeCompletion::computeContext( FunctionDefinitionAST * ast, int line, int col )
{
	kdDebug( 9007 ) << "CppCodeCompletion::computeContext() -- main" << endl;

	SimpleContext* ctx = new SimpleContext();

	if ( ast && ast->initDeclarator() && ast->initDeclarator() ->declarator() )
	{
		DeclaratorAST * d = ast->initDeclarator() ->declarator();
		if ( ParameterDeclarationClauseAST * clause = d->parameterDeclarationClause() )
		{
			if ( ParameterDeclarationListAST * params = clause->parameterDeclarationList() )
			{
				QPtrList<ParameterDeclarationAST> l( params->parameterList() );
				QPtrListIterator<ParameterDeclarationAST> it( l );
				while ( it.current() )
				{
					ParameterDeclarationAST * param = it.current();
					++it;

					SimpleVariable var;
					
					QStringList ptrList;
					QPtrList<AST> ptrOpList = param->declarator()->ptrOpList();
					QPtrList<AST>::iterator it = ptrOpList.begin();
					for ( ; it != ptrOpList.end(); ++it )
					{
						ptrList.append( ( *it )->text() );
					}
					
					var.ptrList = ptrList;
					var.type = typeName( param->typeSpec() ->text() );
					var.name = declaratorToString( param->declarator(), QString::null, true );

					if ( !var.type.isEmpty() )
					{
						ctx->add( var );
						//kdDebug(9007) << "add argument " << var.name << " with type " << var.type << endl;
					}
				}
			}
		}
	}

	
	if ( ast )
		computeContext( ctx, ast->functionBody(), line, col );
	return ctx;
}

void CppCodeCompletion::computeContext( SimpleContext*& ctx, StatementAST* stmt, int line, int col )
{
	if ( !stmt )
		return ;

	switch ( stmt->nodeType() )
	{
	case NodeType_IfStatement:
		computeContext( ctx, static_cast<IfStatementAST*>( stmt ), line, col );
		break;
	case NodeType_WhileStatement:
		computeContext( ctx, static_cast<WhileStatementAST*>( stmt ), line, col );
		break;
	case NodeType_DoStatement:
		computeContext( ctx, static_cast<DoStatementAST*>( stmt ), line, col );
		break;
	case NodeType_ForStatement:
		computeContext( ctx, static_cast<ForStatementAST*>( stmt ), line, col );
		break;
	case NodeType_SwitchStatement:
		computeContext( ctx, static_cast<SwitchStatementAST*>( stmt ), line, col );
		break;
	case NodeType_TryBlockStatement:
		computeContext( ctx, static_cast<TryBlockStatementAST*>( stmt ), line, col );
		break;
	case NodeType_DeclarationStatement:
		computeContext( ctx, static_cast<DeclarationStatementAST*>( stmt ), line, col );
		break;
	case NodeType_StatementList:
		computeContext( ctx, static_cast<StatementListAST*>( stmt ), line, col );
		break;
	case NodeType_ExpressionStatement:
		break;
	}
}

void CppCodeCompletion::computeContext( SimpleContext*& ctx, StatementListAST* ast, int line, int col )
{
	if ( !inContextScope( ast, line, col, false, true ) )
		return;
	
	QPtrList<StatementAST> l( ast->statementList() );
	QPtrListIterator<StatementAST> it( l );
	while ( it.current() )
	{
		StatementAST * stmt = it.current();
		++it;

		computeContext( ctx, stmt, line, col );
	}
}

void CppCodeCompletion::computeContext( SimpleContext*& ctx, IfStatementAST* ast, int line, int col )
{
	if ( !inContextScope( ast, line, col ) )
		return;
	
	computeContext( ctx, ast->condition(), line, col );
	computeContext( ctx, ast->statement(), line, col );
	computeContext( ctx, ast->elseStatement(), line, col );
}

void CppCodeCompletion::computeContext( SimpleContext*& ctx, ForStatementAST* ast, int line, int col )
{
	if ( !inContextScope( ast, line, col ) )
		return;
	
	computeContext( ctx, ast->initStatement(), line, col );
	computeContext( ctx, ast->condition(), line, col );
	computeContext( ctx, ast->statement(), line, col );
}

void CppCodeCompletion::computeContext( SimpleContext*& ctx, DoStatementAST* ast, int line, int col )
{
	if ( !inContextScope( ast, line, col ) )
		return;
	
	//computeContext( ctx, ast->condition(), line, col );
	computeContext( ctx, ast->statement(), line, col );
}

void CppCodeCompletion::computeContext( SimpleContext*& ctx, WhileStatementAST* ast, int line, int col )
{
	if ( !inContextScope( ast, line, col ) )
		return;
	
	computeContext( ctx, ast->condition(), line, col );
	computeContext( ctx, ast->statement(), line, col );
}

void CppCodeCompletion::computeContext( SimpleContext*& ctx, SwitchStatementAST* ast, int line, int col )
{
	if ( !inContextScope( ast, line, col ) )
		return;
	
	computeContext( ctx, ast->condition(), line, col );
	computeContext( ctx, ast->statement(), line, col );
}

void CppCodeCompletion::computeContext( SimpleContext*& ctx, TryBlockStatementAST* ast, int line, int col )
{
	if ( !inContextScope( ast, line, col ) )
		return;
	
	computeContext( ctx, ast->statement(), line, col );
	computeContext( ctx, ast->catchStatementList(), line, col );
}

void CppCodeCompletion::computeContext( SimpleContext*& ctx, CatchStatementListAST* ast, int line, int col )
{
	if ( !inContextScope( ast, line, col, false, true ) )
		return;
	
	QPtrList<CatchStatementAST> l( ast->statementList() );
	QPtrListIterator<CatchStatementAST> it( l );
	while ( it.current() )
	{
		CatchStatementAST * stmt = it.current();
		++it;
		
		computeContext( ctx, stmt, line, col );
	}
}

void CppCodeCompletion::computeContext( SimpleContext*& ctx, CatchStatementAST* ast, int line, int col )
{
	if ( !inContextScope( ast, line, col ) )
		return;
	
	computeContext( ctx, ast->condition(), line, col );
	computeContext( ctx, ast->statement(), line, col );
}

void CppCodeCompletion::computeContext( SimpleContext*& ctx, DeclarationStatementAST* ast, int line, int col )
{
	if ( !ast->declaration() || ast->declaration() ->nodeType() != NodeType_SimpleDeclaration )
		return ;

	if ( !inContextScope( ast, line, col, true, false ) )
		return;
	
	SimpleDeclarationAST* simpleDecl = static_cast<SimpleDeclarationAST*>( ast->declaration() );
	TypeSpecifierAST* typeSpec = simpleDecl->typeSpec();
	QStringList type = typeName( typeSpec->text() );

	InitDeclaratorListAST* initDeclListAST = simpleDecl->initDeclaratorList();
	if ( !initDeclListAST )
		return ;

	QPtrList<InitDeclaratorAST> l = initDeclListAST->initDeclaratorList();
	QPtrListIterator<InitDeclaratorAST> it( l );
	while ( it.current() )
	{
		DeclaratorAST * d = it.current() ->declarator();
		++it;

		if ( d->declaratorId() )
		{
			SimpleVariable var;
			
			QStringList ptrList;
			QPtrList<AST> ptrOpList = d->ptrOpList();
			QPtrList<AST>::iterator it = ptrOpList.begin();
			for ( ; it != ptrOpList.end(); ++it )
			{
				ptrList.append( ( *it )->text() );
			}
			
			var.ptrList = ptrList;
			var.type = type;
			var.name = toSimpleName( d->declaratorId() );
			ctx->add( var );
			//kdDebug(9007) << "add variable " << var.name << " with type " << var.type << endl;
		}
	}
}

void CppCodeCompletion::computeContext( SimpleContext*& ctx, ConditionAST* ast, int line, int col )
{
	if ( !ast->typeSpec() || !ast->declarator() || !ast->declarator() ->declaratorId() )
		return ;

	if ( !inContextScope( ast, line, col, true, false ) )
		return;
	
	QStringList type = typeName( ast->typeSpec() ->text() );
	SimpleVariable var;
	
	QStringList ptrList;
	QPtrList<AST> ptrOpList = ast->declarator()->ptrOpList();
	QPtrList<AST>::iterator it = ptrOpList.begin();
	for ( ; it != ptrOpList.end(); ++it )
	{
		ptrList.append( ( *it )->text() );
	}
	
	var.ptrList = ptrList;
	var.type = type;
	var.name = toSimpleName( ast->declarator() ->declaratorId() );
	ctx->add( var );
	//kdDebug(9007) << "add variable " << var.name << " with type " << var.type << endl;
}

bool CppCodeCompletion::inContextScope( AST* ast, int line, int col, bool checkStart, bool checkEnd )
{
	int startLine, startColumn;
	int endLine, endColumn;
	ast->getStartPosition( &startLine, &startColumn );
	ast->getEndPosition( &endLine, &endColumn );
	
// 	kdDebug(9007) << k_funcinfo << endl;
// 	kdDebug(9007) << "current char line: " << line << " col: " << col << endl;
// 	
// 	kdDebug(9007) << nodeTypeToString( ast->nodeType() )
// 		<< " start line: " << startLine
// 		<< " col: " << startColumn << endl;
// 	kdDebug(9007) << nodeTypeToString( ast->nodeType() )
// 		<< " end line: " << endLine
// 		<< " col: " << endColumn << endl;
	
	bool start = line > startLine || ( line == startLine && col >= startColumn );
	bool end = line < endLine || ( line == endLine && col <= endColumn );
	
	if ( checkStart && checkEnd  )
		return start && end;
	else if ( checkStart )
		return start;
	else if ( checkEnd )
		return end;
	
	return false;
}

FunctionDefinitionAST * CppCodeCompletion::functionDefinition( AST* node )
{
	while ( node )
	{
		if ( node->nodeType() == NodeType_FunctionDefinition )
			return static_cast<FunctionDefinitionAST*>( node );
		node = node->parent();
	}
	return 0;
}

QString CppCodeCompletion::getText( int startLine, int startColumn, int endLine, int endColumn, int omitLine )
{
	if ( startLine == endLine )
	{
		QString textLine = m_activeEditor->textLine( startLine );
		return textLine.mid( startColumn, endColumn - startColumn );
	}

	QStringList contents;

	for ( int line = startLine; line <= endLine; ++line )
	{
		if ( line == omitLine )
			continue;

		QString textLine = m_activeEditor->textLine( line );

		if ( line == startLine )
			textLine = textLine.mid( startColumn );
		if ( line == endLine )
			textLine = textLine.left( endColumn );

		contents << textLine;
	}
	return contents.join( "\n" );
}

// namespace?
class ComputeRecoveryPoints: public TreeParser
{
public:
	ComputeRecoveryPoints( QPtrList<RecoveryPoint>& points )
			: recoveryPoints( points )
	{}

	virtual void parseTranslationUnit( TranslationUnitAST* ast )
	{
		QValueList<QStringList> dummy;

		m_imports.push( dummy );
		TreeParser::parseTranslationUnit( ast );
		m_imports.pop();

		kdDebug( 9007 ) << "found " << recoveryPoints.count() << " recovery points" << endl;
	}

	virtual void parseUsingDirective( UsingDirectiveAST* ast )
	{
		if ( !ast->name() )
			return ;

		QStringList type = CppCodeCompletion::typeName( ast->name() ->text() );
		m_imports.top().push_back( type );
	}

	virtual void parseNamespace( NamespaceAST* ast )
	{
		//insertRecoveryPoint( ast );
		m_currentScope.push_back( ast->namespaceName() ->text() );

		m_imports.push( m_imports.top() ); // dup
		m_imports.top().push_back( m_currentScope );

		TreeParser::parseNamespace( ast );

		m_imports.pop();
		m_currentScope.pop_back();
	}

	virtual void parseSimpleDeclaration( SimpleDeclarationAST* ast )
	{
		TypeSpecifierAST * typeSpec = ast->typeSpec();
		//InitDeclaratorListAST* declarators = ast->initDeclaratorList();

		if ( typeSpec )
			parseTypeSpecifier( typeSpec );

		//insertRecoveryPoint( ast );
		TreeParser::parseSimpleDeclaration( ast );
	}

	virtual void parseFunctionDefinition( FunctionDefinitionAST* ast )
	{
		m_imports.push( m_imports.top() ); // dup
		insertRecoveryPoint( ast );
		m_imports.pop();
	}

	virtual void parseClassSpecifier( ClassSpecifierAST* ast )
	{
		insertRecoveryPoint( ast );
		m_currentScope.push_back( toSimpleName( ast->name() ) );
		TreeParser::parseClassSpecifier( ast );
		m_currentScope.pop_back();
	}

	void insertRecoveryPoint( AST* ast )
	{
		if ( !ast )
			return ;

		RecoveryPoint* pt = new RecoveryPoint();
		pt->kind = ast->nodeType();
		pt->scope = m_currentScope;
		ast->getStartPosition( &pt->startLine, &pt->startColumn );
		ast->getEndPosition( &pt->endLine, &pt->endColumn );
		pt->imports = m_imports.top();

		recoveryPoints.append( pt );
	}

private:
	QPtrList<RecoveryPoint>& recoveryPoints;
	QValueStack< QValueList<QStringList> > m_imports;
	QStringList m_currentScope;
};


void CppCodeCompletion::computeRecoveryPoints( )
{
	if ( m_blockForKeyword )
		return;

	kdDebug( 9007 ) << "CppCodeCompletion::computeRecoveryPoints" << endl;

	d->recoveryPoints.clear();
	TranslationUnitAST* unit = m_pSupport->backgroundParser() ->translationUnit( m_activeFileName );
	if ( !unit )
		return ;

	ComputeRecoveryPoints walker( d->recoveryPoints );
	walker.parseTranslationUnit( unit );
}

QStringList CppCodeCompletion::typeOf( const QValueList< Tag > & tags, MemberAccessOp accessOp )
{
	QValueList<Tag>::ConstIterator it = tags.begin();
	while ( it != tags.end() )
	{
		const Tag & tag = *it;
		++it;
		
		if ( tag.hasAttribute( "t" ) )
		{
			QString type = tag.attribute( "t" ).toString();
			QRegExp ptrRx( "(\\*|\\&)" );
			QString ptr = type.mid( type.find( ptrRx ) );
			QStringList ptrList = QStringList::split( "", ptr );
			if ( !correctAccessOp( ptrList, accessOp ) )
				type = "";
			return typeName( type );
		}
		else if ( tag.kind() == Tag::Kind_Class || tag.kind() == Tag::Kind_Namespace )
		{
			QStringList l = tag.scope();
			l += typeName( tag.name() );
			return l;
		}
	}

	return QStringList();
}

QStringList CppCodeCompletion::typeOf( const QString & name, ClassDom klass, MemberAccessOp accessOp )
{
	QStringList type;

	if ( klass->hasVariable( name ) )
		return typeName( klass->variableByName( name ) ->type() );

	type = typeOf( name, klass->functionList(), accessOp );
	if ( !type.isEmpty() )
		return type;

	QStringList parents = klass->baseClassList();
	for ( QStringList::Iterator it = parents.begin(); it != parents.end(); ++it )
	{
		type = typeOf( name, typeName( *it ), accessOp );
		if ( !type.isEmpty() )
			return type;
	}

	return QStringList();
}

QStringList CppCodeCompletion::typeOf( const QString & name, NamespaceDom scope, MemberAccessOp accessOp )
{
	if ( scope->hasVariable( name ) )
		return typeName( scope->variableByName( name ) ->type() );

	QStringList type;

	type = typeOf( name, scope->functionList(), accessOp );
	if ( !type.isEmpty() )
		return type;

	return QStringList();
}

QStringList CppCodeCompletion::typeOf( const QString & name, const FunctionList & methods, MemberAccessOp accessOp )
{
	FunctionList::ConstIterator it = methods.begin();
	while ( it != methods.end() )
	{
		FunctionDom meth = *it;
		++it;

		if ( meth->name() == name )
		{	
			QString type = meth->resultType();
			QRegExp ptrRx( "(\\*|\\&)" );
			QString ptr = type.mid( type.find( ptrRx ) );
			QStringList ptrList = QStringList::split( "", ptr );
			if ( !correctAccessOp( ptrList, accessOp ) )
				type = "";
			return typeName( type );
		}
	}

	return QStringList();
}

void CppCodeCompletion::computeCompletionEntryList( QValueList< KTextEditor::CompletionEntry > & entryList, const QStringList & type, bool isInstance )
{
	CppCodeCompletionConfig * cfg = m_pSupport->codeCompletionConfig();
	QString key = findClass( type.join( "::" ) );
	ClassDom klass = findContainer( key );
	if ( klass )
	{
		computeCompletionEntryList( entryList, klass, isInstance );
	} /*else*/ {
		QValueList<Catalog::QueryArgument> args;
		QValueList<Tag> tags;

		args.clear();
		args << Catalog::QueryArgument( "kind", Tag::Kind_FunctionDeclaration )
		<< Catalog::QueryArgument( "scope", type );
		tags = m_repository->query( args );
		computeCompletionEntryList( entryList, tags, isInstance );

		args.clear();
		args << Catalog::QueryArgument( "kind", Tag::Kind_Variable )
		<< Catalog::QueryArgument( "scope", type );
		tags = m_repository->query( args );
		computeCompletionEntryList( entryList, tags, isInstance );

		if ( !isInstance && cfg->includeEnums() )
		{
			args.clear();
			args << Catalog::QueryArgument( "kind", Tag::Kind_Enumerator )
			<< Catalog::QueryArgument( "scope", type );
			tags = m_repository->query( args );
			computeCompletionEntryList( entryList, tags, isInstance );
		}

		if ( !isInstance && cfg->includeTypedefs() )
		{
			args.clear();
			args << Catalog::QueryArgument( "kind", Tag::Kind_Typedef )
			<< Catalog::QueryArgument( "scope", type );
			tags = m_repository->query( args );
			computeCompletionEntryList( entryList, tags, isInstance );
		}

		args.clear();
		args << Catalog::QueryArgument( "kind", Tag::Kind_Base_class );
		QString fullname = type.join( "::" );
		/*    	if( fullname.length() >=2 )
		            args << Catalog::QueryArgument( "prefix", fullname.left(2) );*/
		args << Catalog::QueryArgument( "name", fullname );

		QValueList<Tag> parents = m_repository->query( args );
		QValueList<Tag>::Iterator it = parents.begin();
		while ( it != parents.end() )
		{
			CppBaseClass<Tag> info( *it );
			++it;

			computeCompletionEntryList( entryList, typeName( info.baseClass() ), isInstance );
		}
	}
}

void CppCodeCompletion::computeCompletionEntryList( QValueList< KTextEditor::CompletionEntry > & entryList, QValueList< Tag > & tags, bool /*isInstance*/ )
{
	QValueList<Tag>::Iterator it = tags.begin();
	while ( it != tags.end() )
	{
		Tag & tag = *it;
		++it;

		if ( tag.name().isEmpty() )
		{
			continue;
		}
		else if ( m_completionMode != NormalCompletion )
		{
			if ( tag.kind() != Tag::Kind_FunctionDeclaration )
				continue;

			CppFunction<Tag> info( tag );

			if ( m_completionMode == SlotCompletion && !info.isSlot() )
				continue;
			else if ( m_completionMode == SignalCompletion && !info.isSignal() )
				continue;
			else if ( m_completionMode == VirtualDeclCompletion && !info.isVirtual() )
				continue;
		}

		entryList << CodeInformationRepository::toEntry( tag, m_completionMode );
	}
}

void CppCodeCompletion::computeCompletionEntryList( QValueList< KTextEditor::CompletionEntry > & entryList, ClassDom klass, bool isInstance )
{
	computeCompletionEntryList( entryList, klass->functionList(), isInstance );
	if ( m_completionMode == NormalCompletion )
		computeCompletionEntryList( entryList, klass->variableList(), isInstance );

	QStringList parents = klass->baseClassList();
	for ( QStringList::Iterator it = parents.begin(); it != parents.end(); ++it )
	{
		QStringList type = typeName( *it );

		if ( !type.isEmpty() )
			computeCompletionEntryList( entryList, type, isInstance );
	}
}

void CppCodeCompletion::computeCompletionEntryList( QValueList< KTextEditor::CompletionEntry > & entryList, NamespaceDom scope, bool isInstance )
{
	CppCodeCompletionConfig * cfg = m_pSupport->codeCompletionConfig();

	if ( cfg->includeGlobalFunctions() )
	{
		computeCompletionEntryList( entryList, scope->functionList(), isInstance );

		if ( m_completionMode == NormalCompletion )
			computeCompletionEntryList( entryList, scope->variableList(), isInstance );
	}

	if ( !isInstance && cfg->includeTypes() )
	{
		computeCompletionEntryList( entryList, scope->classList(), isInstance );
		computeCompletionEntryList( entryList, scope->namespaceList(), isInstance );
	}
}

void CppCodeCompletion::computeCompletionEntryList( QValueList< KTextEditor::CompletionEntry > & entryList, const ClassList & lst, bool isInstance )
{
	CppCodeCompletionConfig * cfg = m_pSupport->codeCompletionConfig();

	ClassList::ConstIterator it = lst.begin();
	while ( it != lst.end() )
	{
		ClassDom klass = *it;
		++it;

		KTextEditor::CompletionEntry entry;
		entry.prefix = "class";
		entry.text = klass->name();
		entryList << entry;

		if ( cfg->includeTypes() )
		{
			computeCompletionEntryList( entryList, klass->classList(), isInstance );
		}
	}
}

void CppCodeCompletion::computeCompletionEntryList( QValueList< KTextEditor::CompletionEntry > & entryList, const NamespaceList & lst, bool /*isInstance*/ )
{
	NamespaceList::ConstIterator it = lst.begin();
	while ( it != lst.end() )
	{
		NamespaceDom scope = *it;
		++it;

		KTextEditor::CompletionEntry entry;
		entry.prefix = "namespace";
		entry.text = scope->name();
		entryList << entry;
	}
}

void CppCodeCompletion::computeCompletionEntryList( QValueList< KTextEditor::CompletionEntry > & entryList, const FunctionList & methods, bool isInstance )
{
	FunctionList::ConstIterator it = methods.begin();
	while ( it != methods.end() )
	{
		FunctionDom meth = *it;
		++it;

		if ( isInstance && meth->isStatic() )
			continue;
		else if ( m_completionMode == SignalCompletion && !meth->isSignal() )
			continue;
		else if ( m_completionMode == SlotCompletion && !meth->isSlot() )
			continue;
		else if ( m_completionMode == VirtualDeclCompletion && !meth->isVirtual() )
			continue;

		KTextEditor::CompletionEntry entry;
		//entry.prefix = meth->type();

		QString text;

		ArgumentList args = meth->argumentList();
		ArgumentList::Iterator argIt = args.begin();
		
		if ( m_completionMode == VirtualDeclCompletion )
		{
			//Ideally the type info would be a entry.prefix, but we need them to be
			//inserted upon completion so they have to be part of entry.text
			entry.text = meth->resultType();
			entry.text += " ";
			entry.text += meth->name();
		}
		else
			entry.text = meth->name();
		
		if ( !args.size() )
			entry.text += "(";
		else
			entry.text += "( ";
		
		while ( argIt != args.end() )
		{
			ArgumentDom arg = *argIt;
			++argIt;

			text += arg->type();
			if ( m_completionMode == NormalCompletion ||
			     m_completionMode == VirtualDeclCompletion )
				text += QString( " " ) + arg->name();

			if ( argIt != args.end() )
				text += ", ";
		}

		if ( text.isEmpty() )
			entry.text += ")";
		else
			text += " )";

		if ( meth->isConstant() )
			text += " const";
		
		if ( m_completionMode == VirtualDeclCompletion )
			entry.text += text + ";";
		if ( m_completionMode != NormalCompletion )
			entry.text += text;
		else
			entry.postfix = text;

		entryList << entry;
	}
}

void CppCodeCompletion::computeCompletionEntryList( QValueList< KTextEditor::CompletionEntry > & entryList, const VariableList & attributes, bool isInstance )
{
	if ( m_completionMode != NormalCompletion )
		return ;

	VariableList::ConstIterator it = attributes.begin();
	while ( it != attributes.end() )
	{
		VariableDom attr = *it;
		++it;

		if ( isInstance && attr->isStatic() )
			continue;

		KTextEditor::CompletionEntry entry;
		entry.text = attr->name();
		entryList << entry;
	}
}

void CppCodeCompletion::computeCompletionEntryList( QValueList< KTextEditor::CompletionEntry > & entryList, SimpleContext * ctx, bool /*isInstance*/ )
{
	while ( ctx )
	{
		QValueList<SimpleVariable> vars = ctx->vars();
		QValueList<SimpleVariable>::ConstIterator it = vars.begin();
		while ( it != vars.end() )
		{
			const SimpleVariable & var = *it;
			++it;

			KTextEditor::CompletionEntry entry;
			entry.text = var.name;
			entryList << entry;
		}
		ctx = ctx->prev();
	}
}

void CppCodeCompletion::computeSignatureList( QStringList & signatureList, const QString & name, const QStringList & scope )
{
	QString key = findClass( scope.join( "::" ) );
	ClassDom klass = findContainer( key );
	if ( klass )
	{
		computeSignatureList( signatureList, name, klass );
		return ;
	}

	QValueList<Catalog::QueryArgument> args;
	args << Catalog::QueryArgument( "kind", Tag::Kind_FunctionDeclaration );
	args << Catalog::QueryArgument( "scope", scope );
	/*    if( name.length() >=2 )
	        args << Catalog::QueryArgument( "prefix", name.left(2) );    */
	args << Catalog::QueryArgument( "name", name );

	QValueList<Tag> tags = m_repository->query( args );
	computeSignatureList( signatureList, name, tags );

	args.clear();
	args << Catalog::QueryArgument( "kind", Tag::Kind_Base_class );
	QString fullname = scope.join( "::" );
	/*    if( fullname.length() >=2 )
	        args << Catalog::QueryArgument( "prefix", fullname.left(2) );*/

	args << Catalog::QueryArgument( "name", fullname );

	QValueList<Tag> parents = m_repository->query( args );
	QValueList<Tag>::Iterator it = parents.begin();
	while ( it != parents.end() )
	{
		CppBaseClass<Tag> info( *it );
		++it;

		computeSignatureList( signatureList, name, typeName( info.baseClass() ) );
	}
}

void CppCodeCompletion::computeSignatureList( QStringList & signatureList, const QString & name, ClassDom klass )
{
	computeSignatureList( signatureList, name, klass->functionList() );

	QStringList parents = klass->baseClassList();
	for ( QStringList::Iterator it = parents.begin(); it != parents.end(); ++it )
	{
		QStringList type = typeName( *it );
		if ( !type.isEmpty() )
			computeSignatureList( signatureList, name, type );
	}
}

void CppCodeCompletion::computeSignatureList( QStringList & signatureList, const QString & name, const FunctionList & methods )
{
	FunctionList::ConstIterator it = methods.begin();
	while ( it != methods.end() )
	{
		FunctionDom meth = *it;
		++it;

		if ( meth->name() != name )
			continue;

		QString signature;
		signature += meth->resultType() + " ";
		signature += meth->name() + "(";

		ArgumentList args = meth->argumentList();
		ArgumentList::Iterator argIt = args.begin();
		while ( argIt != args.end() )
		{
			ArgumentDom arg = *argIt;
			++argIt;

			signature += arg->type() + " " + arg->name();
			signature = signature.stripWhiteSpace();

			if ( argIt != args.end() )
				signature += ", ";
		}

		signature += ")";

		if ( meth->isConstant() )
			signature += " const";

		signatureList << signature.stripWhiteSpace();
	}
}

void CppCodeCompletion::computeSignatureList( QStringList & signatureList, const QString & name, QValueList< Tag > & tags )
{
	QValueList<Tag>::Iterator it = tags.begin();
	while ( it != tags.end() )
	{
		Tag & tag = *it;
		++it;

		CppFunction<Tag> info( tag );
		if ( info.name() != name )
			continue;

		QString signature;
		signature += info.type() + " " + tag.name() + "(";
		signature = signature.stripWhiteSpace();

		QStringList arguments = info.arguments();
		QStringList argumentNames = info.argumentNames();

		for ( uint i = 0; i < arguments.size(); ++i )
		{
			signature += arguments[ i ];
			if ( m_completionMode == NormalCompletion )
			{
				QString argName = argumentNames[ i ];
				if ( !argName.isEmpty() )
					signature += QString::fromLatin1( " " ) + argName;

				if ( i != ( arguments.size() - 1 ) )
				{
					signature += ", ";
				}
			}
		}
		signature += ")";

		if ( info.isConst() )
			signature += " const";

		signatureList << signature.stripWhiteSpace();
	}
}

QString CppCodeCompletion::findClass( const QString & className )
{
	if ( className.isEmpty() )
		return className;

	QStringList lst = d->classNameList.grep( QRegExp( "\\b" + className + "$" ) );
	if ( lst.size() )
	{
		kdDebug( 9007 ) << "found class: " << lst[ 0 ] << endl;
		return lst[ 0 ];
	}

	return className;
}

ClassDom CppCodeCompletion::findContainer( const QString& name, NamespaceDom container, bool includeImports )
{
	kdDebug( 9007 ) << "-----------> findContainer name: " << name << " " << ( container ? container->name() : QString( "" ) ) << endl;

	if ( name.isEmpty() )
		return model_cast<ClassDom>( container );

	if ( !container )
		return findContainer( name, m_pSupport->codeModel() ->globalNamespace(), includeImports );

	QStringList path = QStringList::split( "::", name );
	QStringList::Iterator it = path.begin();
	while ( it != path.end() )
	{
		QString s = *it;
		QStringList::Iterator sv_it = it;
		++it;

		kdDebug( 9007 ) << "has namespace " << s << ": " << container->hasNamespace( s ) << endl;
		if ( !container->hasNamespace( s ) )
			break;

		NamespaceDom scope = container->namespaceByName( s );

		path.remove( sv_it );
		container = scope;
	}

	if ( path.size() == 0 )
		return model_cast<ClassDom>( container );

	QString className = path.join( "::" );
	kdDebug( 9007 ) << "find class: " << className << " in namespace " << container->name() << endl;

	ClassDom c = model_cast<ClassDom>( container );
	while ( c && path.size() )
	{
		QString s = path.front();
		path.pop_front();

		if ( !c->hasClass( s ) )
		{
			c = 0;
			break;
		}

		ClassList classList = c->classByName( s );
		c = classList[ 0 ];
		for ( ClassList::Iterator cit = classList.begin(); cit != classList.end(); ++cit )
		{
			if ( QFileInfo( ( *cit ) ->fileName() ).dirPath( true ) == QFileInfo( m_activeFileName ).dirPath( true ) )
				c = *cit;
		}
	}

	kdDebug( 9007 ) << "found class: " << ( c ? c->name() : QString( "<anon>" ) ) << endl;

#if 0

	if ( !c && includeImports )
	{

		QStringList imports;
		QValueList<QStringList>::Iterator lIt = m_imports.begin();
		while ( lIt != m_imports.end() )
		{
			imports += ( *lIt );
			++lIt;
		}

		QStringList::Iterator impIt = imports.begin();
		while ( impIt != imports.end() )
		{
			ClassDom kl = findContainer( ( *impIt ) + "::" + name, container, false );
			if ( kl )
				return kl;
			++impIt;
		}
	}
#endif

	return c;
}

void CppCodeCompletion::computeFileEntryList( )
{
	m_fileEntryList.clear();

	QStringList fileList = m_pSupport->project() ->allFiles();
	for ( QStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it )
	{
		if ( !m_pSupport->isHeader( *it ) )
			continue;

		KTextEditor::CompletionEntry entry;
		entry.text = QFileInfo( *it ).fileName();
		m_fileEntryList.push_back( entry );
	}

	m_fileEntryList = unique( m_fileEntryList );
}

#include "cppcodecompletion.moc"
//kate: indent-mode csands; tab-width 4; space-indent off;
