// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/stl_util.h"
#include "gn/functions.h"
#include "gn/parse_tree.h"
#include "gn/scope.h"
#include "gn/settings.h"
#include "gn/substitution_list.h"
#include "gn/substitution_writer.h"
#include "gn/target.h"
#include "gn/value_extractors.h"

namespace functions {

const char kProcessFileTemplate[] = "process_file_template";
const char kProcessFileTemplate_HelpShort[] =
    "process_file_template: Do template expansion over a list of files.";
const char kProcessFileTemplate_Help[] =
    R"(process_file_template: Do template expansion over a list of files.

  process_file_template(source_list, template)

  process_file_template applies a template list to a source file list,
  returning the result of applying each template to each source. This is
  typically used for computing output file names from input files.

  In most cases, get_target_outputs() will give the same result with shorter,
  more maintainable code. This function should only be used when that function
  can't be used (like there's no target or the target is defined in another
  build file).

Arguments

  The source_list is a list of file names.

  The template can be a string or a list. If it is a list, multiple output
  strings are generated for each input.

  The template should contain source expansions to which each name in the
  source list is applied. See "gn help source_expansion".

Example

  sources = [
    "foo.idl",
    "bar.idl",
  ]
  myoutputs = process_file_template(
      sources,
      [ "$target_gen_dir/{{source_name_part}}.cc",
        "$target_gen_dir/{{source_name_part}}.h" ])

 The result in this case will be:
    [ "//out/Debug/foo.cc"
      "//out/Debug/foo.h"
      "//out/Debug/bar.cc"
      "//out/Debug/bar.h" ]
)";

Value RunProcessFileTemplate(Scope* scope,
                             const FunctionCallNode* function,
                             const std::vector<Value>& args,
                             Err* err) {
  if (args.size() != 2) {
    *err = Err(function->function(), "Expected two arguments");
    return Value();
  }

  // Source list.
  Target::FileList input_files;
  if (!ExtractListOfRelativeFiles(scope->settings()->build_settings(), args[0],
                                  scope->GetSourceDir(), &input_files, err))
    return Value();

  std::vector<std::string> result_files;
  SubstitutionList subst;

  // Template.
  const Value& template_arg = args[1];
  if (template_arg.type() == Value::STRING) {
    // Convert the string to a SubstitutionList with one pattern in it to
    // simplify the code below.
    std::vector<std::string> list;
    list.push_back(template_arg.string_value());
    if (!subst.Parse(list, template_arg.origin(), err))
      return Value();
  } else if (template_arg.type() == Value::LIST) {
    if (!subst.Parse(template_arg, err))
      return Value();
  } else {
    *err = Err(template_arg, "Not a string or a list.");
    return Value();
  }

  auto& types = subst.required_types();
  if (base::ContainsValue(types, &SubstitutionSourceTargetRelative)) {
    *err = Err(template_arg, "Not a valid substitution type for the function.");
    return Value();
  }

  SubstitutionWriter::ApplyListToSourcesAsString(
      nullptr, scope->settings(), subst, input_files, &result_files);

  // Convert the list of strings to the return Value.
  Value ret(function, Value::LIST);
  ret.list_value().reserve(result_files.size());
  for (const auto& file : result_files)
    ret.list_value().push_back(Value(function, file));

  return ret;
}

}  // namespace functions
