/***************************************************************************
*   Copyright (C) 2003 by Roberto Raggi                                   *
*   roberto@tdevelop.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.                                   *
*                                                                         *
***************************************************************************/

#include "codeinformationrepository.h"
#include "cpp_tags.h"

#include <kdevcoderepository.h>
#include <kdebug.h>

/// @todo move in utils.cpp
static TQValueList<KTextEditor::CompletionEntry>
my_unique( const TQValueList<KTextEditor::CompletionEntry>& entryList )
{

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

CodeInformationRepository::CodeInformationRepository( KDevCodeRepository* rep )
		: m_rep( rep )
{}

CodeInformationRepository::~CodeInformationRepository()
{}

TQValueList<Tag> CodeInformationRepository::query( const TQValueList<Catalog :: QueryArgument> & args )
{
//	kdDebug( 9007 ) << "CodeInformationRepository::query()" << endl;

	TQValueList<Tag> tags;

	TQValueList<Catalog*> catalogs = m_rep->registeredCatalogs();
	TQValueList<Catalog*>::Iterator it = catalogs.begin();
	while ( it != catalogs.end() )
	{
		Catalog * catalog = *it;
		++it;

		if ( !catalog->enabled() )
			continue;

		tags += catalog->query( args );
	}

	return tags;
}

TQValueList<Tag> CodeInformationRepository::getTagsInFile( const TQString & fileName )
{
	kdDebug( 9007 ) << "CodeInformationRepository::getTagsInFile()" << endl;

	TQValueList<Catalog::QueryArgument> args;
	args << Catalog::QueryArgument( "fileName", fileName );

	TQValueList<Catalog*> catalogs = m_rep->registeredCatalogs();
	TQValueList<Catalog*>::Iterator it = catalogs.begin();
	while ( it != catalogs.end() )
	{
		Catalog * catalog = *it;
		++it;

		TQValueList<Tag> tags = catalog->query( args );

		if ( tags.size() )
			return tags;
	}

	return TQValueList<Tag>();
}

TQValueList<Tag> CodeInformationRepository::getTagsInScope( const TQStringList & scope, bool // isInstance
                                                         )
{
	kdDebug( 9007 ) << "CodeInformationRepository::getTagsInScope()" << endl;

	TQValueList<Tag> tags;
	TQValueList<Catalog::QueryArgument> args;

#if 0

	args.clear();
	args << Catalog::QueryArgument( "kind", Tag::Kind_Namespace )
	<< Catalog::QueryArgument( "scope", scope );
	tags += query( args );

	args.clear();
	args << Catalog::QueryArgument( "kind", Tag::Kind_Class )
	<< Catalog::QueryArgument( "scope", scope );
	tags += query( args );
#endif

	args.clear();
	args << Catalog::QueryArgument( "kind", Tag::Kind_FunctionDeclaration )
	<< Catalog::QueryArgument( "scope", scope );
	tags += query( args );

	args.clear();
	args << Catalog::QueryArgument( "kind", Tag::Kind_Variable )
	<< Catalog::QueryArgument( "scope", scope );
	tags += query( args );

	if ( true /*!isInstance*/ )
	{
		args.clear();
		args << Catalog::QueryArgument( "kind", Tag::Kind_Enumerator )
		<< Catalog::QueryArgument( "scope", scope );
		tags += query( args );
	}

	return tags;
}

TQValueList<KTextEditor::CompletionEntry> CodeInformationRepository::getEntriesInScope( const TQStringList & scope, bool isInstance, bool recompute )
{
	kdDebug( 9007 ) << "CodeInformationRepository::getEntriesInScope()" << endl;

	if ( !recompute && !scope.size() && m_globalEntries.size() )
		return m_globalEntries;
	else if ( scope.size() == 0 )
	{
		m_globalEntries = my_unique( toEntryList( getTagsInScope( scope, isInstance ) ) );
		return m_globalEntries;
	}

	return toEntryList( getTagsInScope( scope, isInstance ) );
}


TQValueList<Tag> CodeInformationRepository::getBaseClassList( const TQString& className )
{
//	kdDebug( 9007 ) << "CodeInformationRepository::getBaseClassList()" << endl;

	if ( className.isEmpty() )
		return TQValueList<Tag>();

	TQValueList<Catalog::QueryArgument> args;
	args << Catalog::QueryArgument( "kind", Tag::Kind_Base_class );
	/*    if( className.length() >= 2 )
	        args << Catalog::QueryArgument( "prefix", className.left(2) );*/
	args << Catalog::QueryArgument( "name", className );
	return query( args );
}

TQValueList<Tag> CodeInformationRepository::getClassOrNamespaceList( const TQStringList & scope )
{
	kdDebug( 9007 ) << "CodeInformationRepository::getClassOrNamespaceList()" << endl;

	TQValueList<Tag> tags;
	TQValueList<Catalog::QueryArgument> args;

	args << Catalog::QueryArgument( "kind", Tag::Kind_Namespace )
	<< Catalog::QueryArgument( "scope", scope );
	tags += query( args );

	args.clear();
	args << Catalog::QueryArgument( "kind", Tag::Kind_Class )
	<< Catalog::QueryArgument( "scope", scope );
	tags += query( args );

	return tags;
}

TQValueList<Tag> CodeInformationRepository::getTagsInScope( const TQString & name, const TQStringList & scope )
{
	TQValueList<Tag> tags;
	TQValueList<Catalog::QueryArgument> args;

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

	tags += query( args );

	return tags;
}

KTextEditor::CompletionEntry CodeInformationRepository::toEntry( Tag & tag, CppCodeCompletion::CompletionMode completionMode, TypeProcessor* proc )
{
	KTextEditor::CompletionEntry entry;

	if ( tag.name().isEmpty() )
		return entry;

	switch ( tag.kind() )
	{
	case Tag::Kind_Typedef:
		entry.prefix = "typedef";
		entry.text = tag.name();
		break;

	case Tag::Kind_Class:
		entry.prefix = "class";
		entry.text = tag.name();
		break;

	case Tag::Kind_Struct:
		entry.prefix = "struct";
		entry.text = tag.name();
		break;

		case Tag::Kind_Namespace:
		entry.prefix = "namespace";
		entry.text = tag.name();
		break;

	case Tag::Kind_FunctionDeclaration:
		//case Tag::Kind_Function:
		{

			CppFunction<Tag> tagInfo( tag );
			TQStringList arguments = tagInfo.arguments();
			TQStringList argumentNames = tagInfo.argumentNames();

			if ( completionMode == CppCodeCompletion::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 = tagInfo.type();
				entry.text += " ";
				entry.text += tag.name();
			}
			else
				entry.text = tag.name();
			
			if ( !arguments.size() )
				entry.text += "(";
			else
				entry.text += "( ";
			
			TQString signature;
			for ( uint i = 0; i < arguments.size(); ++i )
			{
                            if( !proc )
                                signature += arguments[ i ];
                            else
                                signature += proc->processType( arguments[ i ] );
                            
                            if ( completionMode == CppCodeCompletion::NormalCompletion ||
                                        completionMode == CppCodeCompletion::VirtualDeclCompletion )
                            {
                                    TQString argName = argumentNames[ i ];
                                    if ( !argName.isEmpty() ) 
                                        signature += TQString::fromLatin1( " " ) + argName;
                                    
                            }
                            
                            if ( i != ( arguments.size() - 1 ) )
                            {
                                    signature += ", ";
                            }
			}

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

			if ( tagInfo.isConst() )
				entry.postfix += " const";

			if ( completionMode == CppCodeCompletion::VirtualDeclCompletion )
			{
				entry.text += entry.postfix + ";";
				entry.postfix = TQString();
			}
			else if ( completionMode != CppCodeCompletion::NormalCompletion )
			{
				entry.text += entry.postfix;
				entry.postfix = TQString();
			}

			TQString comment = tag.attribute( "description" ).toString();
			if ( !comment.isNull() )
				entry.comment = comment;
			//else
			//entry.comment = "no documentation available!";
		}

		break;

	case Tag::Kind_Enumerator:
	case Tag::Kind_Variable:
		entry.text = tag.name();
		break;

	default:
		;
	}
        
        entry.comment = tag.comment();
        
	return entry;
}

TQValueList<KTextEditor :: CompletionEntry> CodeInformationRepository::toEntryList( const TQValueList<Tag> & tags, CppCodeCompletion::CompletionMode completionMode )
{
	TQValueList<KTextEditor :: CompletionEntry> entryList;
	TQMap<TQString, bool> ns;

	TQValueList<Tag>::ConstIterator it = tags.begin();
	while ( it != tags.end() )
	{
		Tag tag = *it;
		++it;

		KTextEditor::CompletionEntry entry = toEntry( tag, completionMode );
		if ( !entry.text.isEmpty() )
			entryList << entry;
	}

	return entryList;
}