/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file LICENSE.rst or https://cmake.org/licensing for details.  */
#include "cmEvaluatedTargetProperty.h"

#include <unordered_map>
#include <utility>

#include "cmGenExContext.h"
#include "cmGenExEvaluation.h"
#include "cmGeneratorFileSets.h"
#include "cmGeneratorTarget.h"
#include "cmLinkItem.h"
#include "cmList.h"
#include "cmTargetPropertyEntry.h"

struct cmGeneratorExpressionDAGChecker;

namespace cm {
EvaluatedTargetPropertyEntry::EvaluatedTargetPropertyEntry(
  cmLinkItem const& item, cmListFileBacktrace bt)
  : LinkItem(item)
  , Backtrace(std::move(bt))
{
}

EvaluatedTargetPropertyEntry EvaluateTargetPropertyEntry(
  cmGeneratorTarget const* thisTarget, cm::GenEx::Context const& context,
  cmGeneratorExpressionDAGChecker* dagChecker, cm::TargetPropertyEntry& entry)
{
  EvaluatedTargetPropertyEntry ee(entry.LinkItem, entry.GetBacktrace());
  cmExpandList(entry.Evaluate(context, thisTarget, dagChecker), ee.Values);
  if (entry.GetHadContextSensitiveCondition()) {
    ee.ContextDependent = true;
  }
  return ee;
}

EvaluatedTargetPropertyEntries EvaluateTargetPropertyEntries(
  cmGeneratorTarget const* thisTarget, cm::GenEx::Context const& context,
  cmGeneratorExpressionDAGChecker* dagChecker,
  std::vector<std::unique_ptr<cm::TargetPropertyEntry>> const& in)
{
  EvaluatedTargetPropertyEntries out;
  out.Entries.reserve(in.size());
  for (auto const& entry : in) {
    out.Entries.emplace_back(
      EvaluateTargetPropertyEntry(thisTarget, context, dagChecker, *entry));
  }
  return out;
}

namespace {
void addInterfaceEntry(cmGeneratorTarget const* headTarget,
                       std::string const& prop,
                       cm::GenEx::Context const& context,
                       cmGeneratorExpressionDAGChecker* dagChecker,
                       EvaluatedTargetPropertyEntries& entries,
                       cmGeneratorTarget::UseTo usage,
                       std::vector<cmLinkItem> const& libraries)
{
  for (cmLinkItem const& lib : libraries) {
    if (lib.Target) {
      EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace);
      // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in our
      // caller's property and hand-evaluate it as if it were compiled.
      // Create a context as cmCompiledGeneratorExpression::Evaluate does.
      cm::GenEx::Evaluation eval(context, false, headTarget, headTarget, true,
                                 lib.Backtrace);
      cmExpandList(
        lib.Target->EvaluateInterfaceProperty(prop, &eval, dagChecker, usage),
        ee.Values);
      ee.ContextDependent = eval.HadContextSensitiveCondition;
      entries.Entries.emplace_back(std::move(ee));
    }
  }
}

void addInterfaceFileSetsEntry(cmGeneratorTarget const* headTarget,
                               cm::string_view type, std::string const& prop,
                               cm::GenEx::Context const& context,
                               cmGeneratorExpressionDAGChecker* dagChecker,
                               EvaluatedTargetPropertyEntries& entries,
                               std::vector<cmLinkItem> const& libraries)
{
  for (cmLinkItem const& lib : libraries) {
    if (lib.Target) {
      EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace);
      // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in our
      // caller's property and hand-evaluate it as if it were compiled.
      // Create a context as cmCompiledGeneratorExpression::Evaluate does.
      cm::GenEx::Evaluation eval(context, false, headTarget, headTarget, true,
                                 lib.Backtrace);
      cmExpandList(
        lib.Target->GetGeneratorFileSets()->EvaluateInterfaceProperty(
          type, prop, &eval, dagChecker),
        ee.Values);
      ee.ContextDependent = eval.HadContextSensitiveCondition;
      entries.Entries.emplace_back(std::move(ee));
    }
  }
}
}

void AddInterfaceEntries(cmGeneratorTarget const* headTarget,
                         std::string const& prop,
                         cm::GenEx::Context const& context,
                         cmGeneratorExpressionDAGChecker* dagChecker,
                         EvaluatedTargetPropertyEntries& entries,
                         IncludeRuntimeInterface searchRuntime,
                         cmGeneratorTarget::UseTo usage)
{
  if (searchRuntime == IncludeRuntimeInterface::Yes) {
    if (cmLinkImplementation const* impl =
          headTarget->GetLinkImplementation(context.Config, usage)) {
      entries.HadContextSensitiveCondition =
        impl->HadContextSensitiveCondition;

      auto runtimeLibIt =
        impl->LanguageRuntimeLibraries.find(context.Language);
      if (runtimeLibIt != impl->LanguageRuntimeLibraries.end()) {
        addInterfaceEntry(headTarget, prop, context, dagChecker, entries,
                          usage, runtimeLibIt->second);
      }
      addInterfaceEntry(headTarget, prop, context, dagChecker, entries, usage,
                        impl->Libraries);
    }
  } else {
    if (cmLinkImplementationLibraries const* impl =
          headTarget->GetLinkImplementationLibraries(context.Config, usage)) {
      entries.HadContextSensitiveCondition =
        impl->HadContextSensitiveCondition;
      addInterfaceEntry(headTarget, prop, context, dagChecker, entries, usage,
                        impl->Libraries);
    }
  }
}

void AddInterfaceFileSetsEntries(cmGeneratorTarget const* headTarget,
                                 cm::string_view type, std::string const& prop,
                                 cm::GenEx::Context const& context,
                                 cmGeneratorExpressionDAGChecker* dagChecker,
                                 EvaluatedTargetPropertyEntries& entries)
{
  if (cmLinkImplementationLibraries const* impl =
        headTarget->GetLinkImplementationLibraries(
          context.Config, cmGeneratorTarget::UseTo::Compile)) {
    entries.HadContextSensitiveCondition = impl->HadContextSensitiveCondition;
    addInterfaceFileSetsEntry(headTarget, type, prop, context, dagChecker,
                              entries, impl->Libraries);
  }
}
}
