/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef intl_components_BidiEmbeddingLevel_h_
#define intl_components_BidiEmbeddingLevel_h_

#include <cstdint>

/**
 * This file has the BidiEmbeddingLevel and BidiDirection enum broken out from
 * the main Bidi class for faster includes. This code is used in Layout which
 * could trigger long build times when changing core mozilla::intl files.
 */
namespace mozilla::intl {

/**
 * This enum unambiguously classifies text runs as either being left to right,
 * or right to left.
 */
enum class BidiDirection : uint8_t {
  // Left to right text.
  LTR = 0,
  // Right to left text.
  RTL = 1,
};

/**
 * Embedding levels are numbers that indicate how deeply the bidi text is
 * embedded, and the direction of text on that embedding level. When switching
 * between strongly LTR code points and strongly RTL code points the embedding
 * level normally switches between an embedding level of 0 (LTR) and 1 (RTL).
 * The only time the embedding level increases is if the embedding code points
 * are used. This is the Left-to-Right Embedding (LRE) code point (U+202A), or
 * the Right-to-Left Embedding (RLE) code point (U+202B). The minimum
 * embedding level of text is zero, and the maximum explicit depth is 125.
 *
 * The most significant bit is reserved for additional meaning. It can be used
 * to signify in certain APIs that the text should by default be LTR or RTL if
 * no strongly directional code points are found.
 *
 * Bug 1736595: At the time of this writing, some places in Gecko code use a 1
 * in the most significant bit to indicate that an embedding level has not
 * been set. This leads to an ambiguous understanding of what the most
 * significant bit actually means.
 */
class BidiEmbeddingLevel {
 public:
  explicit BidiEmbeddingLevel(uint8_t aValue) : mValue(aValue) {}
  explicit BidiEmbeddingLevel(int aValue)
      : mValue(static_cast<uint8_t>(aValue)) {}

  BidiEmbeddingLevel() = default;

  // Enable the copy operators, but disable move as this is only a uint8_t.
  BidiEmbeddingLevel(const BidiEmbeddingLevel& other) = default;
  BidiEmbeddingLevel& operator=(const BidiEmbeddingLevel& other) = default;

  /**
   * Determine the direction of the embedding level by looking at the least
   * significant bit. If it is 0, then it is LTR. If it is 1, then it is RTL.
   */
  BidiDirection Direction();

  /**
   * Create a left-to-right embedding level.
   */
  static BidiEmbeddingLevel LTR();

  /**
   * Create an right-to-left embedding level.
   */
  static BidiEmbeddingLevel RTL();

  /**
   * When passed into `SetParagraph`, the direction is determined by first
   * strongly directional character, with the default set to left-to-right if
   * none is found.
   *
   * This is encoded with the highest bit set to 1.
   */
  static BidiEmbeddingLevel DefaultLTR();

  /**
   * When passed into `SetParagraph`, the direction is determined by first
   * strongly directional character, with the default set to right-to-left if
   * none is found.
   *
   * * This is encoded with the highest and lowest bits set to 1.
   */
  static BidiEmbeddingLevel DefaultRTL();

  bool IsDefaultLTR() const;
  bool IsDefaultRTL() const;
  bool IsLTR() const;
  bool IsRTL() const;
  bool IsSameDirection(BidiEmbeddingLevel aOther) const;

  /**
   * Get the underlying value as a uint8_t.
   */
  uint8_t Value() const;

  /**
   * Implicitly convert to the underlying value.
   */
  operator uint8_t() const { return mValue; }

 private:
  uint8_t mValue = 0;
};

}  // namespace mozilla::intl
#endif
