//===-- LVSupport.cpp -----------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This implements the supporting functions.
//
//===----------------------------------------------------------------------===//

#include "llvm/DebugInfo/LogicalView/Core/LVSupport.h"
#include "llvm/Support/FormatVariadic.h"

using namespace llvm;
using namespace llvm::logicalview;

#define DEBUG_TYPE "Support"

namespace {
// Unique string pool instance used by all logical readers.
LVStringPool StringPool;
} // namespace
LVStringPool &llvm::logicalview::getStringPool() { return StringPool; }

// Perform the following transformations to the given 'Path':
// - all characters to lowercase.
// - '\\' into '/' (Platform independent).
// - '//' into '/'
std::string llvm::logicalview::transformPath(StringRef Path) {
  std::string Name(Path);
  llvm::transform(Name, Name.begin(), tolower);
  llvm::replace(Name, '\\', '/');

  // Remove all duplicate slashes.
  size_t Pos = 0;
  while ((Pos = Name.find("//", Pos)) != std::string::npos)
    Name.erase(Pos, 1);

  return Name;
}

// Convert the given 'Path' to lowercase and change any matching character
// from 'CharSet' into '_'.
// The characters in 'CharSet' are:
//   '/', '\', '<', '>', '.', ':', '%', '*', '?', '|', '"', ' '.
std::string llvm::logicalview::flattenedFilePath(StringRef Path) {
  std::string Name(Path);
  llvm::transform(Name, Name.begin(), tolower);

  const char *CharSet = "/\\<>.:%*?|\" ";
  char *Input = Name.data();
  while (Input && *Input) {
    Input = strpbrk(Input, CharSet);
    if (Input)
      *Input++ = '_';
  };
  return Name;
}

using LexicalEntry = std::pair<size_t, size_t>;
using LexicalIndexes = SmallVector<LexicalEntry, 10>;

static LexicalIndexes getAllLexicalIndexes(StringRef Name) {
  if (Name.empty())
    return {};

  size_t AngleCount = 0;
  size_t ColonSeen = 0;
  size_t Current = 0;

  LexicalIndexes Indexes;

#ifndef NDEBUG
  auto PrintLexicalEntry = [&]() {
    LexicalEntry Entry = Indexes.back();
    llvm::dbgs() << formatv(
        "'{0}:{1}', '{2}'\n", Entry.first, Entry.second,
        Name.substr(Entry.first, Entry.second - Entry.first + 1));
  };
#endif

  size_t Length = Name.size();
  for (size_t Index = 0; Index < Length; ++Index) {
    LLVM_DEBUG({
      llvm::dbgs() << formatv("Index: '{0}', Char: '{1}'\n", Index,
                              Name[Index]);
    });
    switch (Name[Index]) {
    case '<':
      ++AngleCount;
      break;
    case '>':
      --AngleCount;
      break;
    case ':':
      ++ColonSeen;
      break;
    }
    if (ColonSeen == 2) {
      if (!AngleCount) {
        Indexes.push_back(LexicalEntry(Current, Index - 2));
        Current = Index + 1;
        LLVM_DEBUG({ PrintLexicalEntry(); });
      }
      ColonSeen = 0;
      continue;
    }
  }

  // Store last component.
  Indexes.push_back(LexicalEntry(Current, Length - 1));
  LLVM_DEBUG({ PrintLexicalEntry(); });
  return Indexes;
}

LVLexicalComponent llvm::logicalview::getInnerComponent(StringRef Name) {
  if (Name.empty())
    return {};

  LexicalIndexes Indexes = getAllLexicalIndexes(Name);
  if (Indexes.size() == 1)
    return std::make_tuple(StringRef(), Name);

  LexicalEntry BeginEntry = Indexes.front();
  LexicalEntry EndEntry = Indexes[Indexes.size() - 2];
  StringRef Outer =
      Name.substr(BeginEntry.first, EndEntry.second - BeginEntry.first + 1);

  LexicalEntry LastEntry = Indexes.back();
  StringRef Inner =
      Name.substr(LastEntry.first, LastEntry.second - LastEntry.first + 1);

  return std::make_tuple(Outer, Inner);
}

LVStringRefs llvm::logicalview::getAllLexicalComponents(StringRef Name) {
  if (Name.empty())
    return {};

  LexicalIndexes Indexes = getAllLexicalIndexes(Name);
  LVStringRefs Components;
  for (const LexicalEntry &Entry : Indexes)
    Components.push_back(
        Name.substr(Entry.first, Entry.second - Entry.first + 1));

  return Components;
}

std::string llvm::logicalview::getScopedName(const LVStringRefs &Components,
                                             StringRef BaseName) {
  if (Components.empty())
    return {};
  std::string Name(BaseName);
  raw_string_ostream Stream(Name);
  if (BaseName.size())
    Stream << "::";
  Stream << Components[0];
  for (LVStringRefs::size_type Index = 1; Index < Components.size(); ++Index)
    Stream << "::" << Components[Index];
  return Name;
}
