// This file is part of the AspectC++ compiler 'ac++'.
// Copyright (C) 1999-2003  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                                            

#ifndef __IntroductionUnit_h__
#define __IntroductionUnit_h__

#include <string.h>
#include "version.h"

class ACM_Introduction;

#include "llvm/Support/MemoryBuffer.h"
#include "clang/Basic/Version.h"
#include "clang/Basic/SourceLocation.h"

class IntroductionUnit {
#if CLANG_VERSION_NUMBER >= VERSION_NUMBER_12_0_0
  llvm::MemoryBufferRef _target_unit;
#else
  const llvm::MemoryBuffer *_target_unit; // FIXME: Could get this from _loc.
#endif
  clang::SourceLocation _target_loc;
  clang::FileID _fid;
  int _nesting_level;
  int _precedence;
  bool _jp_needed;
  ACM_Introduction *_intro;
  string _content;
  int _kind; // IU_OTHER, IU_BASE, IU_MEMBERS, or 0-N for non-inline member 'kind'

  void update_nesting_level () {
    IntroductionUnit *outer = cast (_target_unit);
    if (outer)
      _nesting_level = outer->_nesting_level + 1;
  }
public:
  enum { IU_OTHER = -3, IU_BASE = -2, IU_MEMBERS = -1 };
  IntroductionUnit (int kind) :
#if CLANG_VERSION_NUMBER < VERSION_NUMBER_12_0_0
    _target_unit (0),
#endif
    _nesting_level (0), _precedence (0),
    _jp_needed (false), _intro (0), _kind(kind) {
  }
  bool is_base_intro () const { return _kind == IU_BASE; }
  bool is_members_intro () const { return _kind == IU_MEMBERS; }
  bool is_other_intro () const { return _kind == IU_OTHER; }
  int non_inline_member_no () const { return _kind; } // < 0: error
#if CLANG_VERSION_NUMBER >= VERSION_NUMBER_12_0_0
  void target_unit(llvm::MemoryBufferRef t) {
    _target_unit = t;
    update_nesting_level();
  }
  llvm::MemoryBufferRef target_unit () const { return _target_unit; }
  llvm::MemoryBufferRef final_target_unit () const {
    llvm::MemoryBufferRef result = _target_unit;
    const IntroductionUnit *intro_unit = 0;
    do {
      intro_unit = IntroductionUnit::cast (result);
      if (intro_unit)
        result = intro_unit->target_unit ();
    } while (intro_unit);
    return result;
  }
#else
  void target_unit(const llvm::MemoryBuffer *t) {
    _target_unit = t;
    update_nesting_level();
  }
  const llvm::MemoryBuffer *target_unit () const { return _target_unit; }
  const llvm::MemoryBuffer *final_target_unit () const {
    const llvm::MemoryBuffer *result = _target_unit;
    const IntroductionUnit *intro_unit = 0;
    do {
      intro_unit = IntroductionUnit::cast (result);
      if (intro_unit)
        result = intro_unit->target_unit ();
    } while (intro_unit);
    return result;
  }
#endif
  void location (clang::SourceLocation t) { _target_loc = t; }
  clang::SourceLocation location () const { return _target_loc; }
  void content (const string &str) { _content = str; }
  const string &content () const { return _content; }
  // create memory buffer on demand from _content string
  std::unique_ptr<llvm::MemoryBuffer> buffer () {
    std::ostringstream name;
    name << "<intro:" << (void *)this << '>';
    // cout << "name: " << name.str () << endl << _content << endl << "--" << endl;
    return llvm::MemoryBuffer::getMemBufferCopy(_content, name.str());
  }
  void file_id(clang::FileID t) { _fid = t; }
  clang::FileID file_id () const { return _fid; }
  int nesting_level () const { return _nesting_level; }
  int precedence () const { return _precedence; }
  void precedence (int p) { _precedence = p; }
  bool jp_needed () const { return _jp_needed; }
  void jp_needed (bool jpn) { _jp_needed = jpn; }
  void intro (ACM_Introduction *i) { _intro = i; }
  ACM_Introduction *intro () const { return _intro; }
#if CLANG_VERSION_NUMBER >= VERSION_NUMBER_12_0_0
  static IntroductionUnit *cast (llvm::MemoryBufferRef buf) {
    const char *name = buf.getBufferIdentifier().data();
    if (!strncmp(name, "<intro:", 7)) {
      // We can't use istringstream here because libc++'s implementation is
      // buggy http://llvm.org/PR19740.
      void *ptr;
      sscanf(name + 7, "%p", &ptr);
      return (IntroductionUnit *)ptr;
    }
    return 0;
  }
#else
  static IntroductionUnit *cast (const llvm::MemoryBuffer *buf) {
    const char *name = buf->getBufferIdentifier().data();
    if (!strncmp(name, "<intro:", 7)) {
      void *ptr;
      sscanf(name + 7, "%p", &ptr);
      return (IntroductionUnit *)ptr;
    }
    return 0;
  }
#endif
};

#endif // __IntroductionUnit_h__
