// file      : build/context -*- C++ -*-
// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#ifndef BUILD_CONTEXT
#define BUILD_CONTEXT

#include <string>
#include <ostream>
#include <cstdint> // uint64_t

#include <butl/filesystem>

#include <build/types>
#include <build/utility>
#include <build/operation>

namespace build
{
  class scope;
  class file;

  extern dir_path work;
  extern dir_path home;

  extern string_pool extension_pool;
  extern string_pool project_name_pool;

  // Current action (meta/operation).
  //
  extern const meta_operation_info* current_mif;
  extern const operation_info* current_inner_oif;
  extern const operation_info* current_outer_oif;

  extern execution_mode current_mode;

  // Total number of dependency relationships in the current action.
  // Together with the target::dependents count it is incremented
  // during the rule search & match phase and is decremented during
  // execution with the expectation of it reaching 0. Used as a sanity
  // check.
  //
  extern std::uint64_t dependency_count;

  // Reset the dependency state. In particular, this removes all the
  // targets, scopes, and variable names.
  //
  void
  reset ();

  // The dual interface wrapper for the {mk,rm}{file,dir}() functions
  // below that allows you to use it as a true/false return or a more
  // detailed enum from <butl/filesystem>
  //
  template <typename T>
  struct fs_status
  {
    T v;
    fs_status (T v): v (v) {};
    operator T () const {return v;}
    explicit operator bool () const {return v == T::success;}
  };

  // Create the directory and print the standard diagnostics. Note that
  // this implementation is not suitable if it is expected that the
  // directory will exist in the majority of case and performance is
  // important. See the fsdir{} rule for details.
  //
  fs_status<butl::mkdir_status>
  mkdir (const dir_path&);

  fs_status<butl::mkdir_status>
  mkdir_p (const dir_path&);

  // Remove the file and print the standard diagnostics. The second
  // argument is only used in diagnostics, to print the target name.
  // Passing the path for target will result in the relative path
  // being printed.
  //
  template <typename T>
  fs_status<butl::rmfile_status>
  rmfile (const path&, const T& target);

  inline fs_status<butl::rmfile_status>
  rmfile (const path& f) {return rmfile (f, f);}

  // Similar to rmfile() but for directories.
  //
  template <typename T>
  fs_status<butl::rmdir_status>
  rmdir (const dir_path&, const T& target);

  inline fs_status<butl::rmdir_status>
  rmdir (const dir_path& d) {return rmdir (d, d);}

  // Note that this function returns not_empty if we try to remove
  // a working directory.
  //
  fs_status<butl::rmdir_status>
  rmdir_r (const dir_path&);

  // Return the src/out directory corresponding to the given out/src. The
  // passed directory should be a sub-directory of out/src_root.
  //
  dir_path
  src_out (const dir_path& out, scope&);

  dir_path
  src_out (const dir_path& out,
           const dir_path& out_root, const dir_path& src_root);

  dir_path
  out_src (const dir_path& src, scope&);

  dir_path
  out_src (const dir_path& src,
           const dir_path& out_root, const dir_path& src_root);

  // If possible and beneficial, translate an absolute, normalized path
  // into relative to the relative_base directory, which is normally
  // work. Note that if the passed path is the same as relative_base,
  // then this function returns empty path.
  //
  template <typename K>
  basic_path<char, K>
  relative (const basic_path<char, K>&);

  // By default this points to work. Setting this to something else
  // should only be done in tightly controlled, non-parallel
  // situations (see dump). If base is empty, then relative()
  // returns the original path.
  //
  extern const dir_path* relative_base;

  // In addition to calling relative(), this function also uses shorter
  // notations such as '~/'.
  //
  std::string
  diag_relative (const path&);

  // As above but also adds trailing '/'. If the path is the same as
  // base, returns "./" if current is true and empty string otherwise.
  //
  std::string
  diag_relative (const dir_path&, bool current = true);

  // Action phrases, e.g., "configure update exe{foo}", "updating exe{foo}",
  // and "updating exe{foo} is configured".
  //
  class target;

  std::string
  diag_do (const action&, const target&);

  std::string
  diag_doing (const action&, const target&);

  std::string
  diag_done (const action&, const target&);
}

#include <build/context.txx>

#endif // BUILD_CONTEXT