// This file is part of the AspectC++ compiler 'ac++'.
// Copyright (C) 1999-2013  The 'ac++' developers (see aspectc.org)
//
// This program is free software;  you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free
// Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
// MA  02111-1307  USA

#include "ClangWeaverBase.h"
#include "ACIntroducer.h"
#include "IntroductionUnit.h"
#include "LineDirectiveMgr.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/Support/MemoryBuffer.h"
#include "ACModel/Utils.h"
#include "version.h"

// Returns the location one past the end of the token.
static clang::SourceLocation getLocAfterToken(ClangToken tok) {
  unsigned len = tok.get ().getLength ();
  return tok.location ().getLocWithOffset (len);
}

const WeavePos &WeaverBase::get_pos_after_loc (clang::SourceLocation loc,
    WeavePos::Pos pos) {
  clang::SourceLocation result;
  if (loc.isValid() && loc.isMacroID ()) {
    clang::SourceLocation macro_end_loc;
    // See if we're at the end of a macro and get the location for the last
    // token if we are.
    bool is_at_end = clang::Lexer::isAtEndOfMacroExpansion(loc,
        getRewriter().getSourceMgr(), getRewriter().getLangOpts (), &macro_end_loc);
    if (is_at_end) {
      loc = macro_end_loc;
      // Now go on and get the location after this token.
    }
    clang::SourceLocation spell_loc = getRewriter().getSourceMgr().getSpellingLoc(loc);
    unsigned len = clang::Lexer::MeasureTokenLength(spell_loc, getRewriter().getSourceMgr(),
        getRewriter().getLangOpts ());
    result = loc.getLocWithOffset(len);
  }
  else {
    result = clang::Lexer::getLocForEndOfToken (loc, 0,
        getRewriter().getSourceMgr(), getRewriter().getLangOpts ());
  }

  assert (!result.isInvalid ());
  return weave_pos (result, pos);
}

const WeavePos &WeaverBase::weave_pos (ClangToken t, int p) {
  if (p == (int)WeavePos::WP_AFTER)
    return weave_pos (getLocAfterToken(t), p);
  else
    return weave_pos (t.location(), p);
}

const WeavePos &WeaverBase::weave_pos (clang::SourceLocation loc, int p) {
  assert(loc.isValid() && "Invalid SourceLocation!");

  // Optimization: If a location is directly before or after a macro we can turn
  // it into a non-macro location and avoid the extra work for macro expansion.
  // TODO: This is currently disabled because clang returns wrong locations for
  // some macro arguments.
  /*if (loc.isValid() && loc.isMacroID()) {
    clang::SourceManager &SM = _rewriter->getSourceMgr();
    clang::SourceLocation fixed_loc;
    if (SM.isAtStartOfImmediateMacroExpansion (loc, &fixed_loc))
      loc = fixed_loc;
    else if (SM.isAtEndOfImmediateMacroExpansion (loc, &fixed_loc))
      loc = fixed_loc;
  }*/

  // check whether the weaving position has already been created
  WPSet::iterator i = _positions.find (WeavePos (loc, (WeavePos::Pos)p));
  if (i != _positions.end ())
    return *i;

  // insert the position as a new one
  pair<WPSet::iterator, bool> result =
      _positions.insert (WeavePos (loc, (WeavePos::Pos)p));

  // the resulting iterator points to the new entry
  return *result.first;
}

const WeavePos &WeaverBase::header_pos () {
  clang::FileID main_fid = _rewriter->getSourceMgr().getMainFileID();
  return header_pos (_rewriter->getSourceMgr().getFileEntryForID (main_fid));
}

const WeavePos &WeaverBase::header_pos (ACFileID acfid) {
  const clang::FileEntry *fentry = acfid.file_entry ();
  clang::FileID fid = _rewriter->getSourceMgr().translateFile (fentry);
  clang::SourceLocation first =
      _rewriter->getSourceMgr().getLocForStartOfFile(fid);
  return weave_pos (first, WeavePos::WP_HEADER);
}

const WeavePos &WeaverBase::footer_pos () {
  clang::FileID main_fid = _rewriter->getSourceMgr().getMainFileID();
  return footer_pos (_rewriter->getSourceMgr().getFileEntryForID (main_fid));
}

const WeavePos &WeaverBase::footer_pos (ACFileID acfid) {
  const clang::FileEntry *fentry = acfid.file_entry ();
  clang::FileID fid = _rewriter->getSourceMgr().translateFile (fentry);
  clang::SourceLocation last =
      _rewriter->getSourceMgr().getLocForEndOfFile(fid);
  return weave_pos (last, WeavePos::WP_FOOTER);
}

// insert a generated string a given position
void WeaverBase::insert (const WeavePos &pos, const string &str,
                              bool before) {
  // return immediately if the string to paste is empty
  if (str == "")
    return;

  // If this is a bulk code insert wrap it in line directives.
  if (str.find('\n') != string::npos) {
    clang::PresumedLoc loc = _rewriter->getSourceMgr().getPresumedLoc(pos._loc);

    // <ac> directives are fixed up later on.
    std::ostringstream os;
    _line_mgr.directive (os, clang::PresumedLoc ());
    os << str;
    _line_mgr.directive (os, loc);

    if (before)
      pos._strings.push_front(os.str());
    else
      pos._strings.push_back(os.str());
    return;
  }

  // FIXME: handle before

  // store the unit in the right order for insertion (later when committing)
  if (before)
    pos._strings.push_front(str);
  else
    pos._strings.push_back(str);
}

// replace the text between two positions with some new text
void WeaverBase::replace (const WeavePos &from, const WeavePos &to,
    const string &str) {
  insert (from, str);
  kill (from, to);
}

extern "C" void __real__ZN5clang12Preprocessor3LexERNS_5TokenE(clang::Preprocessor &that, clang::Token &Result);

// make sure that Clang parses attributes, even if we are not in C++11-mode
void WeaverBase::lex_and_filter_attr(clang::Preprocessor &PP, clang::Token &result) {

  static clang::LangOptions &lang_options = _project.get_compiler_instance()->getLangOpts ();
  static clang::LangOptions original_lang_options = lang_options;
  static clang::SourceLocation prev_location;
  static std::set<clang::SourceLocation> hidden;
  static bool in_attr = false;

  do {
    __real__ZN5clang12Preprocessor3LexERNS_5TokenE(PP, result);
    if (result.is(clang::tok::eof) || !_annotation_map)
      return;
  } while (hidden.find(result.getLocation()) != hidden.end());

//  if (in_attr)
//    cout << "Got token: " << PP.getSpelling(result) << endl;
//    cout << "Backtrack enabled: " << PP.isBacktrackEnabled() << endl;
  clang::SourceManager &SM = _rewriter->getSourceMgr();
  if (prev_location.isValid() && !SM.isBeforeInTranslationUnit (prev_location, result.getLocation()))
    return;
  prev_location = result.getLocation();

//  cout << "New token: " << PP.getSpelling(result) << endl;
//  cout << "New token: " << PP.getSpelling(result) << result.getLocation().printToString(SM) << endl;
  if (PP.isBacktrackEnabled() || (!in_attr && result.isNot(clang::tok::l_square)) || (in_attr && result.isNot(clang::tok::r_square)))
    return;

//  PP.EnableBacktrackAtThisPos();
  list<clang::Token> reinsert_tokens;
  clang::Token tok;
  __real__ZN5clang12Preprocessor3LexERNS_5TokenE(PP, tok);
  reinsert_tokens.push_back(tok);
  if (!in_attr && tok.is(clang::tok::l_square)) {
    // here we have seen "[[": an attribute sequence starts
#if CLANG_VERSION_NUMBER >= VERSION_NUMBER_6_0_0
    lang_options.DoubleSquareBracketAttributes = 1;
#endif
    lang_options.CPlusPlus11 = 1;
    in_attr = true;
    hidden.clear();
    // parse the attribute list
    std::list<AnnotationMap::iterator> annotation_iters;
    bool in_seq = true;
    bool is_user_defined_seq = true;
    while (in_seq) {
      // create and initialize an attribute object
      Annotation attr;
      attr.checked = false;
      attr.attribute = 0;
      attr.seqBegin = prev_location;
      // parse an attribute, start with the qualified name
      std::vector<clang::Token> tokens;
      while (true) {
        __real__ZN5clang12Preprocessor3LexERNS_5TokenE(PP, tok);
        reinsert_tokens.push_back(tok);
        if (tok.isNot(clang::tok::identifier))
          break;
        tokens.push_back(tok);
        attr.attrNames.push_back(PP.getSpelling(tok));
        __real__ZN5clang12Preprocessor3LexERNS_5TokenE(PP, tok);
        reinsert_tokens.push_back(tok);
        if (tok.isNot(clang::tok::coloncolon))
          break;
        tokens.push_back(tok);
      };
      bool is_user_defined = (attr.attrNames.size() > 1 &&
          attr.attrNames[0] != "gnu" && attr.attrNames[0] != "clang");
      attr.is_user_defined = is_user_defined;
      if (is_user_defined) {
        for (unsigned int i = 1; i < tokens.size() - 2; i++) {
          hidden.insert(tokens[i].getLocation()); // hide the middle part so that clang can parse it
        }
      }
      else
        is_user_defined_seq = false;
      attr.tokBegin = tokens[0].getLocation();
      if (tok.is(clang::tok::l_paren)) {
        unsigned paren_count = 1;
        string param;
        do {
          __real__ZN5clang12Preprocessor3LexERNS_5TokenE(PP, tok);
          reinsert_tokens.push_back(tok);
          if (tok.is(clang::tok::l_paren))
            paren_count++;
          else if (tok.is(clang::tok::r_paren))
            paren_count--;
          else if (tok.is(clang::tok::comma)) {
            attr.params.push_back(param);
            param.clear();
          }
          if (paren_count > 0 && tok.isNot(clang::tok::comma)) {
            if (!param.empty())
              param += " ";
            param += PP.getSpelling(tok);
          }
          // TODO: error handling
        } while (paren_count > 0);
        attr.params.push_back(param);
        __real__ZN5clang12Preprocessor3LexERNS_5TokenE(PP, tok);
        reinsert_tokens.push_back(tok);
      }
      if (tok.isNot(clang::tok::comma))
        in_seq = false;
      attr.tokEnd = tok.getLocation(); // end is begin of next token => better for deleting the attribute
      std::pair<AnnotationMap::iterator,bool> ret =
          _annotation_map->insert(std::pair<clang::SourceLocation, Annotation>(attr.tokBegin, attr));
      annotation_iters.push_back (ret.first);
    }

    if (tok.is(clang::tok::r_square)) {
      clang::SourceLocation end = tok.getLocation();
      __real__ZN5clang12Preprocessor3LexERNS_5TokenE(PP, tok);
      reinsert_tokens.push_back(tok);
      if (tok.is(clang::tok::r_square)) {
        if (is_user_defined_seq) {
          kill(weave_pos(prev_location, WeavePos::WP_BEFORE),
              weave_pos(tok.getLocation().getLocWithOffset(1), WeavePos::WP_AFTER));
        }
        else {
          clang::SourceLocation begin = end;
          bool have_builtin = false;
          for (std::list<AnnotationMap::iterator>::reverse_iterator i = annotation_iters.rbegin();
              i != annotation_iters.rend(); ++i) {
            if ((*i)->second.is_user_defined) {
              begin = (*i)->second.tokBegin;
            }
            else {
              if (!have_builtin)
                begin = (*i)->second.tokEnd; // remove trailing ','
              if (begin != end)
                kill(weave_pos(begin, WeavePos::WP_BEFORE), weave_pos(end, WeavePos::WP_AFTER));
              begin = end = (*i)->second.tokBegin;
              have_builtin = true;
            }
          }
          if (begin != end)
            kill(weave_pos(begin, WeavePos::WP_BEFORE), weave_pos(end, WeavePos::WP_AFTER));
        }
      }
    }
  }
  else if (in_attr && tok.is(clang::tok::r_square)) {
    lang_options = original_lang_options;
    in_attr = false;
//    cout << "in_attr " << in_attr << " at " << tok.getLocation().printToString(SM) << endl;
  }
  //  PP.Backtrack();
#if CLANG_VERSION_NUMBER >= VERSION_NUMBER_3_9_1
    std::unique_ptr<clang::Token[]> input(new clang::Token[reinsert_tokens.size()]);
#else
    clang::Token *input = new clang::Token[reinsert_tokens.size()];
#endif
    int pos = 0;
    for (list<clang::Token>::const_iterator i = reinsert_tokens.begin(); i != reinsert_tokens.end(); ++i)
      input[pos++] = *i;
#if CLANG_VERSION_NUMBER >= VERSION_NUMBER_3_9_1
    PP.EnterTokenStream(std::move(input), reinsert_tokens.size(), true);
#else
    PP.EnterTokenStream(input, reinsert_tokens.size(), true, true);
#endif
  return;
}


void WeaverBase::collect_macro_token (clang::Preprocessor &PP, clang::Token &Result) {

  clang::SourceLocation loc = Result.getLocation();
  // ignore annotation (getSpelling fails for them!) and non-macro tokens
  if (Result.isAnnotation() || !loc.isMacroID())
    return;

  // TODO: ignore macros that are expand in a file, which does not belong to the project
  //       => improved efficiency; make sure the cache the last check's result

  bool invalid;
  std::string text = PP.getSpelling(Result, &invalid);
  if (invalid)
    return;

//  cout << "tok: " << Result.getName() << " "<< text << endl;
  
  clang::SourceManager &SM = _rewriter->getSourceMgr();
  clang::SourceLocation expansion_loc = SM.getExpansionLoc(Result.getLocation());
  clang::SourceLocation end_loc = loc.getLocWithOffset (text.size ());
  MacroMap::iterator mm_iter = _macros.find (expansion_loc);
  if (mm_iter == _macros.end ()) {
    MacroExpansion macro_expansion;
    macro_expansion._token_loc_map.insert (std::pair<clang::SourceLocation, unsigned int> (loc, 0u));
    macro_expansion._token_end_loc_map.insert (std::pair<clang::SourceLocation, unsigned int> (end_loc, text.size ()));
    macro_expansion._somewhere_inside = loc;
    macro_expansion._expanded_macro_text = text;
    _macros.insert (std::pair<clang::SourceLocation, MacroExpansion> (expansion_loc, macro_expansion));
//    cout << "new: " << text << endl;
  }
  else {
    std::pair<TokenLocMap::iterator, bool> ret;
    unsigned int pos = mm_iter->second._expanded_macro_text.size () + 1u;
    ret = mm_iter->second._token_loc_map.insert (std::pair<clang::SourceLocation, unsigned int> (loc, pos));
    if (ret.second) {
      mm_iter->second._token_end_loc_map.insert (std::pair<clang::SourceLocation, unsigned int> (end_loc, pos + text.size ()));
      mm_iter->second._expanded_macro_text += " ";
      mm_iter->second._expanded_macro_text += text;
//      cout << "ext: " << mm_iter->second._expanded_macro_text << endl;
    }
  }
}

bool WeaverBase::lookup_macro_token (clang::SourceLocation loc, clang::SourceLocation &mapped_loc) {
  clang::SourceManager &SM = _rewriter->getSourceMgr();

  // find the expansion information stored for 'loc' or return false
  clang::SourceLocation expansion_loc = SM.getExpansionLoc(loc);
  MacroMap::iterator mm_iter = _macros.find (expansion_loc);
  if (mm_iter == _macros.end ()) {
    cout << "fatal: macro not found" << endl;
    return false;
  }

  // generate a memory buffer for the expansion if not done yet
  clang::FileID &fid = mm_iter->second._fid;
  if (fid.isInvalid ()) {
#if CLANG_VERSION_NUMBER <= VERSION_NUMBER_3_5_2
    llvm::MemoryBuffer *mb =
      llvm::MemoryBuffer::getMemBufferCopy(mm_iter->second._expanded_macro_text + " ", "<macro-expansion>");
    fid = SM.createFileID(mb, clang::SrcMgr::C_User, 0, 0, expansion_loc);
#else // C++ 11 interface
    std::unique_ptr<llvm::MemoryBuffer> mb (llvm::MemoryBuffer::getMemBufferCopy(mm_iter->second._expanded_macro_text + " ", "<macro-expansion>"));
    fid = SM.createFileID(std::move (mb), clang::SrcMgr::C_User, 0, 0, expansion_loc);
#endif
  }
  
  clang::SourceLocation start = SM.getLocForStartOfFile(fid);
  TokenLocMap::iterator tl_iter = mm_iter->second._token_loc_map.find (loc);
  if (tl_iter == mm_iter->second._token_loc_map.end ()) {
    tl_iter = mm_iter->second._token_end_loc_map.find (loc);
    if (tl_iter == mm_iter->second._token_end_loc_map.end ()) {
      cout << "fatal: token not found" << endl;
      return false;
    }
  }
  unsigned int offset = tl_iter->second;
  mapped_loc = start.getLocWithOffset(offset);
  
  return true;
}


// kill the text between two positions
void WeaverBase::kill (const WeavePos &from, const WeavePos &to) {
  // TODO: this should not matter, the code shoud be able to deal with it
  assert (from._pos == WeavePos::WP_BEFORE);
  assert (to._pos == WeavePos::WP_AFTER);

  _deletions.push_back(LocPair(from._loc, to._loc));
}


void WeaverBase::commit_kills() {
  for (LocPairList::iterator i = _deletions.begin(); i != _deletions.end(); ++i)
    commit_kill (i->first, i->second);
}


// kill the text between two positions
void WeaverBase::commit_kill (clang::SourceLocation from, clang::SourceLocation to) {

  /*if (macro_problem (from) || macro_problem (to))
    return;

  bool has_nl = (strstr (from._token.text (), "\n") != NULL);
  if ((to._token && from._token != to._token) || has_nl)
    _line_mgr.insert ((Unit*)to._token.unit (), to._token, true);*/

  clang::Rewriter::RewriteOptions opts;
  opts.IncludeInsertsAtBeginOfRange = false;
  opts.IncludeInsertsAtEndOfRange   = false;
  clang::SourceManager &SM = _rewriter->getSourceMgr();

  // Handle kills inside a macro.

  if (from.isMacroID() || to.isMacroID()) {

    clang::SourceLocation loc1 = from;
    clang::SourceLocation expansion_loc1 = SM.getExpansionLoc(loc1);

    if (from.isMacroID() && !lookup_macro_token(from, loc1)) {
      cout << "fatal: 'from' location not found during kill" << endl;
      return;
    }

    clang::SourceLocation loc2 = to;
    clang::SourceLocation expansion_loc2 = SM.getExpansionLoc(loc2);
    if (to.isMacroID() && !lookup_macro_token(to, loc2)) {
      cout << "fatal: 'to' location not found during kill" << endl;
      return;
    }

    if (from.isMacroID() && !to.isMacroID()) {
      MacroMap::iterator macro_info = _macros.find (expansion_loc1);
      if (macro_info == _macros.end ()) {
        cout << "fatal: macro info not found in kill" << endl;
        return;
      }
      clang::SourceLocation macro_end = macro_info->second.endLoc (SM);
      _rewriter->RemoveText(clang::CharSourceRange::getCharRange(loc1, macro_end));

      clang::SourceLocation somewhere_inside = macro_info->second._somewhere_inside;
      std::pair<clang::SourceLocation, clang::SourceLocation> range =
          SM.getExpansionRange(somewhere_inside);
      unsigned len = _rewriter->getRangeSize(clang::SourceRange(range.second, range.second));
      _rewriter->RemoveText(clang::CharSourceRange::getCharRange(
          range.second.getLocWithOffset(len), loc2), opts);
    }
    else if (!from.isMacroID() && to.isMacroID()) {
      MacroMap::iterator macro_info = _macros.find (expansion_loc2);
      if (macro_info == _macros.end ()) {
        cout << "fatal: macro info not found in kill" << endl;
        return;
      }
      clang::SourceLocation macro_start = macro_info->second.startLoc (SM);
      _rewriter->RemoveText(clang::CharSourceRange::getCharRange(macro_start, loc2));

      clang::SourceLocation somewhere_inside = macro_info->second._somewhere_inside;
      std::pair<clang::SourceLocation, clang::SourceLocation> range =
          SM.getExpansionRange(somewhere_inside);
      _rewriter->RemoveText(clang::CharSourceRange::getCharRange(
          loc1, range.first), opts);
    }
    else {
      // both locations are in a macro instance

      // remove from 'from' to the macro end
      MacroMap::iterator macro_info1 = _macros.find (expansion_loc1);
      if (macro_info1 == _macros.end ()) {
        cout << "fatal: macro info not found in kill" << endl;
        return;
      }
      clang::SourceLocation macro_end = macro_info1->second.endLoc (SM);

      // remove from macro start to 'end'
      MacroMap::iterator macro_info2 = _macros.find (expansion_loc2);
      if (macro_info2 == _macros.end ()) {
        cout << "fatal: macro info not found in kill" << endl;
        return;
      }
      clang::SourceLocation macro_start = macro_info2->second.startLoc (SM);

      if (macro_info1 != macro_info2) {
        _rewriter->RemoveText(clang::CharSourceRange::getCharRange(loc1, macro_end));
        _rewriter->RemoveText(clang::CharSourceRange::getCharRange(macro_start, loc2));

        // remove from first macro expansion end to second macro expansion begin
        clang::SourceLocation somewhere_inside1 = macro_info1->second._somewhere_inside;
        std::pair<clang::SourceLocation, clang::SourceLocation> range1 =
            SM.getExpansionRange(somewhere_inside1);
        unsigned len = _rewriter->getRangeSize(clang::SourceRange(range1.second, range1.second));

        clang::SourceLocation somewhere_inside2 = macro_info2->second._somewhere_inside;
        std::pair<clang::SourceLocation, clang::SourceLocation> range2 =
            SM.getExpansionRange(somewhere_inside2);

        _rewriter->RemoveText(clang::CharSourceRange::getCharRange(
            range1.second.getLocWithOffset(len), range2.first), opts);
      }
      else {
        _rewriter->RemoveText(clang::CharSourceRange::getCharRange(loc1, loc2));
      }
    }
    return;
  }

  if (!from.isValid () || from.isMacroID ()||
      !to.isValid () || to.isMacroID ()) {
    cout << "Invalid or macro source loc in kill" << endl;
    return;
  }
  _rewriter->RemoveText (clang::CharSourceRange::getCharRange (from, to), opts);
}

void WeaverBase::commit_internal () {

  // commit all code deletions and clear the operations
  commit_kills();
  _deletions.clear();

  // commit code insertions
  clang::SourceManager &SM = _rewriter->getSourceMgr();
  // handle code that has to be inserted at weaving positions
  for (WPSet::iterator i = _positions.begin (); i != _positions.end (); ++i) {
    const WeavePos &wp = *i;
    if (wp._loc.isInvalid ()) {
      cout << "Invalid source loc" << endl;
      continue;
    }
    bool nl = false;
    bool last_nl = false;

    clang::SourceLocation loc;
    if (wp._loc.isMacroID()) {
      // find the location in the genrated memory buffer
      if (!lookup_macro_token (wp._loc, loc)) {
        cout << "fatal: location not found during commit" << endl;
        continue;
      }
      // mark this macro instance for expansion
      clang::SourceLocation expansion_loc = SM.getExpansionLoc(wp._loc);
      _macro_expansions.insert (expansion_loc);
    }
    else
      loc = wp._loc;

    // perform the code insertion
    for (list<string>::iterator si = wp._strings.begin();
	 si != wp._strings.end(); ++si) {
      const string &str = *si;
      _rewriter->InsertTextAfter(loc, str);
      nl |= (str.find("\n") != string::npos);
      last_nl = (str[str.size() - 1] == '\n');
    }
    if (nl) { // at least one new line character was inserted here
      // generate and insert a #line directive to make sure that debuggers will
      // be able to show the original source locations
      const llvm::MemoryBuffer *u = SM.getBuffer(SM.getFileID(wp._loc));
      if (IntroductionUnit::cast(u)) {
	// TODO: Why don't we insert the #line directive into introduced code? Check!
      }
      else {
        clang::PresumedLoc ploc = SM.getPresumedLoc(wp._loc);
        ostringstream directive;
        // TODO: this seems to be unnecessary as the line mgr issues \n
        if (!last_nl) directive << endl;
        _line_mgr.directive (directive, ploc);
	// TODO: Why wp._loc and not 'loc'? ...
        _rewriter->InsertTextAfter (wp._loc, directive.str ());
      }
    }

  }

  // clear the list of weaving positions used for this transaction
  _positions.clear();
}

void WeaverBase::resolve_macros() {
  clang::SourceManager &SM = _rewriter->getSourceMgr();

  for (set<clang::SourceLocation>::iterator i = _macro_expansions.begin ();
      i != _macro_expansions.end (); ++i) {
    MacroMap::iterator mm_iter = _macros.find (*i);
    if (mm_iter == _macros.end ()) {
      cout << "fatal: macro info not found in resolve_macros" << endl;
      continue;
    }
    clang::FileID fid = mm_iter->second._fid;
    clang::SourceLocation somewhere_inside = mm_iter->second._somewhere_inside;
//    cout << mm_iter->second._expanded_macro_text << endl;

    // expand macro
    std::pair<clang::SourceLocation, clang::SourceLocation> range =
        SM.getExpansionRange(somewhere_inside);
    const clang::RewriteBuffer *RB = _rewriter->getRewriteBufferFor(fid);
    if (!RB) {
      cout << "fatal: Rewrite buffer not found" << endl;
      continue;
    }
    string str(RB->begin(), RB->end());
//    cout << "Replace macro " << range.first.printToString(SM) << ": " << str << endl;
    _rewriter->ReplaceText(clang::SourceRange(range.first, range.second), str);
  }

  _macros.clear ();
  _macro_expansions.clear ();
}

bool WeaverBase::commit (clang::SourceManager &Target) {
  clang::SourceManager &SM = _rewriter->getSourceMgr();

  // Resolve weaving positions.
  commit_internal();

  // Put all macros back into the source.
  resolve_macros();

  for (clang::Rewriter::buffer_iterator i = _rewriter->buffer_begin(),
                                        e = _rewriter->buffer_end();
       i != e; ++i) {
    if (const clang::FileEntry *FE = SM.getFileEntryForID(i->first)) {
      std::string data(i->second.begin(), i->second.end());
#if CLANG_VERSION_NUMBER <= VERSION_NUMBER_3_5_2
      llvm::MemoryBuffer *Buf =
          llvm::MemoryBuffer::getMemBufferCopy(data, FE->getName());
      Target.overrideFileContents(FE, Buf);
#else // C++ 11 interface
      Target.overrideFileContents(FE, llvm::MemoryBuffer::getMemBufferCopy(data, FE->getName()));
#endif
    }
  }

  // renew rewrite
  const clang::LangOptions &LO = _rewriter->getLangOpts ();
  delete _rewriter;
  _rewriter = new clang::Rewriter (Target, LO);

  return true;
}

// postorder traversal of IntroductionUnits, inserting into their parents.
static void po_intros(WeaverBase &wb, ::ACIntroducer &intro,
                      IntroductionUnit *unit, unsigned depth = 0) {
  std::vector<IntroductionUnit *> &units = intro.get_intros_for(unit);

  for (unsigned i = 0, e = units.size(); i != e; ++i)
    po_intros(wb, intro, units[i], unit ? depth + 1 : depth);

  if (!unit)
    return;

  if (const clang::RewriteBuffer *RB =
          wb.getRewriter().getRewriteBufferFor(unit->file_id())) {
    std::string str(RB->begin(), RB->end());
    if (depth == 0) // Insert the last unit via weave_pos, there may be macros.
      wb.insert(wb.weave_pos(unit->location(), WeavePos::WP_AFTER), str);
    else
      wb.getRewriter().InsertText(unit->location(), str);
  } else {
#if CLANG_VERSION_NUMBER <= VERSION_NUMBER_3_5_2
    llvm::StringRef str(unit->buffer()->getBuffer());
#else // C++ 11 interface
    std::unique_ptr<llvm::MemoryBuffer> buf (unit->buffer());
    llvm::StringRef str(buf->getBuffer());
#endif
    if (depth == 0) // Insert the last unit via weave_pos, there may be macros.
      wb.insert(wb.weave_pos(unit->location(), WeavePos::WP_AFTER), str);
    else
      wb.getRewriter().InsertText(unit->location(), str);
  }
}

bool WeaverBase::resolve_introduction_units(::ACIntroducer &intro) {
  // Resolve weaving positions.
  commit_internal();
  // Insert introduction units.
  po_intros(*this, intro, NULL);
  commit_internal();
  resolve_macros();
  return true;
}
