// Copyright 2010-2021, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef MOZC_CONVERTER_CANDIDATE_H_
#define MOZC_CONVERTER_CANDIDATE_H_

#include <cstddef>
#include <cstdint>
#include <ostream>
#include <string>
#include <vector>

#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "base/number_util.h"

#ifndef NDEBUG
#define MOZC_CANDIDATE_DEBUG
#define MOZC_CANDIDATE_LOG(result, message) \
  (result)->Dlog(__FILE__, __LINE__, message)

#else  // NDEBUG
#define MOZC_CANDIDATE_LOG(result, message) \
  {                                         \
  }

#endif  // NDEBUG

namespace mozc {
namespace converter {

class Candidate {
 public:
  Candidate() = default;

  // LINT.IfChange
  enum Attribute {
    DEFAULT_ATTRIBUTE = 0,
    // this was the best candidate before learning
    BEST_CANDIDATE = 1 << 0,
    // this candidate was reranked by user
    RERANKED = 1 << 1,
    // don't save it in history
    NO_HISTORY_LEARNING = 1 << 2,
    // don't save it in suggestion
    NO_SUGGEST_LEARNING = 1 << 3,
    // NO_HISTORY_LEARNING | NO_SUGGEST_LEARNING
    NO_LEARNING = (1 << 2 | 1 << 3),
    // learn it with left/right context
    CONTEXT_SENSITIVE = 1 << 4,
    // has "did you mean"
    SPELLING_CORRECTION = 1 << 5,
    // No need to have full/half width expansion
    NO_VARIANTS_EXPANSION = 1 << 6,
    // No need to have extra descriptions
    NO_EXTRA_DESCRIPTION = 1 << 7,
    // was generated by real-time conversion
    REALTIME_CONVERSION = 1 << 8,
    // contains tokens in user dictionary.
    USER_DICTIONARY = 1 << 9,
    // command candidate. e.g., incognito mode.
    COMMAND_CANDIDATE = 1 << 10,
    // key characters are consumed partially.
    // Consumed size is |consumed_key_size|.
    // If not set, all the key characters are consumed.
    PARTIALLY_KEY_CONSUMED = 1 << 11,
    // Typing correction candidate.
    // - Special description should be shown when the candidate is created
    //   by a dictionary predictor.
    // - No description should be shown when the candidate is loaded from
    //   history.
    // - Otherwise following unexpected behavior can be observed.
    //   1. Type "やんしょん" and submit "マンション" (annotated with "補正").
    //   2. Type "まんしょん".
    //   3. "マンション" (annotated with "補正") is shown as a candidate
    //      regardless of a user's correct typing.
    TYPING_CORRECTION = 1 << 12,
    // Auto partial suggestion candidate.
    // - Special description should be shown when the candidate is created
    //   by a dictionary predictor.
    // - No description should be shown when the candidate is loaded from
    //   history.
    AUTO_PARTIAL_SUGGESTION = 1 << 13,
    // Predicted from user prediction history.
    USER_HISTORY_PREDICTION = 1 << 14,
    // Contains suffix dictionary.
    SUFFIX_DICTIONARY = 1 << 15,
    // Disables modification and removal in rewriters.
    NO_MODIFICATION = 1 << 16,
    // Candidate which is reranked by user segment history rewriter.
    USER_SEGMENT_HISTORY_REWRITER = 1 << 17,
    // Keys are expanded in the dictionary lookup. Usually
    // Kana-modifiers are expanded.
    KEY_EXPANDED_IN_DICTIONARY = 1 << 18,
  };
  // LINT.ThenChange(//converter/converter_main.cc)

  enum Command {
    DEFAULT_COMMAND = 0,
    ENABLE_INCOGNITO_MODE,      // enables "incognito mode".
    DISABLE_INCOGNITO_MODE,     // disables "incognito mode".
    ENABLE_PRESENTATION_MODE,   // enables "presentation mode".
    DISABLE_PRESENTATION_MODE,  // disables "presentation mode".
  };

  // Bit field indicating candidate source info.
  // This should be used for usage stats.
  // TODO(mozc-team): Move Attribute fields for source info
  // to SourceInfo.
  enum SourceInfo {
    SOURCE_INFO_NONE = 0,
    // Attributes for zero query suggestion.
    // These are used for usage stats.
    // For DICTIONARY_PREDICTOR_ZERO_QUERY_XX, XX stands for the
    // types defined at zero_query_list.h.
    DICTIONARY_PREDICTOR_ZERO_QUERY_NONE = 1 << 0,
    DICTIONARY_PREDICTOR_ZERO_QUERY_NUMBER_SUFFIX = 1 << 1,
    DICTIONARY_PREDICTOR_ZERO_QUERY_EMOTICON = 1 << 2,
    DICTIONARY_PREDICTOR_ZERO_QUERY_EMOJI = 1 << 3,
    DICTIONARY_PREDICTOR_ZERO_QUERY_BIGRAM = 1 << 4,
    DICTIONARY_PREDICTOR_ZERO_QUERY_SUFFIX = 1 << 5,
    DICTIONARY_PREDICTOR_ZERO_QUERY_SUPPLEMENTAL_MODEL = 1 << 7,

    USER_HISTORY_PREDICTOR = 1 << 6,
  };

  enum Category {
    DEFAULT_CATEGORY,  // Realtime conversion, history prediction, etc
    SYMBOL,            // Symbol, emoji
    OTHER,             // Misc candidate
  };

  // LINT.IfChange
  std::string key;    // reading
  std::string value;  // surface form
  std::string content_key;
  std::string content_value;

  size_t consumed_key_size = 0;

  // Meta information
  std::string prefix;
  std::string suffix;
  // Description including description type and message
  std::string description;
  // Description for A11y support (e.g. "あ。ヒラガナ あ")
  std::string a11y_description;

  // Usage ID
  int32_t usage_id = 0;
  // Title of the usage containing basic form of this candidate.
  std::string usage_title;
  // Content of the usage.
  std::string usage_description;

  // Context "sensitive" candidate cost.
  // Taking adjacent words/nodes into consideration.
  // Basically, candidate is sorted by this cost.
  int32_t cost = 0;
  // Context "free" candidate cost
  // NOT taking adjacent words/nodes into consideration.
  int32_t wcost = 0;
  // (cost without transition cost between left/right boundaries)
  // Cost of only transitions (cost without word cost adjacent context)
  int32_t structure_cost = 0;

  // lid of left-most node
  uint16_t lid = 0;
  // rid of right-most node
  uint16_t rid = 0;

  // Attributes of this candidate. Can set multiple attributes
  // defined in enum |Attribute|.
  uint32_t attributes = 0;

  // Candidate's source info which will be used for usage stats.
  uint32_t source_info = SOURCE_INFO_NONE;

  Category category = DEFAULT_CATEGORY;

  // Candidate style. This is not a bit-field.
  // The style is defined in enum |Style|.
  NumberUtil::NumberString::Style style =
      NumberUtil::NumberString::DEFAULT_STYLE;

  // Command of this candidate. This is not a bit-field.
  // The style is defined in enum |Command|.
  Command command = DEFAULT_COMMAND;

  // Boundary information for real time conversion.  This will be set only for
  // real time conversion result candidates.  Each element is the encoded
  // lengths of key, value, content key and content value.
  std::vector<uint32_t> inner_segment_boundary;
  // LINT.ThenChange(//converter/segments_matchers.h)

  // The original cost before rescoring. Used for debugging purpose.
  int32_t cost_before_rescoring = 0;
#ifdef MOZC_CANDIDATE_DEBUG
  void Dlog(absl::string_view filename, int line,
            absl::string_view message) const;
  mutable std::string log;
#endif  // MOZC_CANDIDATE_DEBUG

  static bool EncodeLengths(size_t key_len, size_t value_len,
                            size_t content_key_len, size_t content_value_len,
                            uint32_t *result);

  // This function ignores error, so be careful when using this.
  static uint32_t EncodeLengths(size_t key_len, size_t value_len,
                                size_t content_key_len,
                                size_t content_value_len) {
    uint32_t result;
    EncodeLengths(key_len, value_len, content_key_len, content_value_len,
                  &result);
    return result;
  }

  // Inserts a new element to |inner_segment_boundary|.  If one of four
  // lengths is longer than 255, this method returns false.
  bool PushBackInnerSegmentBoundary(size_t key_len, size_t value_len,
                                    size_t content_key_len,
                                    size_t content_value_len);

  // Iterates inner segments.  Usage example:
  // for (InnerSegmentIterator iter(&cand); !iter.Done(); iter.Next()) {
  //   absl::string_view s = iter.GetContentKey();
  //   ...
  // }
  class InnerSegmentIterator final {
   public:
    explicit InnerSegmentIterator(const Candidate *candidate)
        : inner_segment_boundary_(candidate->inner_segment_boundary),
          key_offset_(candidate->key.data()),
          value_offset_(candidate->value.data()),
          index_(0) {}

    InnerSegmentIterator(absl::Span<const uint32_t> inner_segment_boundary,
                         absl::string_view key, absl::string_view value)
        : inner_segment_boundary_(inner_segment_boundary),
          key_offset_(key.data()),
          value_offset_(value.data()),
          index_(0) {}

    bool Done() const { return index_ == inner_segment_boundary_.size(); }

    void Next();
    absl::string_view GetKey() const;
    absl::string_view GetValue() const;
    absl::string_view GetContentKey() const;
    absl::string_view GetContentValue() const;
    absl::string_view GetFunctionalKey() const;
    absl::string_view GetFunctionalValue() const;
    size_t GetIndex() const { return index_; }

   private:
    const absl::Span<const uint32_t> inner_segment_boundary_;
    const char *key_offset_ = nullptr;
    const char *value_offset_ = nullptr;
    size_t index_ = 0;
  };

  // Clears the Candidate with default values. Note that the default
  // constructor already does the same so you don't need to call Clear
  // explicitly.
  void Clear();

  // Returns functional key.
  // functional_key =
  // key.substr(content_key.size(), key.size() - content_key.size());
  absl::string_view functional_key() const;

  // Returns functional value.
  // functional_value =
  // value.substr(content_value.size(), value.size() - content_value.size());
  absl::string_view functional_value() const;

  // Returns whether the inner_segment_boundary member is consistent with
  // key and value.
  // Note: content_key and content_value are not checked here.
  // We cannot compose candidate's content_key and content_value directly
  // from the inner segments in the current implementation.
  // Example:
  // value: 車のほうがあとだ
  // content_value: 車のほうがあとだ
  // inner_segments:
  // <くるまのほうが, 車のほうが, くるま, 車>
  // <あとだ, あとだ, あとだ, あとだ>
  bool IsValid() const;
  std::string DebugString() const;

  friend std::ostream &operator<<(std::ostream &os,
                                  const Candidate &candidate) {
    return os << candidate.DebugString();
  }
};

// Inlining basic accessors here.
inline absl::string_view Candidate::functional_key() const {
  return key.size() <= content_key.size()
             ? absl::string_view()
             : absl::string_view(key.data() + content_key.size(),
                                 key.size() - content_key.size());
}

inline absl::string_view Candidate::functional_value() const {
  return value.size() <= content_value.size()
             ? absl::string_view()
             : absl::string_view(value.data() + content_value.size(),
                                 value.size() - content_value.size());
}

}  // namespace converter
}  // namespace mozc

#endif  // MOZC_CONVERTER_CANDIDATE_H_
