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

#include <map>
#include <string>
#include <utility>

#include <cmext/algorithm>
#include <cmext/string_view>

#include "cmsys/RegularExpression.hxx"

#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"

namespace cm {
namespace FileSetMetadata {
cm::string_view VisibilityToName(Visibility vis)
{
  switch (vis) {
    case Visibility::Interface:
      return "INTERFACE"_s;
    case Visibility::Public:
      return "PUBLIC"_s;
    case Visibility::Private:
      return "PRIVATE"_s;
  }
  return ""_s;
}

Visibility VisibilityFromName(cm::string_view name, cmMakefile* mf)
{
  if (name == "INTERFACE"_s) {
    return Visibility::Interface;
  }
  if (name == "PUBLIC"_s) {
    return Visibility::Public;
  }
  if (name == "PRIVATE"_s) {
    return Visibility::Private;
  }
  auto msg = cmStrCat("File set visibility \"", name, "\" is not valid.");
  if (mf) {
    mf->IssueMessage(MessageType::FATAL_ERROR, msg);
  } else {
    cmSystemTools::Error(msg);
  }
  return Visibility::Private;
}

bool VisibilityIsForSelf(Visibility vis)
{
  switch (vis) {
    case Visibility::Interface:
      return false;
    case Visibility::Public:
    case Visibility::Private:
      return true;
  }
  return false;
}

bool VisibilityIsForInterface(Visibility vis)
{
  switch (vis) {
    case Visibility::Interface:
    case Visibility::Public:
      return true;
    case Visibility::Private:
      return false;
  }
  return false;
}

cm::string_view const HEADERS = "HEADERS"_s;
cm::string_view const SOURCES = "SOURCES"_s;
cm::string_view const CXX_MODULES = "CXX_MODULES"_s;

namespace {
std::map<cm::string_view, FileSetDescriptor> const FileSetDescriptors{
  { cm::FileSetMetadata::HEADERS,
    { cm::FileSetMetadata::HEADERS,
      cm::FileSetMetadata::FileSetLookup::Target,
      { DependencyMode ::Includables },
      DependencyMode ::Includables,
      { cm::FileSetMetadata::FileSetAttributes::FilesInMultipleFileSets } } },
  { cm::FileSetMetadata::SOURCES,
    { cm::FileSetMetadata::SOURCES,
      cm::FileSetMetadata::FileSetLookup::Dependencies,
      { DependencyMode ::IndependentFiles, DependencyMode ::Includables },
      DependencyMode ::Includables,
      { cm::FileSetMetadata::FileSetAttributes::FrameworkCompatible,
        cm::FileSetMetadata::FileSetAttributes::UnityBuild } } },
  { cm::FileSetMetadata::CXX_MODULES,
    { cm::FileSetMetadata::CXX_MODULES,
      cm::FileSetMetadata::FileSetLookup::Target,
      { DependencyMode ::IndependentFiles },
      DependencyMode ::IndependentFiles,
      {} } },
};

std::vector<cm::string_view> KnownTypes{ HEADERS, SOURCES, CXX_MODULES };

cmsys::RegularExpression const ValidNameRegex("^[a-z0-9][a-zA-Z0-9_]*$");
}

cm::optional<FileSetDescriptor> GetFileSetDescriptor(cm::string_view type)
{
  auto it = FileSetDescriptors.find(type);
  if (it != FileSetDescriptors.end()) {
    return it->second;
  }
  return cm::nullopt;
}

DependencyMode GetDependencyMode(cm::string_view type)
{
  auto descriptor = GetFileSetDescriptor(type);
  if (descriptor) {
    return descriptor->DefaultDependency;
  }
  return DependencyMode::Includables;
}
DependencyMode GetDependencyMode(cm::string_view type,
                                 DependencyMode requestedMode)
{
  auto descriptor = GetFileSetDescriptor(type);
  if (descriptor) {
    // Select the requested mode or the next-weakest mode that is supported by
    // the file set type
    auto mode = descriptor->SupportedDependencies.lower_bound(requestedMode);
    return mode == descriptor->SupportedDependencies.end()
      ? descriptor->DefaultDependency
      : *mode;
  }
  return DependencyMode::Includables;
}

AttributeSet GetAttributes(cm::string_view type)
{
  auto descriptor = GetFileSetDescriptor(type);
  if (descriptor) {
    return descriptor->Attributes;
  }
  return {};
}
bool IsFrameworkSupported(cm::string_view type)
{
  return GetAttributes(type).contains(FileSetAttributes::FrameworkCompatible);
}

std::vector<cm::string_view> const& GetKnownTypes()
{
  return KnownTypes;
}

bool IsKnownType(cm::string_view type)
{
  return cm::contains(GetKnownTypes(), type);
}

bool IsValidName(cm::string_view name)
{
  cmsys::RegularExpressionMatch match;
  return ValidNameRegex.find(name.data(), match);
}
}
}
