
// FIXME: This is a hack to give ClangIntroSema access rights to the current scope
// This has to be included FIRST!
#define private protected
#include "clang/Sema/Sema.h"
#undef private
#include "clang/Frontend/CompilerInstance.h"

#include "ClangIntroSema.h"
#include "ClangIntroducer.h"
#include "ClangBinding.h"
#include "ACProject.h"
#include "ModelBuilder.h"

using namespace clang;

//#include <iostream>
//using namespace std;

ClangIntroSema::ClangIntroSema(ClangIntroducer &introducer,
      Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
      TranslationUnitKind TUKind, CodeCompleteConsumer *CompletionConsumer) :
        Sema (pp, ctxt, consumer, TUKind, CompletionConsumer),
        _introducer (&introducer), _nested_tu_end(0), _nested_base_specifiers (0) {
}

ClangIntroSema::~ClangIntroSema() {
}

Scope *ClangIntroSema::setCurScope(Scope *new_scope) {
  Scope *old_scope = CurScope;
  CurScope = new_scope;
  return old_scope;
}

void ClangIntroSema::ActOnStartCXXMemberDeclarations(Scope *S, Decl *TagDecl,
    SourceLocation FinalLoc, bool IsFinalSpelledSealed,
    SourceLocation LBraceLoc) {
  _introducer->class_start(TagDecl, LBraceLoc);
  RealActOnStartCXXMemberDeclarations(*this, S, TagDecl, FinalLoc,
      IsFinalSpelledSealed, LBraceLoc);
}

void ClangIntroSema::ActOnBaseSpecifiers (Decl *ClassDecl,
    CXXBaseSpecifier **Bases, unsigned NumBases) {
  for (unsigned n = 0; n < NumBases; n++)
    _BaseInfo.push_back(Bases[n]);
  if (_nested_base_specifiers == 0) {
    _nested_base_specifiers++;
    _introducer->base_specs_end(ClassDecl);
    _nested_base_specifiers--;
    RealActOnBaseSpecifiers(*this, ClassDecl, _BaseInfo.data (), _BaseInfo.size ());
    _BaseInfo.clear ();
  }
}

void ClangIntroSema::ActOnFinishCXXMemberSpecification (Scope* S,
    SourceLocation RLoc, Decl *TagDecl, SourceLocation LBrac,
    SourceLocation RBrac, AttributeList *AttrList) {
  _introducer->class_end (TagDecl, RBrac);
  RealActOnFinishCXXMemberSpecification(*this, S, RLoc, TagDecl, LBrac, RBrac,
      AttrList);
}

void ClangIntroSema::ActOnEndOfTranslationUnit () {
  if (_nested_tu_end == 0) {
    _nested_tu_end++;
    _introducer->tunit_end ();
    _nested_tu_end--;
    RealActOnEndOfTranslationUnit(*this);
  }
}

bool ClangIntroSema::canSkipFunctionBody(Decl *FctDecl) {
  clang::SourceManager &sm = _introducer->_ci->getSourceManager();
  clang::PresumedLoc PL = sm.getPresumedLoc(FctDecl->getLocation());
  llvm::StringRef Name = PL.getFilename();
  llvm::StringRef BufferName = sm.getBufferName(FctDecl->getLocation());
  ACProject &project = _introducer->get_model_builder().get_project();
  bool in_project = (BufferName.startswith("<intro") ||
      (!Name.empty() && (Name.equals("<ac>") || project.isBelow(Name.str().c_str()))));
//  cout << Name.str() << " " << BufferName.str() << " " << in_project << endl;
  return !in_project && RealCanSkipFunctionBody(*this, FctDecl);
}
