// This file is part of the AspectC++ compiler 'ac++'.
// Copyright (C) 1999-2015  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 "ClangFileTracker.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"

#include <iostream>
using namespace std;

using namespace clang;

ClangFileTracker::ClangFileTracker(ACConfig &config, const std::string &ac_predef) :
    _config(config), _in_project(true), _ac_keywords_enabled(false), _ac_predefines(ac_predef) {
}

void ClangFileTracker::FileChanged (SourceLocation Loc, FileChangeReason Reason,
    SrcMgr::CharacteristicKind FileType, FileID PrevFID) {
  ACProject &project = _config.project();
  SourceManager &SM = project.get_compiler_instance()->getSourceManager();

  clang::PresumedLoc PL = SM.getPresumedLoc(Loc);
  llvm::StringRef Name = PL.getFilename();
  llvm::StringRef BufferName = SM.getBufferName(Loc);

  // after parsing the C++ built-ins, inject the AspectC++ built-ins in the preprocessor
  if (Name == "<built-in>" && Reason == ExitFile && !_ac_predefines.empty()) {
    std::unique_ptr<llvm::MemoryBuffer> SB =
      llvm::MemoryBuffer::getMemBufferCopy(_ac_predefines, "<ac-built-in>");
    assert(SB && "Cannot create predefined source buffer");
    FileID FID = SM.createFileID(std::move(SB));
    assert(FID.isValid() && "Could not create FileID for predefines?");

    // Clear the string to avoid repeated insertions -> we are still in <build-ins>!
    _ac_predefines.clear();
    
    // Start parsing the AspectC++ predefines.
    project.get_compiler_instance()->getPreprocessor().EnterSourceFile(FID, nullptr, SourceLocation());
  }

  if (!(CLANG_STR_STARTS_WITH(BufferName, "<intro") ||
      (!Name.empty() && (Name == "<ac>" || CLANG_STR_ENDS_WITH(Name, "_virt"))))) {
    // recalculate state only if we have a real file change
    _in_project = project.isBelow(Name.str().c_str());
    _ac_keywords_enabled = _in_project && (_config.keywords() || CLANG_STR_ENDS_WITH(Name, ".ah"));
  //  cout << "==> " << Name.str() << " " << BufferName.str() << " " << Reason << " " << _in_project << " " << _ac_keywords_enabled << endl;
  }
}
