#include "tdelocale.h" /* defines [function] i18n */
#include <lexer.h>

#include "kdevdriver.h"
#include "cppcodecompletionconfig.h"
#include "setuphelper.h"
#include <unistd.h>
#include "includepathresolver.h"


KDevDriver::KDevDriver( CppSupportPart* cppSupport, bool foreground )
: m_cppSupport( cppSupport ), m_includePathResolver(0), m_foreground(foreground), m_shouldParseIncludedFiles(true)
{
	//setupProject();
	setup();
	
}

KDevDriver::~KDevDriver() {
	delete m_includePathResolver;
}

CppSupportPart* KDevDriver::cppSupport() { return m_cppSupport; }

void KDevDriver::setupProject()
{
	TQMap<TQString, bool> map;
	
	TQStringList fileList = m_cppSupport->project() ->allFiles();
	TQStringList::ConstIterator it = fileList.begin();
	while ( it != fileList.end() )
	{
		TQFileInfo info( *it );
		++it;
		
		map.insert( info.dirPath( true ), true );
	}
	TQMap<TQString, bool>::Iterator mapit = map.begin();
	while ( mapit != map.end() )
	{
		addIncludePath( mapit.key() );
		++mapit;
	}
}

void KDevDriver::setupLexer( Lexer* lexer )
{
	Driver::setupLexer( lexer );
	lexer->setReportMessages( true );
	lexer->setReportWarnings( true );
}

//! setup the preprocessor
//! code provided by Reginald Stadlbauer <reggie@trolltech.com>
void KDevDriver::setup()
{
	if( lexerCache() ) lexerCache()->clear(); ///Clear the lexer-cache so missing headers get a chance to be parsed
	clearMacros();
	clearIncludePaths();

	addMacro( Macro("KDEVELOP_PARSER", "3.4") );
	
	bool ok;
	TQString verboseGccOutput = SetupHelper::getVerboseGccIncludePath( &ok );
	TQStringList verboseGccLines = TQStringList::split( '\n', verboseGccOutput );
	if( verboseGccLines.count() > 3 ) {
		///Parse the output of gcc. It includes gcc's final include-path when parsing an empty c++-file(including dirs like /usr/include/c++/4.xx/...)
		//what about order?
		for( TQStringList::iterator it = verboseGccLines.begin(); it != verboseGccLines.end(); ++it ) {
			if( (*it).startsWith(" ") && (*it).length() > 2 && (*it)[1] != ' ' ) {
				//it is a potential include-file
				TQString path = (*it).stripWhiteSpace();
				TQFileInfo info( path );
				if( info.exists() ) {
					kdDebug( 9007 ) << "Adding include-path from gcc-output: \"" << path << "\" absolute: \"" << info.absFilePath() << "\"" <<  endl;
					addIncludePath(info.absFilePath());
				}
			}
		}
	} else {
		///Do some of the old stuff
		addIncludePath( "/include" );
		addIncludePath( "/usr/include" );
		addIncludePath( "/usr/local/include" );
        
		bool ok;
        TQString includePath = SetupHelper::getGccIncludePath(&ok);
		if (ok) {
			TQStringList ls = TQStringList::split( "\n", includePath );
			for( TQStringList::const_iterator it = ls.begin(); it != ls.end(); ++it ) {
			if( !(*it).isEmpty() )
				addIncludePath( *it );
			}
		}
    
        addIncludePath( includePath );
		addIncludePath( "/usr/include/g++-3" );
		addIncludePath( "/usr/include/g++" );
	}
	
	addMacro( Macro( "__cplusplus", "1" ) );

	///@todo maybe remove the following? Is there any normal user who has his environment set up correctly so this is of any use?
	TQString tdedir = getenv( "TDEDIR" );
	if( !tdedir.isNull() )
		addIncludePath( tdedir + "/include" );

	TQString qmakespec = getenv( "QMAKESPEC" );
	if ( qmakespec.isNull() )
	qmakespec = "linux-g++";
	
	TQString qtdir = getenv( "TQTDIR" );
	if( !qtdir.isNull() ) {
		addIncludePath( qtdir + "/include" );

		// #### implement other mkspecs and find a better way to find the
		// #### proper mkspec (althoigh this will be no fun :-)

		addIncludePath( qtdir + "/mkspecs/" + qmakespec );
	}

	TQStringList lines = SetupHelper::getGccMacros(&ok);
	if (!ok) {
		for (TQStringList::ConstIterator it = lines.constBegin(); it != lines.constEnd(); ++it) {
			TQStringList lst = TQStringList::split( ' ', *it );
			if ( lst.count() != 3 )
				continue;
			addMacro( Macro( lst[1], lst[2] ) );
		}
	}
		
	addMacro( Macro( "__cplusplus", "1" ) );
	addMacro( Macro( "signals", "signals" ) );
	addMacro( Macro( "slots", "slots" ) );
	addMacro( Macro( "TQ_SCRIPTABLE", "" ) );

	CppCodeCompletionConfig* cfg = m_cppSupport->codeCompletionConfig();
	TQString str = cfg->customIncludePaths();
	int pos = 0;
	while( pos < str.length() ) {
		int end = str.find( ';', pos );
		if( end == -1 ) {
			end = str.length();
		}
		
		TQString s = str.mid( pos, end-pos ).stripWhiteSpace();
		if( !s.isEmpty() ) {
			if( !s.startsWith( "/" ) && m_cppSupport->project() ) {
				s = m_cppSupport->project()->projectDirectory() + "/" + s;
			}
			addIncludePath( s );
		}
		
		pos = end+1;
	}

	setResolveDependencesEnabled( cfg->preProcessAllHeaders() | cfg->parseMissingHeaders() );

	delete m_includePathResolver;
	if( cfg->resolveIncludePaths() ) {
		m_includePathResolver = new CppTools::IncludePathResolver( m_foreground );
		if( m_cppSupport && m_cppSupport->project() )
			m_includePathResolver->setOutOfSourceBuildSystem( m_cppSupport->project()->projectDirectory(), m_cppSupport->project()->buildDirectory() );
	} else
		m_includePathResolver = 0;
	
	m_shouldParseIncludedFiles = cfg->parseMissingHeaders();
}

TQStringList KDevDriver::getCustomIncludePath( const TQString& file ) {
	if( !file.startsWith("/") )
		kdDebug( 9007 ) << "KDevDriver::getCustomIncludePath(..): given file \"" << file << "\" is not absolute" << endl;
	if( !m_includePathResolver )
		return includePaths();
	CppTools::PathResolutionResult res = m_includePathResolver->resolveIncludePath( file );
	
	if( !res.success ) {
		Problem p( i18n( "%1. Message: %2" ).arg( res.errorMessage ).arg( res.longErrorMessage ), 0, 0, Problem::Level_Warning );
		p.setFileName( file );
		addProblem( file, p );
	}

	return res.path + includePaths();
}

bool KDevDriver::shouldParseIncludedFile( const ParsedFilePointer& file ) {
	TQString compoundString = file->fileName() + "||" + TQString("%1").arg(file->usedMacros().valueHash()) + "||" + TQString("%1").arg(file->usedMacros().idHash());

	if( !m_shouldParseIncludedFiles )
		return false;
	m_cppSupport->safeFileSet().contains( compoundString );

	if( m_cppSupport->safeFileSet().contains( file->fileName()) ){
		return false;
	} else if( m_cppSupport->safeFileSet().contains( compoundString ) ) {
		//kdDebug( 9007 ) << "ALREADY IN FILE-SET: " << compoundString << endl;
		return false;
	} else {
		m_cppSupport->safeFileSet().insert( compoundString ); //This is needed so the same instance of a file is not queued many times
		//kdDebug( 9007 ) << "NOT IN FILE-SET, PARSING: " << compoundString << endl;
		return true;
	}

}
