// 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.

syntax = "proto2";

package mozc.user_dictionary;

option java_outer_classname = "ProtoUserDictionaryStorage";
option java_package = "org.mozc.android.inputmethod.japanese.protobuf";

message UserDictionary {
  enum PosType {
    NOUN = 1;                            // "名詞"
    ABBREVIATION = 2;                    // "短縮よみ"
    SUGGESTION_ONLY = 3;                 // "サジェストのみ"
    PROPER_NOUN = 4;                     // "固有名詞"
    PERSONAL_NAME = 5;                   // "人名"
    FAMILY_NAME = 6;                     // "姓"
    FIRST_NAME = 7;                      // "名"
    ORGANIZATION_NAME = 8;               // "組織"
    PLACE_NAME = 9;                      // "地名"
    SA_IRREGULAR_CONJUGATION_NOUN = 10;  // "名詞サ変"
    ADJECTIVE_VERBAL_NOUN = 11;          // "名詞形動"
    NUMBER = 12;                         // "数"
    ALPHABET = 13;                       // "アルファベット"
    SYMBOL = 14;                         // "記号"
    EMOTICON = 15;                       // "顔文字"

    ADVERB = 16;              // "副詞"
    PRENOUN_ADJECTIVAL = 17;  // "連体詞"
    CONJUNCTION = 18;         // "接続詞"
    INTERJECTION = 19;        // "感動詞"

    PREFIX = 20;              // "接頭語"
    COUNTER_SUFFIX = 21;      // "助数詞"
    GENERIC_SUFFIX = 22;      // "接尾一般"
    PERSON_NAME_SUFFIX = 23;  // "接尾人名"
    PLACE_NAME_SUFFIX = 24;   // "接尾地名"

    WA_GROUP1_VERB = 25;    // "動詞ワ行五段"
    KA_GROUP1_VERB = 26;    // "動詞カ行五段"
    SA_GROUP1_VERB = 27;    // "動詞サ行五段"
    TA_GROUP1_VERB = 28;    // "動詞タ行五段"
    NA_GROUP1_VERB = 29;    // "動詞ナ行五段"
    MA_GROUP1_VERB = 30;    // "動詞マ行五段"
    RA_GROUP1_VERB = 31;    // "動詞ラ行五段"
    GA_GROUP1_VERB = 32;    // "動詞ガ行五段"
    BA_GROUP1_VERB = 33;    // "動詞バ行五段"
    HA_GROUP1_VERB = 34;    // "動詞ハ行四段"
    GROUP2_VERB = 35;       // "動詞一段"
    KURU_GROUP3_VERB = 36;  // "動詞カ変"
    SURU_GROUP3_VERB = 37;  // "動詞サ変"
    ZURU_GROUP3_VERB = 38;  // "動詞ザ変"
    RU_GROUP3_VERB = 39;    // "動詞ラ変"

    ADJECTIVE = 40;                 // "形容詞"
    SENTENCE_ENDING_PARTICLE = 41;  // "終助詞"
    PUNCTUATION = 42;               // "句読点"
    FREE_STANDING_WORD = 43;        // "独立語"

    SUPPRESSION_WORD = 44;  // "抑制単語"
  }

  // ID of this dictionary
  optional uint64 id = 1 [default = 0, jstype = JS_STRING];

  // set false if this dictionary is not used.
  // Even if |enabled| is false, the dictionary
  // itself is visible to user.
  optional bool enabled = 2 [default = true];

  // name of dictionary
  optional string name = 3 [default = ""];

  // entry of each word
  message Entry {
    optional string key = 1 [default = ""];
    optional string value = 2 [default = ""];
    // Do NOT use tag number '3' in this proto. Please see below.
    reserved 3;  // Deprecated pos
    optional string comment = 4 [default = ""];

    // Historically we used to use tag number '3' for POS in string format.
    // In order to switch it to enum based POS, we removed (deprecated) the
    // tag number '3' field and created another field numbered '5'.
    optional PosType pos = 5;

    // In multilingual settings, Gboard's personal dictionary will feed entries
    // with locale information. We can change the behavior (filter or demote)
    // The locale is usually formatted in BCP47.
    // In TSV format, the locale is encoded in the POS field as `pos:locale`
    // "名詞:ja".
    optional string locale = 12 [default = ""];

    // set true if this entry is removed.
    // This flag is used for cloud sync feature.
    // Cloud sync feature is already deprecated and this flag is only
    // used to convert sync dictionary to normal dictionary.
    optional bool removed = 10 [default = false];

    // set true if this entry is automatically registered
    // by converter.
    optional bool auto_registered = 11 [default = false];
  }

  repeated Entry entries = 4;

  // set true if this dictionary is removed.
  // This flag is used for cloud sync feature.
  // Cloud sync feature is already deprecated and this flag is only
  // used to convert sync dictionary to normal dictionary.
  optional bool removed = 5 [default = false];

  // This flag is used for cloud sync feature.
  // Cloud sync feature is already deprecated and this flag is only
  // used to convert sync dictionary to normal dictionary.
  optional bool syncable = 6 [default = false];
}

message UserDictionaryStorage {
  // version of user dictionary
  optional int32 version = 1 [default = 0];

  // dictionary body
  repeated UserDictionary dictionaries = 2;

  enum StorageType {
    SNAPSHOT = 1;  // This storage is a snapshot.
    UPDATE = 2;    // This storage is a diff of some snapshots.
  }

  optional StorageType storage_type = 10 [default = SNAPSHOT];
}

message UserDictionaryCommand {
  enum CommandType {
    // Does nothing.
    NO_OPERATION = 0;

    // Unlink the user dictionary file if necessary.
    // We can do this operation without creating a session.
    // This operation is introduced as a last resort to clean up
    // user dictionary, so it *forces* to unlink the file regardless
    // of the current user dictionary session status.
    CLEAR_STORAGE = 1;

    // Creates a new session, and returns its id via
    // UserDictionaryCommandStatus::session_id.
    CREATE_SESSION = 2;

    // Deletes the session identified by session_id.
    DELETE_SESSION = 3;

    // Sets the default dictionary name. It will be used when
    // ensure_non_empty_storage is enabled and the operation supporting the
    // flag is invoked.
    SET_DEFAULT_DICTIONARY_NAME = 4;

    // Checks if the session is currently undoable or not.
    CHECK_UNDOABILITY = 5;

    // Undoes the last operation.
    UNDO = 6;

    // Loads from local storage.
    LOAD = 7;

    // Saves to local storage.
    SAVE = 8;

    // Returns a list of name and dictionary-id pairs.
    // They are filled in the form of UserDictionary without any entries
    // in UserDictionaryCommandStatus::storage.
    GET_USER_DICTIONARY_NAME_LIST = 9;

    // Returns the number of entries in the dictionary with the
    // given dictionary_id.
    GET_ENTRY_SIZE = 10;

    // Use GET_ENTRIES instead.
    reserved 11;
    reserved "OBSOLETE_GET_ENTRY";

    // Returns if it is possible to add new dictionary or not.
    // The result is returned by using status code.
    CHECK_NEW_DICTIONARY_AVAILABILITY = 12;

    // Creates a new dictionary with the dictionary_name.
    CREATE_DICTIONARY = 13;

    // Deletes the dictionary with the given dictionary_id.
    DELETE_DICTIONARY = 14;

    // Renames the dictionary with the given dictionary_id to dictionary_name.
    RENAME_DICTIONARY = 15;

    // Returns if it is possible to add new entry to the dictionary
    // with the given dictionary_id or not.
    CHECK_NEW_ENTRY_AVAILABILITY = 16;

    // Adds an entry to the dictionary with the given dictionary_id.
    // Added entry should be located at the end of the dictionary, and
    // the data should be passed via entry field.
    ADD_ENTRY = 17;

    // Edits an entry in the dictionary with the given dictionary_id.
    // The new data should be passed via entry.
    // The edit target should be specified via entry_index(0).
    EDIT_ENTRY = 18;

    // Deletes entries in the dictionary with the given dictionary_id.
    // The target entries should be specified based on index in entry_index.
    DELETE_ENTRY = 19;

    // Imports entries from the given data into a dictionary.
    // There are two ways to specify the dictionary:
    // 1) set dictionary_id for the dictionary
    // 2) set dictionary_name to create a new dictionary with the name.
    IMPORT_DATA = 20;

    // Gets the entire UserDictionaryStorage data.
    // Note: The result of this command could be too large for IPC, which has a
    // size limitation of the response data.
    GET_STORAGE = 21;

    // Returns entries in the dictionary specified by dictionary_id.
    // The position of the entry should be specified via entry_index().
    GET_ENTRIES = 22;
  }

  required CommandType type = 1;
  optional uint64 session_id = 2 [jstype = JS_STRING];
  optional uint64 dictionary_id = 3 [jstype = JS_STRING];
  optional string dictionary_name = 4;
  repeated int32 entry_index = 5;
  optional UserDictionary.Entry entry = 6;
  optional string data = 7;
  optional bool ensure_non_empty_storage = 8;
  optional bool ignore_invalid_entries = 9;
}

message UserDictionaryCommandStatus {
  // Note: this status code is now temporary assgined.
  // It may be updated (incl. re-numbering) to organize the code.
  // I.e., the code shouldn't be saved in serialized format for now.
  // TODO(hidehiko): Re-organize and re-number the enum values,
  //   after we check all necessary codes in.
  enum Status {
    // Note: SUCCEEDED is conflicting Windows MACRO.
    USER_DICTIONARY_COMMAND_SUCCESS = 1;
    UNKNOWN_ERROR = 2;

    UNKNOWN_COMMAND = 3;
    INVALID_ARGUMENT = 4;

    UNKNOWN_SESSION_ID = 5;

    FILE_NOT_FOUND = 6;
    INVALID_FILE_FORMAT = 7;

    // Note: currently if we receive this error status,
    // the file is actually saved.
    FILE_SIZE_LIMIT_EXCEEDED = 8;
    DICTIONARY_SIZE_LIMIT_EXCEEDED = 9;
    ENTRY_SIZE_LIMIT_EXCEEDED = 10;

    UNKNOWN_DICTIONARY_ID = 11;
    ENTRY_INDEX_OUT_OF_RANGE = 12;

    // Errors for dictionary names.
    DICTIONARY_NAME_EMPTY = 13;
    DICTIONARY_NAME_TOO_LONG = 14;
    DICTIONARY_NAME_CONTAINS_INVALID_CHARACTER = 15;
    DICTIONARY_NAME_DUPLICATED = 16;

    // Errors for entry data.
    READING_EMPTY = 17;
    READING_TOO_LONG = 18;
    READING_CONTAINS_INVALID_CHARACTER = 19;
    WORD_EMPTY = 20;
    WORD_TOO_LONG = 21;
    WORD_CONTAINS_INVALID_CHARACTER = 22;
    INVALID_POS_TYPE = 23;
    COMMENT_TOO_LONG = 24;
    COMMENT_CONTAINS_INVALID_CHARACTER = 25;

    // Errors for importing.
    IMPORT_TOO_MANY_WORDS = 26;
    IMPORT_INVALID_ENTRIES = 27;

    NO_UNDO_HISTORY = 28;
  }

  required Status status = 1;
  optional uint64 session_id = 2 [jstype = JS_STRING];
  optional UserDictionaryStorage storage = 3;
  // Use entries field instead.
  reserved 4;  // Deprecated entry
  optional uint64 dictionary_id = 5 [jstype = JS_STRING];
  optional uint32 entry_size = 6;
  repeated UserDictionary.Entry entries = 7;
}
