// 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                                            

#include "IncludeGraph.h"
#include "ACFileID.h"

#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"
using namespace clang;

void IncludeGraph::IncludeGraphCallback::InclusionDirective(SourceLocation HashLoc,
                                      const clang::Token &IncludeTok,
                                      StringRef FileName,
                                      bool IsAngled,
                                      CharSourceRange FilenameRange,
#if CLANG_VERSION_NUMBER >= VERSION_NUMBER_18_1_3
                                      clang::OptionalFileEntryRef File,
#else
                                      const clang::FileEntry *File,
#endif
                                      StringRef SearchPath,
                                      StringRef RelativePath,
#if CLANG_VERSION_NUMBER >= VERSION_NUMBER_19_1_6
                                     const Module *SuggestedModule,
                                     bool ModuleImported,
#else
                                     const Module *Imported,
#endif
                                      SrcMgr::CharacteristicKind FileType
                                      ) {
  if (!File)
    return;

  Preprocessor &PP = _ig._project.get_compiler_instance()->getPreprocessor();
  SourceManager &SM = PP.getSourceManager();
#if CLANG_VERSION_NUMBER >= VERSION_NUMBER_18_1_3
  ACFileID FromFile = SM.getFileEntryRefForID(SM.getFileID(SM.getExpansionLoc(HashLoc)));
#else
  ACFileID FromFile = SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(HashLoc)));
#endif
  if (!FromFile.is_valid())
    return;

  _ig.add_edge (FromFile, File);
}

// Add an edge to the include graph from 'a' to 'b'
void IncludeGraph::add_edge (ACFileID a, ACFileID b) {
  Node &this_node = find (a);
  Node &included_node = find (b);
  this_node._includes.insert (&included_node);
}

// find/create an entry in '_nodes'
IncludeGraph::Node &IncludeGraph::find (ACFileID unit) {
  std::pair<Map::iterator,bool> p =
    _nodes.insert (Map::value_type (unit, Node (unit)));
  return p.first->second;
}

// Checks whether on unit 'a' directly or indirecly includes another unit 'b'
bool IncludeGraph::includes (ACFileID a, ACFileID b) const {
  Map::const_iterator a_iter = _nodes.find (a);
  Map::const_iterator b_iter = _nodes.find (b);
  if (a_iter == _nodes.end () || b_iter == _nodes.end ())
    return false;
  bool result = includes (a_iter->second, b_iter->second);
  
  // reset 'visited' flags
  reset_visited ();

  return result;
}

void IncludeGraph::reset_visited () const {
  // reset 'visited' flags
  Map::const_iterator iter = _nodes.begin ();
  while (iter != _nodes.end ()) {
    const IncludeGraph::Node &node = iter->second;
    node._visited = false;
    ++iter;
  }
}

// Checks whether there is a path from node 'a' to 'b' in the include graph
bool IncludeGraph::includes (const Node &a, const Node &b) const {
  // cycle detection
  if (a._visited)
    return false;
    
  set<Node*>::iterator iter = a._includes.begin ();
  a._visited = true;
  bool found = false;
  while (iter != a._includes.end ()) {
    if (*iter == &b || includes (**iter, b)) {
      found = true;
      break;
    }
    ++iter;
  }
  return found;  
}

void IncludeGraph::included_files (const Node &node,
  set<ACFileID> &units, bool only_project) const {

  // cycle detection
  if (node._visited)
    return;
    
  set<Node*>::iterator iter = node._includes.begin ();
  node._visited = true;
  while (iter != node._includes.end ()) {
    ACFileID unit = (*iter)->_unit;
    if (!only_project || _project.is_below (
        unit.name ()       // TODO: using the name might be inefficient
        ))
      units.insert (unit);
    included_files (**iter, units);
    ++iter;
  }
}
 
// Get all files the are directly or indirectly included
bool IncludeGraph::included_files (ACFileID unit,
  set<ACFileID> &units, bool only_project) const {
  Map::const_iterator iter = _nodes.find (unit);
  if (iter == _nodes.end ())
    return false;
  
  // collect the included file for this node
  included_files (iter->second, units, only_project);
  
  // reset 'visited' flags
  reset_visited ();

  return true;
}

void IncludeGraph::Node::dump () const {
  std::cout << "unit " /*<< _unit.get_unit ()*/ << " '" << _unit.name () << "' includes:" << std::endl;
  std::set<Node*>::iterator iter = _includes.begin ();
  while (iter != _includes.end ()) {
    std::cout << "  " << (*iter)->_unit.name () << std::endl;
    ++iter;
  }
}

void IncludeGraph::dump () const {
  Map::const_iterator iter = _nodes.begin ();
  while (iter != _nodes.end ()) {
    const IncludeGraph::Node &node = iter->second;
    node.dump ();
    ++iter;
  }
}
