// -*- mode: C++ -*-
//
// Copyright (C) 2013 Red Hat, Inc.
//
// This file is part of the GNU Application Binary Interface Generic
// Analysis and Instrumentation Library (libabigail).  This library is
// free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 3, or (at your option) any
// later version.

// This library 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 Lesser Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this program; see the file COPYING-LGPLV3.  If
// not, see <http://www.gnu.org/licenses/>.

/// @file

#ifndef __ABG_CORPUS_H__
#define __ABG_CORPUS_H__

#include <tr1/memory>
#include "abg-fwd.h"
#include "abg-traverse.h"

namespace abigail
{

/// @brief The source location of a token.
///
/// This represents the location of a token coming from a given ABI
/// Corpus.  This location is actually an abstraction of cursor in the
/// table of all the locations of all the tokens of the ABI Corpus.
/// That table is managed by the location_manager type.
class location
{
  unsigned		m_value;

  location(unsigned v) : m_value(v) { }

public:

  location() : m_value(0) { }

  unsigned
  get_value() const
  { return m_value; }

  operator bool() const
  { return !!m_value; }

  bool
  operator==(const location other) const
  { return m_value == other.m_value; }

  bool
  operator<(const location other) const
  { return m_value < other.m_value; }

  friend class location_manager;
};

/// @brief The entry point to manage locations.
///
/// This type keeps a table of all the locations for tokens of a
/// given ABI Corpus
class location_manager
{
  struct _Impl;

  /// Pimpl.
  shared_ptr<_Impl>			m_priv;

public:

  location_manager();

  /// Insert the triplet representing a source locus into our internal
  /// vector of location triplet.  Return an instance of location type,
  /// built from an integral type that represents the index of the
  /// source locus triplet into our source locus table.
  ///
  /// @param fle the file path of the source locus
  /// @param lne the line number of the source location
  /// @param col the column number of the source location
  location
  create_new_location(const std::string& fle, size_t lne, size_t col);

  /// Given an instance of location type, return the triplet
  /// {path,line,column} that represents the source locus.  Note that
  /// the location must have been previously created from the function
  /// location_manager::expand_location otherwise this function yields
  /// unexpected results, including possibly a crash.
  ///
  /// @param location the instance of location type to expand
  /// @param path the resulting path of the source locus
  /// @param line the resulting line of the source locus
  /// @param column the resulting colum of the source locus
  void
  expand_location(const location location, std::string& path,
		  unsigned& line, unsigned& column) const;
};


/// This is the abstraction of the set of relevant artefacts (types,
/// variable declarations, functions, templates, etc) bundled together
/// into a translation unit.
class translation_unit : public traversable_base
{
public:

  typedef shared_ptr<global_scope>     	global_scope_sptr;

private:
  std::string 				m_path;
  location_manager 			m_loc_mgr;
  mutable global_scope_sptr 		m_global_scope;

  // Forbidden
  translation_unit();

public:

  /// Constructor of translation_unit.
  ///
  /// @param path the location of the translation unit.
  translation_unit(const std::string& path);

  virtual ~translation_unit();

  /// @return the path of the compilation unit that gave birth to this
  /// instance of tranlation_unit.
  const std::string&
  get_path() const;

  /// Getter of the the global scope of the translation unit.
  ///
  /// @return the global scope of the current translation unit.  If
  /// there is not global scope allocated yet, this function creates one
  /// and returns it.
  const global_scope_sptr
  get_global_scope() const;

  /// Getter of the location manager for the current translation unit.
  ///
  /// @return a reference to the location manager for the current
  /// translation unit.
  location_manager&
  get_loc_mgr();

  /// const Getter of the location manager.
  ///
  /// @return a const reference to the location manager for the current
  /// translation unit.
  const location_manager&
  get_loc_mgr() const;

  /// Tests whether if the current translation unit contains ABI
  /// artifacts or not.
  ///
  /// @return true iff the current translation unit is empty.
  bool
  is_empty() const;

  /// Deserialize the contents of the external file into this
  /// translation_unit object.
  bool
  read();

  /// Serialize the contents of this translation unit object into an
  /// external file.
  ///
  /// @param out the ostream output object.
  bool
  write(std::ostream& out); 

  /// This implements the traversable_base::traverse pure virtual
  /// function.
  ///
  /// @param v the visitor used on the member nodes of the translation
  /// unit during the traversal.
  void
  traverse(ir_node_visitor& v);
};


/// This is the abstraction of a set of translation units (themselves
/// seen as bundles of unitary abi artefacts like types and decls)
/// bundled together as a corpus.  A corpus is thus the Application
/// binary interface of a program, a library or just a set of modules
/// put together.
class corpus
{
public:
  typedef std::string					string;
  typedef std::tr1::shared_ptr<translation_unit>	translation_unit_sptr;
  typedef std::list<translation_unit_sptr>		translation_units;

private:
  string 			m_name;
  translation_units 		m_members;

  corpus();

public:

  corpus(const string&);

  void
  add(const translation_unit_sptr);

  const translation_units&
  get_translation_units() const;

  bool
  is_empty() const;
};
}//end namespace abigail
#endif //__ABG_CORPUS_H__