//===-- FormatEntity.h ------------------------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_CORE_FORMATENTITY_H
#define LLDB_CORE_FORMATENTITY_H

#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-types.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallVector.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>

namespace lldb_private {
class Address;
class CompletionRequest;
class ExecutionContext;
class FileSpec;
class Status;
class Stream;
class StringList;
class SymbolContext;
class ValueObject;
}

namespace llvm {
class StringRef;
}

namespace lldb_private {
namespace FormatEntity {
struct Entry {
  enum class Type {
    Invalid,
    ParentNumber,
    ParentString,
    EscapeCode,
    Root,
    String,
    Scope,
    Variable,
    VariableSynthetic,
    ScriptVariable,
    ScriptVariableSynthetic,
    AddressLoad,
    AddressFile,
    AddressLoadOrFile,
    ProcessID,
    ProcessFile,
    ScriptProcess,
    ThreadID,
    ThreadProtocolID,
    ThreadIndexID,
    ThreadName,
    ThreadQueue,
    ThreadStopReason,
    ThreadStopReasonRaw,
    ThreadReturnValue,
    ThreadCompletedExpression,
    ScriptThread,
    ThreadInfo,
    TargetArch,
    TargetFile,
    ScriptTarget,
    ModuleFile,
    File,
    Lang,
    FrameIndex,
    FrameNoDebug,
    FrameRegisterPC,
    FrameRegisterSP,
    FrameRegisterFP,
    FrameRegisterFlags,
    FrameRegisterByName,
    FrameIsArtificial,
    FrameKind,
    FrameBorrowedInfo,
    ScriptFrame,
    FunctionID,
    FunctionDidChange,
    FunctionInitialFunction,
    FunctionName,
    FunctionNameWithArgs,
    FunctionNameNoArgs,
    FunctionMangledName,
    FunctionPrefix,
    FunctionScope,
    FunctionBasename,
    FunctionNameQualifiers,
    FunctionTemplateArguments,
    FunctionFormattedArguments,
    FunctionReturnLeft,
    FunctionReturnRight,
    FunctionQualifiers,
    FunctionSuffix,
    FunctionAddrOffset,
    FunctionAddrOffsetConcrete,
    FunctionLineOffset,
    FunctionPCOffset,
    FunctionInitial,
    FunctionChanged,
    FunctionIsOptimized,
    FunctionIsInlined,
    LineEntryFile,
    LineEntryLineNumber,
    LineEntryColumn,
    LineEntryStartAddress,
    LineEntryEndAddress,
    CurrentPCArrow,
    ProgressCount,
    ProgressMessage,
    Separator,
  };

  struct Definition {
    /// The name/string placeholder that corresponds to this definition.
    const char *name;
    /// Insert this exact string into the output
    const char *string = nullptr;
    /// Entry::Type corresponding to this definition.
    const Entry::Type type;
    /// Data that is returned as the value of the format string.
    const uint64_t data = 0;
    /// The number of children of this node in the tree of format strings.
    const uint32_t num_children = 0;
    /// An array of "num_children" Definition entries.
    const Definition *children = nullptr;
    /// Whether the separator is kept during parsing or not.  It's used
    /// for entries with parameters.
    const bool keep_separator = false;

    constexpr Definition(const char *name, const FormatEntity::Entry::Type t)
        : name(name), type(t) {}

    constexpr Definition(const char *name, const char *string)
        : name(name), string(string), type(Entry::Type::EscapeCode) {}

    constexpr Definition(const char *name, const FormatEntity::Entry::Type t,
                         const uint64_t data)
        : name(name), type(t), data(data) {}

    constexpr Definition(const char *name, const FormatEntity::Entry::Type t,
                         const uint64_t num_children,
                         const Definition *children,
                         const bool keep_separator = false)
        : name(name), type(t), num_children(num_children), children(children),
          keep_separator(keep_separator) {}
  };

  template <size_t N>
  static constexpr Definition
  DefinitionWithChildren(const char *name, const FormatEntity::Entry::Type t,
                         const Definition (&children)[N],
                         bool keep_separator = false) {
    return Definition(name, t, N, children, keep_separator);
  }

  Entry(Type t = Type::Invalid, const char *s = nullptr,
        const char *f = nullptr);
  Entry(llvm::StringRef s);
  Entry(char ch);

  void AppendChar(char ch);

  void AppendText(const llvm::StringRef &s);

  void AppendText(const char *cstr);

  void AppendEntry(const Entry &&entry);

  void StartAlternative();

  void Clear() {
    string.clear();
    printf_format.clear();
    children_stack.clear();
    children_stack.emplace_back();
    type = Type::Invalid;
    fmt = lldb::eFormatDefault;
    number = 0;
    level = 0;
    deref = false;
  }

  static const char *TypeToCString(Type t);

  void Dump(Stream &s, int depth = 0) const;

  bool operator==(const Entry &rhs) const {
    if (string != rhs.string)
      return false;
    if (printf_format != rhs.printf_format)
      return false;
    if (children_stack != rhs.children_stack)
      return false;
    if (type != rhs.type)
      return false;
    if (fmt != rhs.fmt)
      return false;
    if (deref != rhs.deref)
      return false;
    return true;
  }

  operator bool() const { return type != Type::Invalid; }

  std::vector<Entry> &GetChildren();

  std::string string;
  std::string printf_format;

  /// A stack of children entries, used by Scope entries to provide alterantive
  /// children. All other entries have a stack of size 1.
  /// @{
  llvm::SmallVector<std::vector<Entry>, 1> children_stack;
  size_t level = 0;
  /// @}

  Type type = Type::Invalid;
  lldb::Format fmt = lldb::eFormatDefault;
  lldb::addr_t number = 0;
  bool deref = false;
};

class Formatter {
public:
  Formatter(const SymbolContext *sc, const ExecutionContext *exe_ctx,
            const Address *addr, bool function_changed, bool initial_function)
      : m_sc(sc), m_exe_ctx(exe_ctx), m_addr(addr),
        m_function_changed(function_changed),
        m_initial_function(initial_function) {}

  bool Format(const Entry &entry, Stream &s, ValueObject *valobj = nullptr);

  bool FormatStringRef(const llvm::StringRef &format, Stream &s,
                       ValueObject *valobj);

private:
  bool DumpValue(Stream &s, const FormatEntity::Entry &entry,
                 ValueObject *valobj);

  bool FormatFunctionNameForLanguage(Stream &s);

  /// Returns \c true if \a Format has been called for an \a Entry
  /// with the specified \c type recusrively. Some types are permitted
  /// to be formatted recursively, in which case this function returns
  /// \c false.
  bool IsInvalidRecursiveFormat(Entry::Type type);

  /// While the returned \a llvm::scope_exit is alive, the specified \c type
  /// is tracked by this \c Formatter object for recursion. Once the returned
  /// scope guard is destructed, the entry stops being tracked.
  auto PushEntryType(Entry::Type type) {
    m_entry_type_stack.push_back(type);
    return llvm::scope_exit([this] {
      assert(!m_entry_type_stack.empty());
      m_entry_type_stack.pop_back();
    });
  }

  const SymbolContext *const m_sc = nullptr;
  const ExecutionContext *const m_exe_ctx = nullptr;
  const Address *const m_addr = nullptr;
  const bool m_function_changed = false;
  const bool m_initial_function = false;

  llvm::SmallVector<Entry::Type, 1> m_entry_type_stack;
};

Status Parse(const llvm::StringRef &format, Entry &entry);

Status ExtractVariableInfo(llvm::StringRef &format_str,
                           llvm::StringRef &variable_name,
                           llvm::StringRef &variable_format);

void AutoComplete(lldb_private::CompletionRequest &request);

// Format the current elements into the stream \a s.
//
// The root element will be stripped off and the format str passed in will be
// either an empty string (print a description of this object), or contain a
// `.`-separated series like a domain name that identifies further
//  sub-elements to display.
bool FormatFileSpec(const FileSpec &file, Stream &s, llvm::StringRef elements,
                    llvm::StringRef element_format);

/// For each variable in 'args' this function writes the variable
/// name and it's pretty-printed value representation to 'out_stream'
/// in following format:
///
/// \verbatim
/// name_1=repr_1, name_2=repr_2 ...
/// \endverbatim
void PrettyPrintFunctionArguments(Stream &out_stream, VariableList const &args,
                                  ExecutionContextScope *exe_scope);
} // namespace FormatEntity
} // namespace lldb_private

#endif // LLDB_CORE_FORMATENTITY_H
