// Copyright 2016 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 "gn/bundle_file_rule.h"

#include "base/strings/stringprintf.h"
#include "gn/output_file.h"
#include "gn/settings.h"
#include "gn/substitution_pattern.h"
#include "gn/substitution_writer.h"
#include "gn/target.h"
#include "gn/variables.h"

namespace {

Err ErrMissingPropertyForExpansion(const Settings* settings,
                                   const Target* target,
                                   const BundleFileRule* bundle_file_rule,
                                   const char* property_name) {
  std::string label = bundle_file_rule->target()->label().GetUserVisibleName(
      settings->default_toolchain_label());

  return Err(target->defined_from(),
             base::StringPrintf("Property %s is required.", property_name),
             base::StringPrintf(
                 "In order to expand {{%s}} in %s, the "
                 "property needs to be defined in the create_bundle target.",
                 property_name, label.c_str()));
}

}  // namespace

BundleFileRule::BundleFileRule(const Target* bundle_data_target,
                               const std::vector<SourceFile> sources,
                               const SubstitutionPattern& pattern)
    : target_(bundle_data_target), sources_(sources), pattern_(pattern) {
  // target_ may be null during testing.
  DCHECK(!target_ || target_->output_type() == Target::BUNDLE_DATA);
}

BundleFileRule::BundleFileRule(const BundleFileRule& other) = default;

BundleFileRule::~BundleFileRule() = default;

bool BundleFileRule::ApplyPatternToSource(const Settings* settings,
                                          const Target* target,
                                          const BundleData& bundle_data,
                                          const SourceFile& source_file,
                                          SourceFile* expanded_source_file,
                                          Err* err) const {
  std::string output_path;
  for (const auto& subrange : pattern_.ranges()) {
    if (subrange.type == &SubstitutionLiteral) {
      output_path.append(subrange.literal);
    } else if (subrange.type == &SubstitutionBundleRootDir) {
      if (bundle_data.root_dir().is_null()) {
        *err = ErrMissingPropertyForExpansion(settings, target, this,
                                              variables::kBundleRootDir);
        return false;
      }
      output_path.append(bundle_data.root_dir().value());
    } else if (subrange.type == &SubstitutionBundleContentsDir) {
      if (bundle_data.contents_dir().is_null()) {
        *err = ErrMissingPropertyForExpansion(settings, target, this,
                                              variables::kBundleContentsDir);
        return false;
      }
      output_path.append(bundle_data.contents_dir().value());
    } else if (subrange.type == &SubstitutionBundleResourcesDir) {
      if (bundle_data.resources_dir().is_null()) {
        *err = ErrMissingPropertyForExpansion(settings, target, this,
                                              variables::kBundleResourcesDir);
        return false;
      }
      output_path.append(bundle_data.resources_dir().value());
    } else if (subrange.type == &SubstitutionBundleExecutableDir) {
      if (bundle_data.executable_dir().is_null()) {
        *err = ErrMissingPropertyForExpansion(settings, target, this,
                                              variables::kBundleExecutableDir);
        return false;
      }
      output_path.append(bundle_data.executable_dir().value());
    } else {
      output_path.append(SubstitutionWriter::GetSourceSubstitution(
          target_, target_->settings(), source_file, subrange.type,
          SubstitutionWriter::OUTPUT_ABSOLUTE, SourceDir()));
    }
  }
  *expanded_source_file = SourceFile(std::move(output_path));
  return true;
}

bool BundleFileRule::ApplyPatternToSourceAsOutputFile(
    const Settings* settings,
    const Target* target,
    const BundleData& bundle_data,
    const SourceFile& source_file,
    OutputFile* expanded_output_file,
    Err* err) const {
  SourceFile expanded_source_file;
  if (!ApplyPatternToSource(settings, target, bundle_data, source_file,
                            &expanded_source_file, err)) {
    return false;
  }

  *expanded_output_file =
      OutputFile(settings->build_settings(), expanded_source_file);
  return true;
}