//===-- DemangledNameInfo.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
//
//===----------------------------------------------------------------------===//

#include "lldb/Core/DemangledNameInfo.h"

using namespace llvm::itanium_demangle;

namespace lldb_private {

bool TrackingOutputBuffer::shouldTrack() const {
  if (!isPrintingTopLevelFunctionType())
    return false;

  if (isInsideTemplateArgs())
    return false;

  if (NameInfo.ArgumentsRange.first > 0)
    return false;

  return true;
}

bool TrackingOutputBuffer::canFinalize() const {
  if (!isPrintingTopLevelFunctionType())
    return false;

  if (isInsideTemplateArgs())
    return false;

  if (NameInfo.ArgumentsRange.first == 0)
    return false;

  return true;
}

void TrackingOutputBuffer::updateBasenameEnd() {
  if (!shouldTrack())
    return;

  NameInfo.BasenameRange.second = getCurrentPosition();
}

void TrackingOutputBuffer::updateScopeStart() {
  if (!shouldTrack())
    return;

  NameInfo.ScopeRange.first = getCurrentPosition();
}

void TrackingOutputBuffer::updateScopeEnd() {
  if (!shouldTrack())
    return;

  NameInfo.ScopeRange.second = getCurrentPosition();
}

void TrackingOutputBuffer::finalizeArgumentEnd() {
  if (!canFinalize())
    return;

  NameInfo.ArgumentsRange.second = getCurrentPosition();
}

void TrackingOutputBuffer::finalizeQualifiersStart() {
  if (!canFinalize())
    return;

  NameInfo.QualifiersRange.first = getCurrentPosition();
}

void TrackingOutputBuffer::finalizeQualifiersEnd() {
  if (!canFinalize())
    return;

  NameInfo.QualifiersRange.second = getCurrentPosition();
}

void TrackingOutputBuffer::finalizeStart() {
  if (!shouldTrack())
    return;

  NameInfo.ArgumentsRange.first = getCurrentPosition();

  // If nothing has set the end of the basename yet (for example when
  // printing templates), then the beginning of the arguments is the end of
  // the basename.
  if (NameInfo.BasenameRange.second == 0)
    NameInfo.BasenameRange.second = getCurrentPosition();

  // There is something between the basename and the start of the function
  // arguments. Assume those are template arguments (which *should* be true for
  // C++ demangled names, but this assumption may change in the future, in
  // which case this needs to be adjusted).
  if (NameInfo.BasenameRange.second != NameInfo.ArgumentsRange.first)
    NameInfo.TemplateArgumentsRange = {NameInfo.BasenameRange.second,
                                       NameInfo.ArgumentsRange.first};

  assert(!shouldTrack());
  assert(canFinalize());
}

void TrackingOutputBuffer::finalizeEnd() {
  if (!canFinalize())
    return;

  if (NameInfo.ScopeRange.first > NameInfo.ScopeRange.second)
    NameInfo.ScopeRange.second = NameInfo.ScopeRange.first;
  NameInfo.BasenameRange.first = NameInfo.ScopeRange.second;

  // We call anything past the FunctionEncoding the "suffix".
  // In practice this would be nodes like `DotSuffix` that wrap
  // a FunctionEncoding.
  NameInfo.SuffixRange.first = getCurrentPosition();
}

ScopedOverride<unsigned> TrackingOutputBuffer::enterFunctionTypePrinting() {
  return {FunctionPrintingDepth, FunctionPrintingDepth + 1};
}

bool TrackingOutputBuffer::isPrintingTopLevelFunctionType() const {
  return FunctionPrintingDepth == 1;
}

void TrackingOutputBuffer::printLeft(const Node &N) {
  switch (N.getKind()) {
  case Node::KFunctionType:
    printLeftImpl(static_cast<const FunctionType &>(N));
    break;
  case Node::KFunctionEncoding:
    printLeftImpl(static_cast<const FunctionEncoding &>(N));
    break;
  case Node::KNestedName:
    printLeftImpl(static_cast<const NestedName &>(N));
    break;
  case Node::KNameWithTemplateArgs:
    printLeftImpl(static_cast<const NameWithTemplateArgs &>(N));
    break;
  default:
    OutputBuffer::printLeft(N);
  }

  // Keep updating suffix until we reach the end.
  NameInfo.SuffixRange.second = getCurrentPosition();
}

void TrackingOutputBuffer::printRight(const Node &N) {
  switch (N.getKind()) {
  case Node::KFunctionType:
    printRightImpl(static_cast<const FunctionType &>(N));
    break;
  case Node::KFunctionEncoding:
    printRightImpl(static_cast<const FunctionEncoding &>(N));
    break;
  default:
    OutputBuffer::printRight(N);
  }

  // Keep updating suffix until we reach the end.
  NameInfo.SuffixRange.second = getCurrentPosition();
}

void TrackingOutputBuffer::printLeftImpl(const FunctionType &N) {
  auto Scoped = enterFunctionTypePrinting();
  OutputBuffer::printLeft(N);
}

void TrackingOutputBuffer::printRightImpl(const FunctionType &N) {
  auto Scoped = enterFunctionTypePrinting();
  OutputBuffer::printRight(N);
}

void TrackingOutputBuffer::printLeftImpl(const FunctionEncoding &N) {
  auto Scoped = enterFunctionTypePrinting();

  const Node *Ret = N.getReturnType();
  if (Ret) {
    printLeft(*Ret);
    if (!Ret->hasRHSComponent(*this))
      *this += " ";
  }

  updateScopeStart();

  N.getName()->print(*this);
}

void TrackingOutputBuffer::printRightImpl(const FunctionEncoding &N) {
  auto Scoped = enterFunctionTypePrinting();
  finalizeStart();

  printOpen();
  N.getParams().printWithComma(*this);
  printClose();

  finalizeArgumentEnd();

  const Node *Ret = N.getReturnType();

  if (Ret)
    printRight(*Ret);

  finalizeQualifiersStart();

  auto CVQuals = N.getCVQuals();
  auto RefQual = N.getRefQual();
  auto *Attrs = N.getAttrs();
  auto *Requires = N.getRequires();

  if (CVQuals & QualConst)
    *this += " const";
  if (CVQuals & QualVolatile)
    *this += " volatile";
  if (CVQuals & QualRestrict)
    *this += " restrict";
  if (RefQual == FrefQualLValue)
    *this += " &";
  else if (RefQual == FrefQualRValue)
    *this += " &&";
  if (Attrs != nullptr)
    Attrs->print(*this);
  if (Requires != nullptr) {
    *this += " requires ";
    Requires->print(*this);
  }

  finalizeQualifiersEnd();
  finalizeEnd();
}

void TrackingOutputBuffer::printLeftImpl(const NestedName &N) {
  N.Qual->print(*this);
  *this += "::";
  updateScopeEnd();
  N.Name->print(*this);
  updateBasenameEnd();
}

void TrackingOutputBuffer::printLeftImpl(const NameWithTemplateArgs &N) {
  N.Name->print(*this);
  updateBasenameEnd();
  N.TemplateArgs->print(*this);
}

} // namespace lldb_private
