// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_SYNC_MODEL_STRING_ORDINAL_H_
#define COMPONENTS_SYNC_MODEL_STRING_ORDINAL_H_

#include <cstddef>
#include <cstdint>
#include <string>

namespace mojo {
template <typename DataViewType, typename T>
struct StructTraits;
}

namespace syncer {

namespace mojom {
class StringOrdinalDataView;
}

// WARNING, DO NOT USE! Use UniquePosition instead.
//
//
// A StringOrdinal is an object that can be used for ordering. The
// StringOrdinal class has an unbounded dense strict total order, which
// mean for any StringOrdinals a, b and c:
//
//  - a < b and b < c implies a < c (transitivity);
//  - exactly one of a < b, b < a and a = b holds (trichotomy);
//  - if a < b, there is a StringOrdinal x such that a < x < b (density);
//  - there are Ordinals<T> x and y such that x < a < y (unboundedness).
//
// This means that when StringOrdinal is used for sorting a list, if any
// item changes its position in the list, only its StringOrdinal value
// has to change to represent the new order, and all the other values
// can stay the same.
//
// A StringOrdinal is internally represented as an array of bytes, so it
// can be serialized to and deserialized from disk.
//
// A StringOrdinal is valid iff its corresponding string has at least
// kMinLength characters, does not contain any characters less than
// kZeroDigit or greater than kMaxDigit, is not all zero digits, and
// does not have any unnecessary trailing zero digits.
//
// Since StringOrdinals contain only printable characters, it is safe
// to store as a string in a protobuf.
class StringOrdinal {
 public:
  // Functors for use with STL algorithms and containers.
  class LessThanFn {
   public:
    LessThanFn();

    bool operator()(const StringOrdinal& lhs, const StringOrdinal& rhs) const;
  };

  class EqualsFn {
   public:
    EqualsFn();

    bool operator()(const StringOrdinal& lhs, const StringOrdinal& rhs) const;
  };

  // Creates an StringOrdinal from the given string of bytes. The StringOrdinal
  // may be valid or invalid.
  explicit StringOrdinal(const std::string& bytes);

  // Creates an invalid StringOrdinal.
  StringOrdinal();

  // Creates a valid initial StringOrdinal. This is called to create the first
  // element of StringOrdinal list (i.e. before we have any other values we can
  // generate from).
  static StringOrdinal CreateInitialOrdinal();

  // Returns true iff this StringOrdinal is valid.  This takes constant
  // time.
  bool IsValid() const;

  // Returns true iff |*this| == |other| or |*this| and |other|
  // are both invalid.
  bool EqualsOrBothInvalid(const StringOrdinal& other) const;

  // Returns a printable string representation of the StringOrdinal suitable
  // for logging.
  std::string ToDebugString() const;

  // All remaining functions can only be called if IsValid() holds.
  // It is an error to call them if IsValid() is false.

  // Order-related functions.

  // Returns true iff |*this| < |other|.
  bool LessThan(const StringOrdinal& other) const;

  // Returns true iff |*this| > |other|.
  bool GreaterThan(const StringOrdinal& other) const;

  // Returns true iff |*this| == |other| (i.e. |*this| < |other| and
  // |other| < |*this| are both false).
  bool Equals(const StringOrdinal& other) const;

  // Given |*this| != |other|, returns a StringOrdinal x such that
  // min(|*this|, |other|) < x < max(|*this|, |other|). It is an error
  // to call this function when |*this| == |other|.
  StringOrdinal CreateBetween(const StringOrdinal& other) const;

  // Returns a StringOrdinal |x| such that |x| < |*this|.
  StringOrdinal CreateBefore() const;

  // Returns a StringOrdinal |x| such that |*this| < |x|.
  StringOrdinal CreateAfter() const;

  // Returns the string of bytes representing the StringOrdinal.  It is
  // guaranteed that an StringOrdinal constructed from the returned string
  // will be valid.
  std::string ToInternalValue() const;

  // Use of copy constructor and default assignment for this class is allowed.

  // Constants for StringOrdinal digits.
  static const uint8_t kZeroDigit = 'a';
  static const uint8_t kMaxDigit = 'z';
  static const size_t kMinLength = 1;
  static const uint8_t kOneDigit = kZeroDigit + 1;
  static const uint8_t kMidDigit = kOneDigit + (kMaxDigit - kOneDigit) / 2;
  static const unsigned int kMidDigitValue = kMidDigit - kZeroDigit;
  static const unsigned int kMaxDigitValue = kMaxDigit - kZeroDigit;
  static const unsigned int kRadix = kMaxDigitValue + 1;

  static_assert(kOneDigit > kZeroDigit, "incorrect StringOrdinal one digit");
  static_assert(kMidDigit > kOneDigit, "incorrect StringOrdinal mid digit");
  static_assert(kMaxDigit > kMidDigit, "incorrect StringOrdinal max digit");
  static_assert(kMinLength > 0, "incorrect StringOrdinal min length");
  static_assert(kMidDigitValue > 1, "incorrect StringOrdinal mid digit");
  static_assert(kMaxDigitValue > kMidDigitValue,
                "incorrect StringOrdinal max digit");
  static_assert(kRadix == kMaxDigitValue + 1, "incorrect StringOrdinal radix");

 private:
  friend struct mojo::StructTraits<syncer::mojom::StringOrdinalDataView,
                                   StringOrdinal>;

  // Returns true iff the given byte string satisfies the criteria for
  // a valid StringOrdinal.
  static bool IsValidOrdinalBytes(const std::string& bytes);

  // Returns the length that bytes.substr(0, length) would be with
  // trailing zero digits removed.
  static size_t GetLengthWithoutTrailingZeroDigits(const std::string& bytes,
                                                   size_t length);

  // Returns the digit at position i, padding with zero digits if
  // required.
  static uint8_t GetDigit(const std::string& bytes, size_t i);

  // Returns the digit value at position i, padding with 0 if required.
  static int GetDigitValue(const std::string& bytes, size_t i);

  // Adds the given value to |bytes| at position i, carrying when
  // necessary.  Returns the left-most carry.
  static int AddDigitValue(std::string* bytes, size_t i, int digit_value);

  // Returns the proper length |bytes| should be resized to, i.e. the
  // smallest length such that |bytes| is still greater than
  // |lower_bound| and is still valid.  |bytes| should be greater than
  // |lower_bound|.
  static size_t GetProperLength(const std::string& lower_bound,
                                const std::string& bytes);

  // Compute the midpoint StringOrdinal byte string that is between |start|
  // and |end|.
  static std::string ComputeMidpoint(const std::string& start,
                                     const std::string& end);

  // Create a StringOrdinal that is lexigraphically greater than |start| and
  // lexigraphically less than |end|. The returned StringOrdinal will be roughly
  // between |start| and |end|.
  static StringOrdinal CreateOrdinalBetween(const StringOrdinal& start,
                                            const StringOrdinal& end);

  // The internal byte string representation of the StringOrdinal.  Never
  // changes after construction except for assignment.
  std::string bytes_;

  // A cache of the result of IsValidOrdinalBytes(bytes_).
  bool is_valid_;
};

bool operator==(const StringOrdinal& lhs, const StringOrdinal& rhs);

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

static_assert(StringOrdinal::kZeroDigit == 'a',
              "StringOrdinal has incorrect zero digit");
static_assert(StringOrdinal::kOneDigit == 'b',
              "StringOrdinal has incorrect one digit");
static_assert(StringOrdinal::kMidDigit == 'n',
              "StringOrdinal has incorrect mid digit");
static_assert(StringOrdinal::kMaxDigit == 'z',
              "StringOrdinal has incorrect max digit");
static_assert(StringOrdinal::kMidDigitValue == 13,
              "StringOrdinal has incorrect mid digit value");
static_assert(StringOrdinal::kMaxDigitValue == 25,
              "StringOrdinal has incorrect max digit value");
static_assert(StringOrdinal::kRadix == 26, "StringOrdinal has incorrect radix");

}  // namespace syncer

#endif  // COMPONENTS_SYNC_MODEL_STRING_ORDINAL_H_