//===-- OptionValue.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_INTERPRETER_OPTIONVALUE_H
#define LLDB_INTERPRETER_OPTIONVALUE_H

#include "lldb/Core/FormatEntity.h"
#include "lldb/Utility/Cloneable.h"
#include "lldb/Utility/CompletionRequest.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/Status.h"
#include "lldb/lldb-defines.h"
#include "lldb/lldb-private-enumerations.h"
#include "lldb/lldb-private-interfaces.h"

namespace lldb_private {

// OptionValue
class OptionValue {
public:
  enum Type {
    eTypeInvalid = 0,
    eTypeArch,
    eTypeArgs,
    eTypeArray,
    eTypeBoolean,
    eTypeChar,
    eTypeDictionary,
    eTypeEnum,
    eTypeFileLineColumn,
    eTypeFileSpec,
    eTypeFileSpecList,
    eTypeFormat,
    eTypeLanguage,
    eTypePathMap,
    eTypeProperties,
    eTypeRegex,
    eTypeSInt64,
    eTypeString,
    eTypeUInt64,
    eTypeUUID,
    eTypeFormatEntity
  };

  enum {
    eDumpOptionName = (1u << 0),
    eDumpOptionType = (1u << 1),
    eDumpOptionValue = (1u << 2),
    eDumpOptionDescription = (1u << 3),
    eDumpOptionRaw = (1u << 4),
    eDumpOptionCommand = (1u << 5),
    eDumpGroupValue = (eDumpOptionName | eDumpOptionType | eDumpOptionValue),
    eDumpGroupHelp =
        (eDumpOptionName | eDumpOptionType | eDumpOptionDescription),
    eDumpGroupExport = (eDumpOptionCommand | eDumpOptionName | eDumpOptionValue)
  };

  OptionValue() = default;

  virtual ~OptionValue() = default;

  // Subclasses should override these functions
  virtual Type GetType() const = 0;

  // If this value is always hidden, the avoid showing any info on this value,
  // just show the info for the child values.
  virtual bool ValueIsTransparent() const {
    return GetType() == eTypeProperties;
  }

  virtual const char *GetTypeAsCString() const {
    return GetBuiltinTypeAsCString(GetType());
  }

  static const char *GetBuiltinTypeAsCString(Type t);

  virtual void DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
                         uint32_t dump_mask) = 0;

  virtual Status
  SetValueFromString(llvm::StringRef value,
                     VarSetOperationType op = eVarSetOperationAssign);

  virtual void Clear() = 0;

  virtual lldb::OptionValueSP
  DeepCopy(const lldb::OptionValueSP &new_parent) const;

  virtual void AutoComplete(CommandInterpreter &interpreter,
                            CompletionRequest &request);

  // Subclasses can override these functions
  virtual lldb::OptionValueSP GetSubValue(const ExecutionContext *exe_ctx,
                                          llvm::StringRef name,
                                          bool will_modify,
                                          Status &error) const {
    error.SetErrorStringWithFormat("'%s' is not a value subvalue", name.str().c_str());
    return lldb::OptionValueSP();
  }

  virtual Status SetSubValue(const ExecutionContext *exe_ctx,
                             VarSetOperationType op, llvm::StringRef name,
                             llvm::StringRef value);

  virtual bool IsAggregateValue() const { return false; }

  virtual ConstString GetName() const { return ConstString(); }

  virtual bool DumpQualifiedName(Stream &strm) const;

  // Subclasses should NOT override these functions as they use the above
  // functions to implement functionality
  uint32_t GetTypeAsMask() { return 1u << GetType(); }

  static uint32_t ConvertTypeToMask(OptionValue::Type type) {
    return 1u << type;
  }

  static OptionValue::Type ConvertTypeMaskToType(uint32_t type_mask) {
    // If only one bit is set, then return an appropriate enumeration
    switch (type_mask) {
    case 1u << eTypeArch:
      return eTypeArch;
    case 1u << eTypeArgs:
      return eTypeArgs;
    case 1u << eTypeArray:
      return eTypeArray;
    case 1u << eTypeBoolean:
      return eTypeBoolean;
    case 1u << eTypeChar:
      return eTypeChar;
    case 1u << eTypeDictionary:
      return eTypeDictionary;
    case 1u << eTypeEnum:
      return eTypeEnum;
    case 1u << eTypeFileLineColumn:
      return eTypeFileLineColumn;
    case 1u << eTypeFileSpec:
      return eTypeFileSpec;
    case 1u << eTypeFileSpecList:
      return eTypeFileSpecList;
    case 1u << eTypeFormat:
      return eTypeFormat;
    case 1u << eTypeLanguage:
      return eTypeLanguage;
    case 1u << eTypePathMap:
      return eTypePathMap;
    case 1u << eTypeProperties:
      return eTypeProperties;
    case 1u << eTypeRegex:
      return eTypeRegex;
    case 1u << eTypeSInt64:
      return eTypeSInt64;
    case 1u << eTypeString:
      return eTypeString;
    case 1u << eTypeUInt64:
      return eTypeUInt64;
    case 1u << eTypeUUID:
      return eTypeUUID;
    }
    // Else return invalid
    return eTypeInvalid;
  }

  static lldb::OptionValueSP
  CreateValueFromCStringForTypeMask(const char *value_cstr, uint32_t type_mask,
                                    Status &error);

  // Get this value as a uint64_t value if it is encoded as a boolean, uint64_t
  // or int64_t. Other types will cause "fail_value" to be returned
  uint64_t GetUInt64Value(uint64_t fail_value, bool *success_ptr);

  OptionValueArch *GetAsArch();

  const OptionValueArch *GetAsArch() const;

  OptionValueArray *GetAsArray();

  const OptionValueArray *GetAsArray() const;

  OptionValueArgs *GetAsArgs();

  const OptionValueArgs *GetAsArgs() const;

  OptionValueBoolean *GetAsBoolean();

  OptionValueChar *GetAsChar();

  const OptionValueBoolean *GetAsBoolean() const;

  const OptionValueChar *GetAsChar() const;

  OptionValueDictionary *GetAsDictionary();

  const OptionValueDictionary *GetAsDictionary() const;

  OptionValueEnumeration *GetAsEnumeration();

  const OptionValueEnumeration *GetAsEnumeration() const;

  OptionValueFileSpec *GetAsFileSpec();

  const OptionValueFileSpec *GetAsFileSpec() const;

  OptionValueFileSpecList *GetAsFileSpecList();

  const OptionValueFileSpecList *GetAsFileSpecList() const;

  OptionValueFormat *GetAsFormat();

  const OptionValueFormat *GetAsFormat() const;

  OptionValueLanguage *GetAsLanguage();

  const OptionValueLanguage *GetAsLanguage() const;

  OptionValuePathMappings *GetAsPathMappings();

  const OptionValuePathMappings *GetAsPathMappings() const;

  OptionValueProperties *GetAsProperties();

  const OptionValueProperties *GetAsProperties() const;

  OptionValueRegex *GetAsRegex();

  const OptionValueRegex *GetAsRegex() const;

  OptionValueSInt64 *GetAsSInt64();

  const OptionValueSInt64 *GetAsSInt64() const;

  OptionValueString *GetAsString();

  const OptionValueString *GetAsString() const;

  OptionValueUInt64 *GetAsUInt64();

  const OptionValueUInt64 *GetAsUInt64() const;

  OptionValueUUID *GetAsUUID();

  const OptionValueUUID *GetAsUUID() const;

  OptionValueFormatEntity *GetAsFormatEntity();

  const OptionValueFormatEntity *GetAsFormatEntity() const;

  bool GetBooleanValue(bool fail_value = false) const;

  bool SetBooleanValue(bool new_value);

  char GetCharValue(char fail_value) const;

  char SetCharValue(char new_value);

  int64_t GetEnumerationValue(int64_t fail_value = -1) const;

  bool SetEnumerationValue(int64_t value);

  FileSpec GetFileSpecValue() const;

  bool SetFileSpecValue(const FileSpec &file_spec);

  FileSpecList GetFileSpecListValue() const;

  lldb::Format
  GetFormatValue(lldb::Format fail_value = lldb::eFormatDefault) const;

  bool SetFormatValue(lldb::Format new_value);

  lldb::LanguageType GetLanguageValue(
      lldb::LanguageType fail_value = lldb::eLanguageTypeUnknown) const;

  bool SetLanguageValue(lldb::LanguageType new_language);

  const FormatEntity::Entry *GetFormatEntity() const;

  const RegularExpression *GetRegexValue() const;

  int64_t GetSInt64Value(int64_t fail_value = 0) const;

  bool SetSInt64Value(int64_t new_value);

  llvm::StringRef GetStringValue(llvm::StringRef fail_value) const;
  llvm::StringRef GetStringValue() const { return GetStringValue(llvm::StringRef()); }

  bool SetStringValue(llvm::StringRef new_value);

  uint64_t GetUInt64Value(uint64_t fail_value = 0) const;

  bool SetUInt64Value(uint64_t new_value);

  UUID GetUUIDValue() const;

  bool SetUUIDValue(const UUID &uuid);

  bool OptionWasSet() const { return m_value_was_set; }

  void SetOptionWasSet() { m_value_was_set = true; }

  void SetParent(const lldb::OptionValueSP &parent_sp) {
    m_parent_wp = parent_sp;
  }

  lldb::OptionValueSP GetParent() const { return m_parent_wp.lock(); }

  void SetValueChangedCallback(std::function<void()> callback) {
    assert(!m_callback);
    m_callback = std::move(callback);
  }

  void NotifyValueChanged() {
    if (m_callback)
      m_callback();
  }

protected:
  using TopmostBase = OptionValue;

  // Must be overriden by a derived class for correct downcasting the result of
  // DeepCopy to it. Inherit from Cloneable to avoid doing this manually.
  virtual lldb::OptionValueSP Clone() const = 0;

  lldb::OptionValueWP m_parent_wp;
  std::function<void()> m_callback;
  bool m_value_was_set = false; // This can be used to see if a value has been
                                // set by a call to SetValueFromCString(). It is
                                // often handy to know if an option value was
                                // set from the command line or as a setting,
                                // versus if we just have the default value that
                                // was already populated in the option value.
};

} // namespace lldb_private

#endif // LLDB_INTERPRETER_OPTIONVALUE_H
