// This file is part of PUMA.
// Copyright (C) 1999-2003  The PUMA developer team.
//                                                                
// 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                                            

#ifndef __ClangIntroducer_h__
#define __ClangIntroducer_h__

// Class that is used for introductions during a parser run.
// The semantics object has a reference to an Introducer and uses
// the interface at some well defined (join) points.

//#include "clang/Sema/Sema.h"
#include "ACFileID.h"
#include "ACErrorStream.h"
#include "ACToken.h"
#include "PosHints.h"
#include "version.h"

#include "clang/Frontend/CompilerInstance.h"
#include "clang/Basic/Version.h"
#include "clang/AST/AttrIterator.h"

#include <set>

#include "ClangAnnotation.h"
#include "ClangFileTracker.h"
using std::set;
#include <map>
using std::map;
#include <deque>
#include <stack>

namespace clang {
  class CompilerInstance;
  class Parser;
  class Decl;
  class RecordDecl;
  class FunctionDecl;
  class NamespaceDecl;
  class TagDecl;
  class TemplateDecl;
  class TemplateArgument;
  class CXXRecordDecl;
  class ClassTemplatePartialSpecializationDecl;
  class TemplateDecl;
  class DeclaratorDecl;
  class AttributedStmt;
}

class ACConfig;
class IncludeGraph;
class CodeWeaver;
class ACM_Class;
class ACM_Introduction;
class ACM_ClassPlan;
class Plan;
class ModelBuilder;
class LineDirectiveMgr;
class IntroductionUnit;
class ClangIntroParser;
class WeavePos;

class ClangIntroducer {
  Plan &_plan;
  CodeWeaver &_code_weaver;
  ModelBuilder &_jpm;
  IncludeGraph &_ig;
  ACConfig &_conf;
  clang::CompilerInstance *_ci;
  PosHints &_pos_hints;

  set<ACFileID> _included_aspect_headers;
  typedef map<clang::Decl *, ACM_Class *> ClangTargetMap;
  ClangTargetMap _clangtargets;
    
  // find a member that is/must be link-once (global) code
  const clang::Decl *link_once_object (clang::CXXRecordDecl *ci);

  // Various sets used to avoid duplicated intros.
  std::set<clang::Decl *> _seen_classes;
  std::set<clang::Decl *> _classes_with_introduced_bases;
  std::set<clang::Decl *> _classes_with_introduced_members;
  std::set<clang::Decl *> _classes_with_introduced_non_inlines;

  // A map of all generated IntroductionUnits. To be used to resolve intros at
  // the end.
  std::map<IntroductionUnit *, std::vector<IntroductionUnit *> > _intros;

  // indicated whether the preprocessor has already signaled the end of file
  int _have_eof = false;

  // used to avoid nested watcher invocation during lookahead
  bool _token_watcher_disabled = false;

  // needed to track whether we are scanning tokens within the project ot from outside
  ClangFileTracker *_file_tracker;

  // needed for tracking unique token source locations in case of macro expansions
  // -> required to identify location hints by phase 1 parser
  int _macro_pos = -1;
  clang::SourceLocation _exp_loc;

  // internal helper class for handling introductions into the token stream
  class IntroContext {
  public:
    enum State { NEW, IN_PROGRESS, FINALIZING, FINISHED };
  private:
    int _depth;
    clang::TagDecl* _tag;
    State _state;
    ACM_ClassPlan *_plan;
    bool _needs_introspection;
  public:
    IntroContext (int depth, clang::TagDecl* tag) : _depth (depth),
      _tag(tag), _state(NEW), _plan(0), _needs_introspection(false) {}
    int depth () const { return _depth; }
    clang::TagDecl* tag () const { return _tag; }
    State &state() { return _state; }
    ACM_ClassPlan *&plan() { return _plan; }
    bool &needs_introspection() { return _needs_introspection; }
  };

  // state of the 'class_intro' function, which is called for every token
  int _depth = 0; // 0 means root scope; 1 means between first '{ ... }' pair; ...
  bool _in_def = false;
  bool _next_is_start = false;
  clang::SourceLocation _last_lbrace_loc;
  std::stack<IntroContext> _tag_intros;

  // state of 'annotation' function
  clang::LangOptions _original_lang_options;
  bool _options_changed = false;
  int _brackets = 0;
  clang::SourceLocation _seq_start_loc;

  // list of "delayed" code injections in front of classes that receiver introductions
  std::list<std::pair<clang::RecordDecl*, std::string>> _delayed_injections;

public:
  // Contructor: every Introducer knows the parser
  ClangIntroducer(Plan &pl, CodeWeaver &ccw, ModelBuilder &jpm,
                  IncludeGraph &ig, ACConfig &c, PosHints &pos_hints, clang::CompilerInstance *ci);

  // Destructor: release all allocated resources
  virtual ~ClangIntroducer ();
  
  ClangIntroParser *_parser;

  ModelBuilder &get_model_builder () const { return _jpm; }
  clang::SourceManager &get_source_manager () const { return _ci->getSourceManager(); }

  std::vector<IntroductionUnit *> &get_intros_for(IntroductionUnit *unit) {
    // note: 'unit' might be a nullptr.
    // In this case the map creates a new entry (empty vector).
    return _intros[unit];
  }

private:

  // This function is hooked into the token stream between the preprocessor and
  // the parser. It scans the stream and injects tokens at the appropriate places.
  void lex (clang::Token &tok);

  // This function is used internally to fetch more tokens in the context of 'lex'
  void internal_lex (clang::Token &tok);

  // used by lex to keep track of macro token locations -> needed for position hints
  void update_macro_pos(const clang::Token& tok);

  // The following functions transform the token stream. They are called from 'lex'.
  // They return 'true' if they have replaced 'tok'.
  // In this case all checks have to be performed again.
  void tunit_end (clang::Token &tok);
  void annotation (clang::Token &tok);
  void skip_asm (clang::Token &tok);
  bool attribute_def (clang::Token &tok);
  bool aspect_def (clang::Token &tok);
  bool advice_def (clang::Token &tok);
  bool pointcut_def (clang::Token &tok);
  void class_intro (clang::Token &tok);

  // These functions manipulated classes/structs/aspects on-the-fly
  void class_start (IntroContext &intro_context, clang::SourceLocation lbrac);
  bool class_end (IntroContext &intro_context, clang::Token &rbrace);

  // introduce base class into the class referenced by 'decl'
  // 'lbrac' is the position of the opening brace of the class body
  // iff 'first' is true, the injected code will start with ":"
  void introduce_base_classes (clang::Decl *decl, clang::SourceLocation lbrac,
      bool first);

  // introduce classes that describe attributes for introspection
  friend class ACAttrInfo;
  void inject_attribute_check (clang::SourceLocation loc, const clang::AttrVec &attrs,
      clang::DeclContext *decl_context);

  // introduce classes that describe attributes for introspection
  void inject_attribute_check (clang::SourceLocation loc, clang::SourceLocation attr_loc,
      clang::DeclContext *decl_context);

  // check in the plan if the current class needs an introduction
  ACM_Class *plan_lookup (clang::Decl *ci);

  // create the weaving plan for a given class
  ACM_Class *create_plan (clang::Decl *ci);
  
  // insert introspection code
  //  * at the end of class definitions, after AspectC++ introductions
  //  * before a slice, if "precedence" is given
  //void insert_introspection_code (Puma::CT_ClassDef *cd, int precedence = -1);

  // insert friend declarations for all aspects
  bool insert_aspect_friend_decls (std::string &str, clang::Decl *ci);

  // insert friend declarations for all aspects
  bool insert_aspectof_function (std::string &str, clang::CXXRecordDecl *ci);

  // insert introspection code
  //  * at the end of class definitions, after AspectC++ introductions
  //  * before a slice, if "precedence" is given
  std::string insert_introspection_code(clang::CXXRecordDecl *cd,
                                        int precedence = -1);

  // insert include files from units into the parser
  void handle_includes(set<ACFileID> &units, clang::SourceManager &sm,
                       clang::Parser *parser, clang::Decl *decl,
                       clang::SourceLocation CurLoc, ACErrorStream &err);

  // generate member intros for a given class
  void gen_intros (ACM_Class *jpl,
                   std::deque<IntroductionUnit *> &units,
                   ACErrorStream &err,
                   clang::TagDecl *target, int introduction_depth,
                   bool non_inline = false) const;

  // generate base class intros for a given class
  void gen_base_intros (ACM_Class *jpl,
                        std::deque<IntroductionUnit *> &units,
                        bool commas_only = false) const;

  void gen_base_intro (std::string &unit,
                       ACM_Introduction *ii, bool first) const;

  // update the base classes in the project model for a class that had
  // base class introductions
  void update_base_classes (clang::Decl *decl);

  // Parse an include.
  void parse (clang::Parser *parser, const std::string &source_code,
              clang::SourceLocation loc, clang::DeclContext *context = 0,
              bool force_cxx11 = false);

  // scan tokens from a string and append them to a token list
  clang::FileID scan(const std::string &in, const std::string &name,
      std::list<clang::Token> &tokens, clang::SourceLocation loc, bool add_eof = false);

  // scan tokens from a memory buffer and append them to a token list
  clang::FileID scan(std::unique_ptr<llvm::MemoryBuffer> mb,
      std::list<clang::Token> &tokens, clang::SourceLocation loc, bool add_eof = false);

  // (re-)inject a list of tokens into the token stream
  void inject_tokens(const std::list<clang::Token> &tokens);

  // Inject a buffer into the parser.
  clang::FileID inject(std::unique_ptr<llvm::MemoryBuffer> mb,
      clang::SourceLocation loc, bool add_eof = false);

  // Inject a (scanned) string into the parser.
  clang::FileID inject(const std::string &in, const std::string &name,
      clang::SourceLocation loc, bool add_eof = false);

  bool end_translation_unit(clang::Parser *parser);
};

#endif /* __ClangIntroducer_h__ */
