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

#ifndef BUILD_ALGORITHM
#define BUILD_ALGORITHM

#include <string>

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

namespace build
{
  class scope;
  class prerequisite;
  class prerequisite_key;

  // The default prerequisite search implementation. It first calls the
  // target-type-specific search function. If that doesn't yeld anything,
  // it creates a new target.
  //
  target&
  search (prerequisite&);

  // As above but specify the prerequisite to search as a key.
  //
  target&
  search (const prerequisite_key&);

  // As above but override the target type. Useful for searching for
  // target group members where we need to search for a different
  // target type.
  //
  target&
  search (const target_type&, const prerequisite_key&);

  // As above but specify the prerequisite to search as individual
  // key components.
  //
  target&
  search (const target_type& type,
          const dir_path& dir,
          const std::string& name,
          const std::string* ext,
          scope* scope);

  // As above but specify the target type as template argument.
  //
  template <typename T>
  T&
  search (const dir_path& dir,
          const std::string& name,
          const std::string* ext,
          scope* scope);

  // Match a rule to the action/target with ambiguity detection.
  //
  void
  match (action, target&);

  // The standard prerequisite search and match implementations. They call
  // search_and_match_*() versions below passing non-empty directory for
  // the clean operation.
  //
  void
  search_and_match_prerequisites (action, target&);

  // If we are cleaning, this function doesn't go into group members,
  // as an optimization (the group should clean everything up).
  //
  void
  search_and_match_prerequisite_members (action, target&);

  // The actual prerequisite search and match implementations. They call
  // search() and then match() for each prerequisite in a loop. If this
  // target is a member of a group, then they first do this to the group's
  // prerequisites.
  //
  // If the directory argument is not empty, then they ignore (do not
  // match) prerequisites that are not in the same or its subdirectory.
  //
  void
  search_and_match_prerequisites (action, target&, const dir_path&);

  void
  search_and_match_prerequisite_members (action, target&, const dir_path&);

  // Unless already available, match, and, if necessary, execute
  // the group in order to obtain its members list.
  //
  group_view
  resolve_group_members (action, target&);

  // Inject dependency on the parent directory's fsdir{}, unless it is
  // the project's out_root (or is outside of any project; say, for
  // example, an install directory). Normally this function is called
  // from the rule's apply() function.
  //
  void
  inject_parent_fsdir (action, target&);

  // Execute the action on target, assuming a rule has been matched
  // and the recipe for this action has been set. This is the default
  // executor implementation.
  //
  target_state
  execute (action, target&);

  // A special version of the above that should be used for "direct"
  // and "now" execution, that is, side-stepping the normal target-
  // prerequisite relationship (so no dependents count is decremented)
  // and execution order (so this function will never return postponed
  // target state).
  //
  target_state
  execute_direct (action, target&);

  // The default prerequisite execute implementation. It calls execute()
  // on each non-ignored (non-NULL) prerequisite target in a loop. If this
  // target is a member of a group, then it first does this to the group's
  // prerequisites. Returns target_state::changed if any of them were
  // changed and target_state::unchanged otherwise. It treats targets
  // with postponed execution the same as ignored. Note that this
  // function can be used as a recipe.
  //
  target_state
  execute_prerequisites (action, target&);

  // As above but iterates over the prerequisites in reverse.
  //
  target_state
  reverse_execute_prerequisites (action, target&);

  // A version of the above that also determines whether the action
  // needs to be executed on the target based on the passed mtime
  // timestamp.
  //
  // Note that because we use mtime, this function should normally
  // only be used in the perform_update action.
  //
  bool
  execute_prerequisites (action, target&, const timestamp&);

  // Another version of the above that does two extra things for the
  // caller: it determines whether the action needs to be executed on
  // the target based on the passed timestamp and, if so, finds a
  // prerequisite of the specified type (e.g., a source file). If
  // there are multiple prerequisites of this type, then the last
  // is returned.
  //
  template <typename T>
  T*
  execute_prerequisites (action, target&, const timestamp&);

  // Return noop_recipe instead of using this function directly.
  //
  target_state
  noop_action (action, target&);

  // Default action implementation which forwards to the prerequisites.
  // Use default_recipe instead of using this function directly.
  //
  target_state
  default_action (action, target&);

  // Standard perform(clean) action implementation for the file target
  // or derived.
  //
  target_state
  perform_clean (action, target&);
}

#include <build/algorithm.ixx>
#include <build/algorithm.txx>

#endif // BUILD_ALGORITHM