/*============================================================================
  CMake - Cross Platform Makefile Generator
  Copyright 2000-2009 Kitware, Inc., Insight Software Consortium

  Distributed under the OSI-approved BSD License (the "License");
  see accompanying file Copyright.txt for details.

  This software is distributed WITHOUT ANY WARRANTY; without even the
  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the License for more information.
============================================================================*/
#include "cmLocalFASTBuildGenerator.h"

#include "cmCustomCommandGenerator.h"
#include "cmFASTBuildTargetGenerator.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalFASTBuildGenerator.h"
#include "cmMakefile.h"
#include "cmSourceFile.h"
#include "cmStateDirectory.h"
#include "cmSystemTools.h"

#ifdef _WIN32
#  include "windows.h"
#endif

cmLocalFASTBuildGenerator::cmLocalFASTBuildGenerator(cmGlobalGenerator* gg,
                                                     cmMakefile* makefile)
  : cmLocalCommonGenerator(gg, makefile, WorkDir::TopBin)
{
}

cmLocalFASTBuildGenerator::~cmLocalFASTBuildGenerator() = default;

void cmLocalFASTBuildGenerator::Generate()
{
  const auto& targets = this->GetGeneratorTargets();
  for (const auto& target : targets) {
    if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
      continue;
    }
    cmFASTBuildTargetGenerator* tg =
      cmFASTBuildTargetGenerator::New(target.get());
    if (tg) {
      tg->Generate();
      delete tg;
    }
  }

  for (cmStateSnapshot const& state : GetStateSnapshot().GetChildren()) {
    std::string const& currentBinaryDir =
      state.GetDirectory().GetCurrentBinary();
    auto AllTargetName =
      GetGlobalFASTBuildGenerator()->ConvertToFASTBuildPath(currentBinaryDir);

    cmGlobalFASTBuildGenerator::FASTBuildTarget allTarget;
    cmGlobalFASTBuildGenerator::FASTBuildAliasNode allNode;
    allTarget.IsGlobal = true;
    allTarget.Name = allNode.Name = AllTargetName + "/all";
    for (const auto& Target : AllTargets[AllTargetName]) {
      allNode.Targets.insert(Target + "-products");
      allTarget.Dependencies.push_back(Target);
    }
    if (allNode.Targets.empty()) {
      allNode.Targets.insert("noop-products");
      allTarget.Dependencies.push_back("noop");
    }

    allTarget.AliasNodes.push_back(allNode);
    GetGlobalFASTBuildGenerator()->AddTarget(allTarget);
  }
}

void cmLocalFASTBuildGenerator::AddTarget(
  const cmGlobalFASTBuildGenerator::FASTBuildTarget& target)
{
  GetGlobalFASTBuildGenerator()->AddTarget(target);
  if (!target.IsGlobal && !target.IsExcluded) {
    AllTargets[cmSystemTools::GetFilenamePath(target.Name)].push_back(
      target.Name);
  }
}

const cmGlobalFASTBuildGenerator*
cmLocalFASTBuildGenerator::GetGlobalFASTBuildGenerator() const
{
  return static_cast<const cmGlobalFASTBuildGenerator*>(
    this->GetGlobalGenerator());
}

cmGlobalFASTBuildGenerator*
cmLocalFASTBuildGenerator::GetGlobalFASTBuildGenerator()
{
  return static_cast<cmGlobalFASTBuildGenerator*>(this->GetGlobalGenerator());
}

void cmLocalFASTBuildGenerator::ComputeObjectFilenames(
  std::map<cmSourceFile const*, std::string>& mapping,
  cmGeneratorTarget const* gt)
{
  for (auto& si : mapping) {
    cmSourceFile const* sf = si.first;
    si.second = this->GetObjectFileNameWithoutTarget(*sf, gt->ObjectDirectory);
  }
}

// TODO: Picked up from cmLocalUnixMakefileGenerator3.  Refactor it.
std::string cmLocalFASTBuildGenerator::GetTargetDirectory(
  const cmGeneratorTarget* target) const
{
  std::string dir = "CMakeFiles/";
  dir += target->GetName();
#if defined(__VMS)
  dir += "_dir";
#else
  dir += ".dir";
#endif
  return dir;
}

void cmLocalFASTBuildGenerator::AppendFlagEscape(
  std::string& flags, const std::string& rawFlag) const
{
  std::string escapedFlag = this->EscapeForShell(rawFlag);
  // Other make systems will remove the double $ but
  // fastbuild uses ^$ to escape it. So switch to that.
  // cmSystemTools::ReplaceString(escapedFlag, "$$", "^$");
  this->AppendFlags(flags, escapedFlag);
}
