/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#pragma once

#include <memory>

#include <react/renderer/attributedstring/AttributedString.h>

namespace facebook::react {

/*
 * Represents an object storing a shared `AttributedString` or a shared pointer
 * to some opaque platform-specific object that can be used as an attributed
 * string. The class serves two main purposes:
 *  - Represent type-erased attributed string entity (which can be
 * platform-specific or platform-independent);
 *  - Represent a container that can be copied with constant complexity.
 */
class AttributedStringBox final {
 public:
  enum class Mode { Value, OpaquePointer };

  /*
   * Default constructor constructs an empty string.
   */
  AttributedStringBox();

  /*
   * Custom explicit constructors.
   */
  explicit AttributedStringBox(const AttributedString& value);
  explicit AttributedStringBox(std::shared_ptr<void> opaquePointer);

  /*
   * Movable, Copyable, Assignable.
   */
  AttributedStringBox(const AttributedStringBox& other) = default;
  AttributedStringBox(AttributedStringBox&& other) noexcept;
  AttributedStringBox& operator=(const AttributedStringBox& other) = default;
  AttributedStringBox& operator=(AttributedStringBox&& other) noexcept;

  /*
   * Getters.
   */
  Mode getMode() const;
  const AttributedString& getValue() const;
  std::shared_ptr<void> getOpaquePointer() const;

 private:
  Mode mode_;
  std::shared_ptr<const AttributedString> value_;
  std::shared_ptr<void> opaquePointer_;
};

bool operator==(const AttributedStringBox& lhs, const AttributedStringBox& rhs);
bool operator!=(const AttributedStringBox& lhs, const AttributedStringBox& rhs);

} // namespace facebook::react

template <>
struct std::hash<facebook::react::AttributedStringBox> {
  size_t operator()(
      const facebook::react::AttributedStringBox& attributedStringBox) const {
    switch (attributedStringBox.getMode()) {
      case facebook::react::AttributedStringBox::Mode::Value:
        return std::hash<facebook::react::AttributedString>()(
            attributedStringBox.getValue());
      case facebook::react::AttributedStringBox::Mode::OpaquePointer:
        return std::hash<std::shared_ptr<void>>()(
            attributedStringBox.getOpaquePointer());
    }
  }
};
