/***************************************************************************
   Copyright 2006 David Nolden <david.nolden.kdevelop@art-master.de>
***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 "environmentmanager.h"
#include <QFileInfo>
#include <kdebug.h>
#include "rpp/pp-macro.h"
#include "rpp/pp-environment.h"
#include <language/interfaces/iproblem.h>
#include "cpppreprocessenvironment.h"
#include <language/duchain/repositories/itemrepository.h>
#include <language/editor/modificationrevision.h>
#include "parser/rpp/macrorepository.h"
#include "cppdebughelper.h"
#include <language/duchain/duchainregister.h>
#include <language/duchain/duchainlock.h>
#include <language/duchain/duchain.h>
#include <language/duchain/referencecounting.h>
#include "cpppreprocessenvironment.h"

using namespace KDevelop;

#define ENSURE_FILE_READ_LOCKED(file)   if((file).indexedTopContext().isValid()) { ENSURE_CHAIN_READ_LOCKED }
#define ENSURE_READ_LOCKED   ENSURE_FILE_READ_LOCKED(*this)
#define ENSURE_WRITE_LOCKED   if(indexedTopContext().isValid()) { ENSURE_CHAIN_READ_LOCKED }

DEFINE_LIST_MEMBER_HASH(IncludePathListItem, m_includePaths, KDevelop::IndexedString)

struct IncludePathListItem {
  
    IncludePathListItem() {
      initializeAppendedLists(true);
      m_refCount = 0;
    }
    IncludePathListItem(const IncludePathListItem& rhs, bool dynamic) {
      initializeAppendedLists(dynamic);
      m_refCount = rhs.m_refCount;
      copyListsFrom(rhs);
    }
    ~IncludePathListItem() {
      freeAppendedLists();
    }
    
    bool persistent() const {
      return (bool)m_refCount;
    }
    
    bool operator==(const IncludePathListItem& rhs) const {
      return listsEqual(rhs);
    }
  
    uint hash() const {
      uint ret = 0;
      for(uint a = 0; a < m_includePathsSize(); ++a)
        ret = (m_includePaths()[a].hash() + ret) * 17;
      
      return ret;
    }
    
    uint itemSize() const {
      return dynamicSize();
    }
    
    uint classSize() const {
      return sizeof(*this);
    }
  
    uint m_refCount;
    START_APPENDED_LISTS(IncludePathListItem);
    
    APPENDED_LIST_FIRST(IncludePathListItem, IndexedString, m_includePaths);
    END_APPENDED_LISTS(IncludePathListItem, m_includePaths);
  private:
    IncludePathListItem& operator=(const IncludePathListItem&);
};

typedef AppendedListItemRequest<IncludePathListItem, 40*4> IncludePathsRequest;

typedef KDevelop::ItemRepository<IncludePathListItem, IncludePathsRequest> IncludePathsRepository;
IncludePathsRepository& includePathsRepository()
{
  static IncludePathsRepository repo("include path repository");
  return repo;
}

//If DYNAMIC_DEBUGGING is defined, debugging can be started at any point in runtime,
//by calling setIsDebugging(true) from within the debugger
// #define DYNAMIC_DEBUGGING

#ifdef DYNAMIC_DEBUGGING
volatile bool is_debugging = true;

bool debugging() {
  return is_debugging;
}

//Ment to be called from within dbg, to start doing debug output at a specific point
void setIsDebugging(bool is) {
  is_debugging = is;
}

#define DEBUG_LEXERCACHE
#define ifDebug(x) if(debugging()) {x;}
#else
inline bool debugging() {
  return false;
}
#endif

using namespace Cpp;

REGISTER_DUCHAIN_ITEM(EnvironmentFile);

namespace Cpp {
  Utils::BasicSetRepository* StaticStringSetRepository::repository() {
    return &Cpp::EnvironmentManager::self()->stringSetRepository();
  }
  Utils::BasicSetRepository* StaticMacroSetRepository::repository() {
    return &Cpp::EnvironmentManager::self()->macroSetRepository();
  }
  void MacroSetRepository::itemRemovedFromSets(uint index) {
    Cpp::EnvironmentManager::self()->macroDataRepository().deleteItem(index);
  }
}

const rpp::pp_macro& Cpp::MacroIndexConversion::toItem(uint index) const {
  return *EnvironmentManager::self()->macroDataRepository().itemFromIndex( index );
}

uint Cpp::MacroIndexConversion::toIndex(const rpp::pp_macro& macro) const {
  return EnvironmentManager::self()->macroDataRepository().index( MacroRepositoryItemRequest(macro) );
}

//For debugging
QString id(const EnvironmentFile* file) {
  return file->url().str() + QString(" %1").arg((size_t)file) ;
}

QString print(const Cpp::ReferenceCountedStringSet& set) {
  QString ret;
  bool first = true;
  Cpp::ReferenceCountedStringSet::Iterator it( set.iterator() );
  while(it) {
    if(!first)
      ret += ", ";
    first = false;
    
    ret += (*it).str();
    ++it;
  }
  return ret;
}

QString print(const Cpp::ReferenceCountedMacroSet& set) {
  QString ret;
  bool first = true;
  Cpp::ReferenceCountedMacroSet::Iterator it( set.iterator() );
  while(it) {
    if(!first)
      ret += ", ";
    first = false;
    
    ret += it.ref().toString();
    ++it;
  }
  return ret;
}

EnvironmentManager* EnvironmentManager::m_self = 0;

EnvironmentManager::EnvironmentManager()
  : m_matchingLevel(Full), m_simplifiedMatching(false),
    m_macroDataRepository("macro repository"), m_stringSetRepository("string sets"), m_macroSetRepository()
{
}

void EnvironmentManager::init()
{
  Q_ASSERT_X(!m_self, "EnvironmentManager::init()", "do not call init() twice");
  m_self = new EnvironmentManager();
  // also init the other getters
  includePathsRepository();
}

void EnvironmentManager::setSimplifiedMatching(bool simplified)
{
  m_simplifiedMatching = simplified;
}

void Cpp::EnvironmentManager::setMatchingLevel(Cpp::EnvironmentManager::MatchingLevel level) {
  m_matchingLevel = level;
}

bool EnvironmentFile::matchEnvironment(const ParsingEnvironment* _environment) const {
  ENSURE_READ_LOCKED
  const CppPreprocessEnvironment* cppEnvironment = dynamic_cast<const CppPreprocessEnvironment*>(_environment);
  if(!cppEnvironment)
    return false;

  if( cppEnvironment->identityOffsetRestrictionEnabled() && cppEnvironment->identityOffsetRestriction() != identityOffset() ) {
#ifdef DEBUG_LEXERCACHE
    kDebug( 9007 ) << "file" << url().str() << "does not match branching hash. Restriction:" << cppEnvironment->identityOffsetRestriction() << "Actual:" << identityOffset();
#endif
    return false;
  }
  
  if(EnvironmentManager::self()->matchingLevel() == EnvironmentManager::Disabled)
    return true;
  
  //Consider files that are out-guarded by the header-guard as a match, without looking into their content
  ///@todo Pick the version that is already in the environment if there is multiple
  if(EnvironmentManager::self()->matchingLevel() == EnvironmentManager::Naive)
    if(cppEnvironment->macroNameSet().contains(headerGuard())) {
#ifdef DEBUG_LEXERCACHE
      kDebug( 9007 ) << "file" << url().str() << "environment contains the header-guard, returning true";
#endif
      return true;
    }
  
  ReferenceCountedStringSet environmentMacroNames = cppEnvironment->macroNameSet();
  
  ReferenceCountedStringSet conflicts = (environmentMacroNames & strings()) - d_func()->m_usedMacroNames;

  for( ReferenceCountedStringSet::Iterator it(conflicts.iterator()); it; ++it ) {
    rpp::pp_macro* m = cppEnvironment->retrieveStoredMacro( *it );
    if(m && !m->isUndef()) {
      
#ifdef DEBUG_LEXERCACHE
      if(debugging()) {
        kDebug(9007) << "The environment contains a macro that can affect the cached file, but that should not exist:" << m->name.str();
      }
#endif
      return false;
    }
  }
  
  //Make sure that all external macros used by the file now exist too

  ///@todo find out why this assertion sometimes triggers, maybe different macros with the same name were used?
  //ifDebug( Q_ASSERT(m_usedMacros.set().count() == m_usedMacroNames.set().count()) );
  ifDebug( kDebug(9007) << "Count of used macros that need to be verified:" << d_func()->m_usedMacros.set().count() );

  for ( ReferenceCountedMacroSet::Iterator it( d_func()->m_usedMacros.iterator() ); it; ++it ) {
    rpp::pp_macro* m = cppEnvironment->retrieveStoredMacro( it.ref().name );
    if ( !m || !(*m == it.ref()) ) {
      if( !m && it.ref().isUndef() ) {
        ifDebug( kDebug( 9007 ) << "Undef-macro" << it.ref().name.str() << "is ok" << m );
        //It is okay, we did not find a macro, but the used macro is an undef macro
        //Q_ASSERT(0); //Undef-macros should not be marked as used
      } else {
        ifDebug( kDebug( 9007 ) << "The cached file " << url().str() << " used a macro called \"" << it.ref().name.str() << "\"(from" << it.ref().file.str() << "), but the environment" << (m ? "contains differing macro of that name" : "does not contain that macro") << ", the cached file is not used"  );
        ifDebug( if(m) { kDebug() << "Used macro: " << it.ref().toString()  << "from" << it.ref().file.str() << "found:" << m->toString() << "from" << m->file.str(); } );
        return false;
      }
    }else{
      ifDebug( kDebug( 9007 ) << it.ref().name.str() << "match" );
    }
  }

  ifDebug( kDebug( 9007 ) << "Using cached file " << url().str() );  
  return true;
}

const KDevelop::ModificationRevisionSet& Cpp::EnvironmentFile::includePathDependencies() const
{
  ENSURE_READ_LOCKED
  return d_func()->m_includePathDependencies;
}

void Cpp::EnvironmentFile::setIncludePathDependencies(const KDevelop::ModificationRevisionSet& set)
{
  ENSURE_WRITE_LOCKED
  d_func_dynamic()->m_includePathDependencies = set;
}

bool EnvironmentFile::needsUpdate(const ParsingEnvironment* environment) const {
  ENSURE_READ_LOCKED
  const CppPreprocessEnvironment* cppEnvironment = dynamic_cast<const CppPreprocessEnvironment*>(environment);
  
  //When in naive matching mode, we even use the non-guarded version when inappropriate. We must make sure not to update it in such
  //a situation, else it will end up empty
  if(cppEnvironment && EnvironmentManager::self()->matchingLevel() <= EnvironmentManager::Naive && !headerGuard().isEmpty() && cppEnvironment->macroNameSet().contains(headerGuard()))
    return false;
  
  return ParsingEnvironmentFile::needsUpdate(environment) || d_func()->m_includePathDependencies.needsUpdate();
}

EnvironmentFile::EnvironmentFile( const IndexedString& url, TopDUContext* topContext ) : ParsingEnvironmentFile(*new EnvironmentFileData(), url) {

  d_func_dynamic()->setClassId(this);
  setLanguage(IndexedString("C++"));
  
  d_func_dynamic()->m_topContext = IndexedTopDUContext(topContext);
  
  d_func_dynamic()->m_url = url;

//   ifDebug( kDebug(9007) << "created for" << url.str() << "modification-time:" << d_func_dynamic()->m_modificationTime );
  
  clearModificationRevisions();
}

EnvironmentFile::EnvironmentFile( EnvironmentFileData& data ) : ParsingEnvironmentFile(data)
{
}

EnvironmentFile::~EnvironmentFile() {
}

void EnvironmentFile::setContentStartLine(int line) {
  ENSURE_WRITE_LOCKED
  d_func_dynamic()->m_contentStartLine = line;
}

int EnvironmentFile::contentStartLine() const {
  ENSURE_READ_LOCKED
  return d_func()->m_contentStartLine;
}

void EnvironmentFile::addDefinedMacro( const rpp::pp_macro& macro, const rpp::pp_macro* previousOfSameName ) {
  ENSURE_WRITE_LOCKED
#ifdef DEBUG_LEXERCACHE
  if(debugging()) {
  kDebug( 9007 )  << id(this) << "defined macro" << macro.name.str();
  }
#endif
  if( previousOfSameName && d_func()->m_definedMacros.contains(*previousOfSameName) )
    d_func_dynamic()->m_definedMacros.remove( *previousOfSameName );
  else if( d_func()->m_definedMacroNames.contains(macro.name) ) {
    //Search if there is already a macro of the same name in the set, and remove it
    //This is slow, but should not happen too often
    ///@todo maybe give a warning, and find out how this can happen
    for( ReferenceCountedMacroSet::Iterator it( d_func()->m_definedMacros.iterator() ); it; ++it )
      if( macro.name == it.ref().name )
        d_func_dynamic()->m_definedMacros.remove(it.ref());
  }
  
  if(macro.isUndef()) {
    d_func_dynamic()->m_definedMacroNames.remove( macro.name );
    
    d_func_dynamic()->m_unDefinedMacroNames.insert( macro.name );
  } else {
    d_func_dynamic()->m_unDefinedMacroNames.remove( macro.name );
    d_func_dynamic()->m_definedMacroNames.insert( macro.name );
    
    d_func_dynamic()->m_definedMacros.insert( macro );
  }
  
  //State: If it is an undef macro, it is not in m_definedMacroNames not in m_definedMacros, and it is in m_unDefinedMacroNames
  //       If  it is a normal macro, it is in m_definedMacroNames, it is in m_definedMacros, and it is not in m_unDefinedMacroNames
}

void EnvironmentFile::usingMacro( const rpp::pp_macro& macro ) {
  ENSURE_WRITE_LOCKED
  if ( !d_func()->m_definedMacroNames.contains( macro.name ) && !d_func()->m_unDefinedMacroNames.contains( macro.name ) && !macro.isUndef() ) {
#ifdef DEBUG_LEXERCACHE
  if(debugging()) {
    kDebug( 9007 ) << id(this) << "used macro" << macro.name.str() << "from" << macro.file.str();
  }
#endif
    d_func_dynamic()->m_usedMacros.insert( macro );
  
    d_func_dynamic()->m_usedMacroNames.insert( macro.name );
  }
}

// const IndexedStringSet& EnvironmentFile::includeFiles() const {
//   return m_includeFiles;
// }

const ReferenceCountedStringSet& EnvironmentFile::strings() const {
  ENSURE_READ_LOCKED
  return d_func()->m_strings;
}


///Set of all defined macros, including those of all deeper included files
const ReferenceCountedMacroSet& EnvironmentFile::definedMacros() const {
  ENSURE_READ_LOCKED
  return d_func()->m_definedMacros;
}

///Set of all macros used from outside, including those used in deeper included files
const ReferenceCountedStringSet& EnvironmentFile::usedMacroNames() const {
  ENSURE_READ_LOCKED
  return d_func()->m_usedMacroNames;
}

///Set of all macros used from outside, including those used in deeper included files
const ReferenceCountedStringSet& EnvironmentFile::definedMacroNames() const {
  ENSURE_READ_LOCKED
  return d_func()->m_definedMacroNames;
}

const ReferenceCountedStringSet& EnvironmentFile::unDefinedMacroNames() const {
  ENSURE_READ_LOCKED
  return d_func()->m_unDefinedMacroNames;
}

///Set of all macros used from outside, including those used in deeper included files
const ReferenceCountedMacroSet& EnvironmentFile::usedMacros() const {
  ENSURE_READ_LOCKED
  return d_func()->m_usedMacros;
}

const QList<IndexedString> EnvironmentFile::includePaths() const {
  ENSURE_READ_LOCKED
  QList<IndexedString> ret;
  if(d_func()->m_includePaths) {
    const IncludePathListItem* item = includePathsRepository().itemFromIndex(d_func()->m_includePaths);
    
    FOREACH_FUNCTION(const IndexedString& include, item->m_includePaths)
      ret << include;
  }
  return ret;
}

void EnvironmentFile::setIncludePaths( const QList<IndexedString>& paths ) {
  ENSURE_WRITE_LOCKED
  
  QMutexLocker lock(includePathsRepository().mutex());
  
  if(d_func()->m_includePaths) {
    KDevelop::DynamicItem<IncludePathListItem, true> item = includePathsRepository().dynamicItemFromIndex(d_func()->m_includePaths);
    --item->m_refCount;
    if(!item->m_refCount)
      includePathsRepository().deleteItem(d_func()->m_includePaths);
    d_func_dynamic()->m_includePaths = 0;
  }
  if(!paths.isEmpty()) {
    IncludePathListItem item;
    foreach(const IndexedString &include, paths)
      item.m_includePathsList().append(include);
    d_func_dynamic()->m_includePaths = includePathsRepository().index(item);
    KDevelop::DynamicItem<IncludePathListItem, true> gotItem = includePathsRepository().dynamicItemFromIndex(d_func()->m_includePaths);
    ++gotItem->m_refCount;
  }
}

KDevelop::IndexedString Cpp::EnvironmentFile::headerGuard() const {
  ENSURE_READ_LOCKED
  return d_func()->m_guard;
}

void Cpp::EnvironmentFile::setHeaderGuard(KDevelop::IndexedString guardName) {
  ENSURE_WRITE_LOCKED
  d_func_dynamic()->m_guard = guardName;
}

void EnvironmentFile::addMissingIncludeFile(const IndexedString& file)
{
  ENSURE_WRITE_LOCKED
  d_func_dynamic()->m_missingIncludeFiles.insert(file);
}

const ReferenceCountedStringSet& EnvironmentFile::missingIncludeFiles() const
{
  ENSURE_READ_LOCKED
  return d_func()->m_missingIncludeFiles;
}

void EnvironmentFile::clearMissingIncludeFiles()
{
  ENSURE_WRITE_LOCKED
  d_func_dynamic()->m_missingIncludeFiles = ReferenceCountedStringSet();
}

void EnvironmentFile::addIncludeFile( const IndexedString& file, const ModificationRevision& modificationTime ) {
//   m_includeFiles.insert(file);
  ENSURE_WRITE_LOCKED

  addModificationRevision(file, modificationTime);
}

void EnvironmentFile::addStrings( const std::set<Utils::BasicSetRepository::Index>& strings ) {
  ENSURE_WRITE_LOCKED
  d_func_dynamic()->m_strings += ReferenceCountedStringSet( strings );
}

//The parameter should be a EnvironmentFile that was lexed AFTER the content of this file
void EnvironmentFile::merge( const EnvironmentFile& file ) {
  ENSURE_WRITE_LOCKED
  //We have to read the other file
  ENSURE_FILE_READ_LOCKED(file)
  
#ifdef DEBUG_LEXERCACHE
  if(debugging()) {
  kDebug( 9007 ) <<  id(this) << ": merging" << id(&file)  << "defined in macros this:" << print(d_func()->m_definedMacroNames)  << "defined macros in other:" << print(file.d_func()->m_definedMacroNames) << "undefined macros in other:" << print(file.d_func()->m_unDefinedMacroNames) << "strings in other:" << print(file.strings());
  }
#endif
  d_func_dynamic()->m_strings = (d_func()->m_strings + (file.d_func()->m_strings - d_func()->m_definedMacroNames)) - d_func()->m_unDefinedMacroNames;
  
  ///@todo Probably it's more efficient having 2 sets m_changedMacroNames and m_unDefinedMacroNames, where m_unDefinedMacroNames is a subset of m_changedMacroNames.  
  //Only add macros to the usedMacros-set that were not defined locally
  d_func_dynamic()->m_usedMacroNames += (file.d_func()->m_usedMacroNames - d_func()->m_definedMacroNames) - d_func()->m_unDefinedMacroNames;

  ///Merge those used macros that were not defined within this environment
  //This is slightly inefficient, would be nicer to have a fast mechanism for this.
  //This is not tragic since usually only few macros are used, and thus few need to be iterated.
  
  {
    Utils::Set definedMacroNamesSet = d_func()->m_definedMacroNames.set();
    Utils::Set unDefinedMacroNamesSet = d_func()->m_unDefinedMacroNames.set();
    
    std::set<uint> addUsedMacros;
    
    ReferenceCountedMacroSet backup = file.d_func()->m_usedMacros;
    Q_ASSERT(backup.set().setIndex() == file.d_func()->m_usedMacros.set().setIndex());
    
    for(ReferenceCountedMacroSet::Iterator it( file.d_func()->m_usedMacros.iterator() ); it; ++it) {
      const rpp::pp_macro& macro(it.ref());
      if( !definedMacroNamesSet.contains(macro.name.index()) && !unDefinedMacroNamesSet.contains(macro.name.index()) )
        addUsedMacros.insert(it.index());
    }
    
    //Must not happen, since we hold the locks
    Q_ASSERT(backup.set().setIndex() == file.d_func()->m_usedMacros.set().setIndex());
    
    if(!addUsedMacros.empty())
      d_func_dynamic()->m_usedMacros += ReferenceCountedMacroSet( addUsedMacros );
  }
  
  ifDebug( Q_ASSERT(d_func()->m_usedMacroNames.set().count() == d_func()->m_usedMacros.set().count()) );
  
  ///Add defined macros from the merged file.

  {
    Utils::Set otherDefinedMacroNamesSet = file.d_func()->m_definedMacroNames.set();
    Utils::Set otherUnDefinedMacroNamesSet = file.d_func()->m_unDefinedMacroNames.set();
    //Since merged macros overrule already stored ones, first remove the ones of the same name.

    Cpp::ReferenceCountedStringSet affectedMacros = d_func()->m_definedMacroNames & (file.d_func()->m_definedMacroNames + file.d_func()->m_unDefinedMacroNames);
    ReferenceCountedMacroSet potentiallyRemoveMacros = d_func()->m_definedMacros - file.d_func()->m_definedMacros;

    std::set<uint> removeDefinedMacros;
    #if 0
    if(env && affectedMacros.count() < potentiallyRemoveMacros.count()) {
      //In the environment there is a map that maps from macro-names to macros, which allows us iterating through 'affectedMacros' directly
      for( Cpp::ReferenceCountedStringSet::Iterator it( affectedMacros.iterator() ); it; ++it ) {
        rpp::pp_macro* macro = env->retrieveStoredMacro(*it);
        if(macro)
        {
          uint macroIndex = EnvironmentManager::macroDataRepository.findIndex( MacroRepositoryItemRequest(*macro) );
          if(macroIndex && potentiallyRemoveMacros.containsIndex(macroIndex))
            removeDefinedMacros.insert(macroIndex);
        }
      }
    }else
    #endif
      
    if(!affectedMacros.isEmpty()) {
      //We have to iterate through all potentially removed macros
      for( ReferenceCountedMacroSet::Iterator it( potentiallyRemoveMacros.iterator() ); it; ++it ) {
        const rpp::pp_macro& macro(it.ref());
        if( affectedMacros.contains( macro.name ) )
          removeDefinedMacros.insert(it.index());
      }
    }
    
    if(!removeDefinedMacros.empty())
      d_func_dynamic()->m_definedMacros -= ReferenceCountedMacroSet( removeDefinedMacros );
  }

  //Now merge in the new defined macros
  
  d_func_dynamic()->m_unDefinedMacroNames += file.d_func()->m_unDefinedMacroNames;
  d_func_dynamic()->m_unDefinedMacroNames -= file.d_func()->m_definedMacroNames;
  d_func_dynamic()->m_definedMacroNames -= file.d_func()->m_unDefinedMacroNames;
  d_func_dynamic()->m_definedMacroNames += file.d_func()->m_definedMacroNames;
  d_func_dynamic()->m_definedMacros += file.d_func()->m_definedMacros;

  ///Merge include-files, problems and other stuff
//   m_includeFiles += file.m_includeFiles.set();
  d_func_dynamic()->m_missingIncludeFiles += file.d_func()->m_missingIncludeFiles;
  
  addModificationRevisions(file.allModificationRevisions());

#ifdef DEBUG_LEXERCACHE
  if(debugging()) {
  kDebug( 9007 ) << id(this) << ": defined macro names in this after merge:" << d_func()->m_definedMacroNames.set().count() << print(d_func()->m_definedMacroNames);
  kDebug( 9007 ) << id(this) << ": defined in this after merge:" << d_func()->m_definedMacros.set().count() << print(d_func()->m_definedMacros);
  ifDebug( Q_ASSERT(d_func()->m_definedMacros.set().count() == d_func()->m_definedMacroNames.set().count()) );
  kDebug( 9007 ) << id(this) << ": undefined in this after merge:" << print(d_func()->m_unDefinedMacroNames);
  kDebug( 9007 ) << id(this) << ": strings in this after merge:" << print(strings());
  kDebug( 9007 ) << id(this) << ": macros used in this after merge:" << print(d_func()->m_usedMacroNames);
  }
#endif
}

size_t EnvironmentFile::hash() const {
  ///@todo remove the (size_t)(this), it is just temporary to make them unique, but will not work with serialization to disk.
  ///Instead, create a hash over the contained strings, and make sure the other hashes work reliably.
  return (size_t)(this); //m_usedMacros.valueHash() + m_usedMacros.idHash() + m_definedMacros.idHash() + m_definedMacros.valueHash() + (size_t)(this)/*+ m_strings.hash()*/; ///@todo is the string-hash needed here?
}

uint EnvironmentFile::identityOffset() const {
  ENSURE_READ_LOCKED
  return d_func()->m_identityOffset;
}

void EnvironmentFile::setIdentityOffset(uint offset) {
  ENSURE_WRITE_LOCKED
  d_func_dynamic()->m_identityOffset = offset;
}


int EnvironmentFile::type() const {
  ENSURE_READ_LOCKED
  return CppParsingEnvironment;
}
