//===-- StringPrinter.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 liblldb_StringPrinter_h_
#define liblldb_StringPrinter_h_

#include <functional>
#include <string>

#include "lldb/lldb-forward.h"

#include "lldb/Utility/DataExtractor.h"

namespace lldb_private {
namespace formatters {
class StringPrinter {
public:
  enum class StringElementType { ASCII, UTF8, UTF16, UTF32 };

  enum class GetPrintableElementType { ASCII, UTF8 };

  class ReadStringAndDumpToStreamOptions {
  public:
    ReadStringAndDumpToStreamOptions()
        : m_location(0), m_process_sp(), m_stream(nullptr), m_prefix_token(),
          m_suffix_token(), m_quote('"'), m_source_size(0),
          m_needs_zero_termination(true), m_escape_non_printables(true),
          m_ignore_max_length(false), m_zero_is_terminator(true),
          m_language_type(lldb::eLanguageTypeUnknown) {}

    ReadStringAndDumpToStreamOptions(ValueObject &valobj);

    ReadStringAndDumpToStreamOptions &SetLocation(uint64_t l) {
      m_location = l;
      return *this;
    }

    uint64_t GetLocation() const { return m_location; }

    ReadStringAndDumpToStreamOptions &SetProcessSP(lldb::ProcessSP p) {
      m_process_sp = p;
      return *this;
    }

    lldb::ProcessSP GetProcessSP() const { return m_process_sp; }

    ReadStringAndDumpToStreamOptions &SetStream(Stream *s) {
      m_stream = s;
      return *this;
    }

    Stream *GetStream() const { return m_stream; }

    ReadStringAndDumpToStreamOptions &SetPrefixToken(const std::string &p) {
      m_prefix_token = p;
      return *this;
    }

    ReadStringAndDumpToStreamOptions &SetPrefixToken(std::nullptr_t) {
      m_prefix_token.clear();
      return *this;
    }

    const char *GetPrefixToken() const { return m_prefix_token.c_str(); }

    ReadStringAndDumpToStreamOptions &SetSuffixToken(const std::string &p) {
      m_suffix_token = p;
      return *this;
    }

    ReadStringAndDumpToStreamOptions &SetSuffixToken(std::nullptr_t) {
      m_suffix_token.clear();
      return *this;
    }

    const char *GetSuffixToken() const { return m_suffix_token.c_str(); }

    ReadStringAndDumpToStreamOptions &SetQuote(char q) {
      m_quote = q;
      return *this;
    }

    char GetQuote() const { return m_quote; }

    ReadStringAndDumpToStreamOptions &SetSourceSize(uint32_t s) {
      m_source_size = s;
      return *this;
    }

    uint32_t GetSourceSize() const { return m_source_size; }

    ReadStringAndDumpToStreamOptions &SetNeedsZeroTermination(bool z) {
      m_needs_zero_termination = z;
      return *this;
    }

    bool GetNeedsZeroTermination() const { return m_needs_zero_termination; }

    ReadStringAndDumpToStreamOptions &SetBinaryZeroIsTerminator(bool e) {
      m_zero_is_terminator = e;
      return *this;
    }

    bool GetBinaryZeroIsTerminator() const { return m_zero_is_terminator; }

    ReadStringAndDumpToStreamOptions &SetEscapeNonPrintables(bool e) {
      m_escape_non_printables = e;
      return *this;
    }

    bool GetEscapeNonPrintables() const { return m_escape_non_printables; }

    ReadStringAndDumpToStreamOptions &SetIgnoreMaxLength(bool e) {
      m_ignore_max_length = e;
      return *this;
    }

    bool GetIgnoreMaxLength() const { return m_ignore_max_length; }

    ReadStringAndDumpToStreamOptions &SetLanguage(lldb::LanguageType l) {
      m_language_type = l;
      return *this;
    }

    lldb::LanguageType GetLanguage() const

    {
      return m_language_type;
    }

  private:
    uint64_t m_location;
    lldb::ProcessSP m_process_sp;
    Stream *m_stream;
    std::string m_prefix_token;
    std::string m_suffix_token;
    char m_quote;
    uint32_t m_source_size;
    bool m_needs_zero_termination;
    bool m_escape_non_printables;
    bool m_ignore_max_length;
    bool m_zero_is_terminator;
    lldb::LanguageType m_language_type;
  };

  class ReadBufferAndDumpToStreamOptions {
  public:
    ReadBufferAndDumpToStreamOptions()
        : m_data(), m_stream(nullptr), m_prefix_token(), m_suffix_token(),
          m_quote('"'), m_source_size(0), m_escape_non_printables(true),
          m_zero_is_terminator(true), m_is_truncated(false),
          m_language_type(lldb::eLanguageTypeUnknown) {}

    ReadBufferAndDumpToStreamOptions(ValueObject &valobj);

    ReadBufferAndDumpToStreamOptions(
        const ReadStringAndDumpToStreamOptions &options);

    ReadBufferAndDumpToStreamOptions &SetData(DataExtractor d) {
      m_data = d;
      return *this;
    }

    lldb_private::DataExtractor GetData() const { return m_data; }

    ReadBufferAndDumpToStreamOptions &SetStream(Stream *s) {
      m_stream = s;
      return *this;
    }

    Stream *GetStream() const { return m_stream; }

    ReadBufferAndDumpToStreamOptions &SetPrefixToken(const std::string &p) {
      m_prefix_token = p;
      return *this;
    }

    ReadBufferAndDumpToStreamOptions &SetPrefixToken(std::nullptr_t) {
      m_prefix_token.clear();
      return *this;
    }

    const char *GetPrefixToken() const { return m_prefix_token.c_str(); }

    ReadBufferAndDumpToStreamOptions &SetSuffixToken(const std::string &p) {
      m_suffix_token = p;
      return *this;
    }

    ReadBufferAndDumpToStreamOptions &SetSuffixToken(std::nullptr_t) {
      m_suffix_token.clear();
      return *this;
    }

    const char *GetSuffixToken() const { return m_suffix_token.c_str(); }

    ReadBufferAndDumpToStreamOptions &SetQuote(char q) {
      m_quote = q;
      return *this;
    }

    char GetQuote() const { return m_quote; }

    ReadBufferAndDumpToStreamOptions &SetSourceSize(uint32_t s) {
      m_source_size = s;
      return *this;
    }

    uint32_t GetSourceSize() const { return m_source_size; }

    ReadBufferAndDumpToStreamOptions &SetEscapeNonPrintables(bool e) {
      m_escape_non_printables = e;
      return *this;
    }

    bool GetEscapeNonPrintables() const { return m_escape_non_printables; }

    ReadBufferAndDumpToStreamOptions &SetBinaryZeroIsTerminator(bool e) {
      m_zero_is_terminator = e;
      return *this;
    }

    bool GetBinaryZeroIsTerminator() const { return m_zero_is_terminator; }

    ReadBufferAndDumpToStreamOptions &SetIsTruncated(bool t) {
      m_is_truncated = t;
      return *this;
    }

    bool GetIsTruncated() const { return m_is_truncated; }

    ReadBufferAndDumpToStreamOptions &SetLanguage(lldb::LanguageType l) {
      m_language_type = l;
      return *this;
    }

    lldb::LanguageType GetLanguage() const

    {
      return m_language_type;
    }

  private:
    DataExtractor m_data;
    Stream *m_stream;
    std::string m_prefix_token;
    std::string m_suffix_token;
    char m_quote;
    uint32_t m_source_size;
    bool m_escape_non_printables;
    bool m_zero_is_terminator;
    bool m_is_truncated;
    lldb::LanguageType m_language_type;
  };

  // I can't use a std::unique_ptr for this because the Deleter is a template
  // argument there
  // and I want the same type to represent both pointers I want to free and
  // pointers I don't need to free - which is what this class essentially is
  // It's very specialized to the needs of this file, and not suggested for
  // general use
  template <typename T = uint8_t, typename U = char, typename S = size_t>
  struct StringPrinterBufferPointer {
  public:
    typedef std::function<void(const T *)> Deleter;

    StringPrinterBufferPointer(std::nullptr_t ptr)
        : m_data(nullptr), m_size(0), m_deleter() {}

    StringPrinterBufferPointer(const T *bytes, S size,
                               Deleter deleter = nullptr)
        : m_data(bytes), m_size(size), m_deleter(deleter) {}

    StringPrinterBufferPointer(const U *bytes, S size,
                               Deleter deleter = nullptr)
        : m_data(reinterpret_cast<const T *>(bytes)), m_size(size),
          m_deleter(deleter) {}

    StringPrinterBufferPointer(StringPrinterBufferPointer &&rhs)
        : m_data(rhs.m_data), m_size(rhs.m_size), m_deleter(rhs.m_deleter) {
      rhs.m_data = nullptr;
    }

    StringPrinterBufferPointer(const StringPrinterBufferPointer &rhs)
        : m_data(rhs.m_data), m_size(rhs.m_size), m_deleter(rhs.m_deleter) {
      rhs.m_data = nullptr; // this is why m_data has to be mutable
    }

    ~StringPrinterBufferPointer() {
      if (m_data && m_deleter)
        m_deleter(m_data);
      m_data = nullptr;
    }

    const T *GetBytes() const { return m_data; }

    const S GetSize() const { return m_size; }

    StringPrinterBufferPointer &
    operator=(const StringPrinterBufferPointer &rhs) {
      if (m_data && m_deleter)
        m_deleter(m_data);
      m_data = rhs.m_data;
      m_size = rhs.m_size;
      m_deleter = rhs.m_deleter;
      rhs.m_data = nullptr;
      return *this;
    }

  private:
    mutable const T *m_data;
    size_t m_size;
    Deleter m_deleter;
  };

  typedef std::function<StringPrinter::StringPrinterBufferPointer<
      uint8_t, char, size_t>(uint8_t *, uint8_t *, uint8_t *&)>
      EscapingHelper;
  typedef std::function<EscapingHelper(GetPrintableElementType)>
      EscapingHelperGenerator;

  static EscapingHelper
  GetDefaultEscapingHelper(GetPrintableElementType elem_type);

  template <StringElementType element_type>
  static bool
  ReadStringAndDumpToStream(const ReadStringAndDumpToStreamOptions &options);

  template <StringElementType element_type>
  static bool
  ReadBufferAndDumpToStream(const ReadBufferAndDumpToStreamOptions &options);
};

} // namespace formatters
} // namespace lldb_private

#endif // liblldb_StringPrinter_h_
