// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
syntax = "proto3";

package BackendProto;

// Generic containers
///////////////////////////////////////////////////////////

message Empty {}

message OptionalInt32 {
  sint32 val = 1;
}

message OptionalUInt32 {
  uint32 val = 1;
}

message Int32 {
  sint32 val = 1;
}

message UInt32 {
  uint32 val = 1;
}

message Int64 {
  int64 val = 1;
}

message String {
  string val = 1;
}

message Json {
  bytes json = 1;
}

message Bool {
  bool val = 1;
}

message StringList {
  repeated string vals = 1;
}

message OpChangesWithCount {
  uint32 count = 1;
  OpChanges changes = 2;
}

// IDs used in RPC calls
///////////////////////////////////////////////////////////

message NoteTypeID {
  int64 ntid = 1;
}

message NoteID {
  int64 nid = 1;
}

message CardID {
  int64 cid = 1;
}

message CardIDs {
  repeated int64 cids = 1;
}

message DeckID {
  int64 did = 1;
}

message DeckIDs {
  repeated int64 dids = 1;
}

message DeckConfigID {
  int64 dcid = 1;
}

// Backend methods
///////////////////////////////////////////////////////////

/// while the protobuf descriptors expose the order services are defined in,
/// that information is not available in prost, so we define an enum to make
/// sure all clients agree on the service index
enum ServiceIndex {
  SERVICE_INDEX_SCHEDULING = 0;
  SERVICE_INDEX_DECKS = 1;
  SERVICE_INDEX_NOTES = 2;
  SERVICE_INDEX_SYNC = 3;
  SERVICE_INDEX_NOTE_TYPES = 4;
  SERVICE_INDEX_CONFIG = 5;
  SERVICE_INDEX_CARD_RENDERING = 6;
  SERVICE_INDEX_DECK_CONFIG = 7;
  SERVICE_INDEX_TAGS = 8;
  SERVICE_INDEX_SEARCH = 9;
  SERVICE_INDEX_STATS = 10;
  SERVICE_INDEX_MEDIA = 11;
  SERVICE_INDEX_I18N = 12;
  SERVICE_INDEX_COLLECTION = 13;
  SERVICE_INDEX_CARDS = 14;
}

service SchedulingService {
  rpc SchedTimingToday(Empty) returns (SchedTimingTodayOut);
  rpc StudiedToday(Empty) returns (String);
  rpc StudiedTodayMessage(StudiedTodayMessageIn) returns (String);
  rpc UpdateStats(UpdateStatsIn) returns (Empty);
  rpc ExtendLimits(ExtendLimitsIn) returns (Empty);
  rpc CountsForDeckToday(DeckID) returns (CountsForDeckTodayOut);
  rpc CongratsInfo(Empty) returns (CongratsInfoOut);
  rpc RestoreBuriedAndSuspendedCards(CardIDs) returns (OpChanges);
  rpc UnburyCardsInCurrentDeck(UnburyCardsInCurrentDeckIn) returns (Empty);
  rpc BuryOrSuspendCards(BuryOrSuspendCardsIn) returns (OpChanges);
  rpc EmptyFilteredDeck(DeckID) returns (Empty);
  rpc RebuildFilteredDeck(DeckID) returns (UInt32);
  rpc ScheduleCardsAsNew(ScheduleCardsAsNewIn) returns (OpChanges);
  rpc SetDueDate(SetDueDateIn) returns (OpChanges);
  rpc SortCards(SortCardsIn) returns (OpChangesWithCount);
  rpc SortDeck(SortDeckIn) returns (OpChangesWithCount);
  rpc GetNextCardStates(CardID) returns (NextCardStates);
  rpc DescribeNextStates(NextCardStates) returns (StringList);
  rpc StateIsLeech(SchedulingState) returns (Bool);
  rpc AnswerCard(AnswerCardIn) returns (OpChanges);
  rpc UpgradeScheduler(Empty) returns (Empty);
  rpc GetQueuedCards(GetQueuedCardsIn) returns (GetQueuedCardsOut);
}

service DecksService {
  rpc AddOrUpdateDeckLegacy(AddOrUpdateDeckLegacyIn) returns (DeckID);
  rpc DeckTree(DeckTreeIn) returns (DeckTreeNode);
  rpc DeckTreeLegacy(Empty) returns (Json);
  rpc GetAllDecksLegacy(Empty) returns (Json);
  rpc GetDeckIDByName(String) returns (DeckID);
  rpc GetDeckLegacy(DeckID) returns (Json);
  rpc GetDeckNames(GetDeckNamesIn) returns (DeckNames);
  rpc NewDeckLegacy(Bool) returns (Json);
  rpc RemoveDecks(DeckIDs) returns (OpChangesWithCount);
  rpc DragDropDecks(DragDropDecksIn) returns (OpChanges);
  rpc RenameDeck(RenameDeckIn) returns (OpChanges);
}

service NotesService {
  rpc NewNote(NoteTypeID) returns (Note);
  rpc AddNote(AddNoteIn) returns (AddNoteOut);
  rpc DefaultsForAdding(DefaultsForAddingIn) returns (DeckAndNotetype);
  rpc DefaultDeckForNotetype(NoteTypeID) returns (DeckID);
  rpc UpdateNote(UpdateNoteIn) returns (OpChanges);
  rpc GetNote(NoteID) returns (Note);
  rpc RemoveNotes(RemoveNotesIn) returns (OpChanges);
  rpc ClozeNumbersInNote(Note) returns (ClozeNumbersInNoteOut);
  rpc AfterNoteUpdates(AfterNoteUpdatesIn) returns (OpChanges);
  rpc FieldNamesForNotes(FieldNamesForNotesIn) returns (FieldNamesForNotesOut);
  rpc NoteIsDuplicateOrEmpty(Note) returns (NoteIsDuplicateOrEmptyOut);
  rpc CardsOfNote(NoteID) returns (CardIDs);
}

service SyncService {
  rpc SyncMedia(SyncAuth) returns (Empty);
  rpc AbortSync(Empty) returns (Empty);
  rpc AbortMediaSync(Empty) returns (Empty);
  rpc BeforeUpload(Empty) returns (Empty);
  rpc SyncLogin(SyncLoginIn) returns (SyncAuth);
  rpc SyncStatus(SyncAuth) returns (SyncStatusOut);
  rpc SyncCollection(SyncAuth) returns (SyncCollectionOut);
  rpc FullUpload(SyncAuth) returns (Empty);
  rpc FullDownload(SyncAuth) returns (Empty);
  rpc SyncServerMethod(SyncServerMethodIn) returns (Json);
}

service ConfigService {
  rpc GetConfigJson(String) returns (Json);
  rpc SetConfigJson(SetConfigJsonIn) returns (Empty);
  rpc RemoveConfig(String) returns (Empty);
  rpc GetAllConfig(Empty) returns (Json);
  rpc GetConfigBool(Config.Bool) returns (Bool);
  rpc SetConfigBool(SetConfigBoolIn) returns (Empty);
  rpc GetConfigString(Config.String) returns (String);
  rpc SetConfigString(SetConfigStringIn) returns (Empty);
  rpc GetPreferences(Empty) returns (Preferences);
  rpc SetPreferences(Preferences) returns (OpChanges);
}

service NoteTypesService {
  rpc AddOrUpdateNotetype(AddOrUpdateNotetypeIn) returns (NoteTypeID);
  rpc GetStockNotetypeLegacy(StockNoteType) returns (Json);
  rpc GetNotetypeLegacy(NoteTypeID) returns (Json);
  rpc GetNotetypeNames(Empty) returns (NoteTypeNames);
  rpc GetNotetypeNamesAndCounts(Empty) returns (NoteTypeUseCounts);
  rpc GetNotetypeIDByName(String) returns (NoteTypeID);
  rpc RemoveNotetype(NoteTypeID) returns (Empty);
}

service CardRenderingService {
  rpc ExtractAVTags(ExtractAVTagsIn) returns (ExtractAVTagsOut);
  rpc ExtractLatex(ExtractLatexIn) returns (ExtractLatexOut);
  rpc GetEmptyCards(Empty) returns (EmptyCardsReport);
  rpc RenderExistingCard(RenderExistingCardIn) returns (RenderCardOut);
  rpc RenderUncommittedCard(RenderUncommittedCardIn) returns (RenderCardOut);
  rpc StripAVTags(String) returns (String);
  rpc RenderMarkdown(RenderMarkdownIn) returns (String);
}

service DeckConfigService {
  rpc AddOrUpdateDeckConfigLegacy(AddOrUpdateDeckConfigLegacyIn)
      returns (DeckConfigID);
  rpc AllDeckConfigLegacy(Empty) returns (Json);
  rpc GetDeckConfigLegacy(DeckConfigID) returns (Json);
  rpc NewDeckConfigLegacy(Empty) returns (Json);
  rpc RemoveDeckConfig(DeckConfigID) returns (Empty);
}

service TagsService {
  rpc ClearUnusedTags(Empty) returns (OpChangesWithCount);
  rpc AllTags(Empty) returns (StringList);
  rpc RemoveTags(String) returns (OpChangesWithCount);
  rpc SetTagExpanded(SetTagExpandedIn) returns (Empty);
  rpc TagTree(Empty) returns (TagTreeNode);
  rpc ReparentTags(ReparentTagsIn) returns (OpChangesWithCount);
  rpc RenameTags(RenameTagsIn) returns (OpChangesWithCount);
  rpc AddNoteTags(NoteIDsAndTagsIn) returns (OpChangesWithCount);
  rpc RemoveNoteTags(NoteIDsAndTagsIn) returns (OpChangesWithCount);
  rpc FindAndReplaceTag(FindAndReplaceTagIn) returns (OpChangesWithCount);
}

service SearchService {
  rpc BuildSearchString(SearchNode) returns (String);
  rpc SearchCards(SearchCardsIn) returns (SearchCardsOut);
  rpc SearchNotes(SearchNotesIn) returns (SearchNotesOut);
  rpc JoinSearchNodes(JoinSearchNodesIn) returns (String);
  rpc ReplaceSearchNode(ReplaceSearchNodeIn) returns (String);
  rpc FindAndReplace(FindAndReplaceIn) returns (OpChangesWithCount);
  rpc BrowserRowForCard(CardID) returns (BrowserRow);
}

service StatsService {
  rpc CardStats(CardID) returns (String);
  rpc Graphs(GraphsIn) returns (GraphsOut);
  rpc GetGraphPreferences(Empty) returns (GraphPreferences);
  rpc SetGraphPreferences(GraphPreferences) returns (Empty);
}

service MediaService {
  rpc CheckMedia(Empty) returns (CheckMediaOut);
  rpc TrashMediaFiles(TrashMediaFilesIn) returns (Empty);
  rpc AddMediaFile(AddMediaFileIn) returns (String);
  rpc EmptyTrash(Empty) returns (Empty);
  rpc RestoreTrash(Empty) returns (Empty);
}

service I18nService {
  rpc TranslateString(TranslateStringIn) returns (String);
  rpc FormatTimespan(FormatTimespanIn) returns (String);
  rpc I18nResources(Empty) returns (Json);
}

service CollectionService {
  rpc OpenCollection(OpenCollectionIn) returns (Empty);
  rpc CloseCollection(CloseCollectionIn) returns (Empty);
  rpc CheckDatabase(Empty) returns (CheckDatabaseOut);
  rpc GetUndoStatus(Empty) returns (UndoStatus);
  rpc Undo(Empty) returns (UndoStatus);
  rpc Redo(Empty) returns (UndoStatus);
  rpc LatestProgress(Empty) returns (Progress);
  rpc SetWantsAbort(Empty) returns (Empty);
}

service CardsService {
  rpc GetCard(CardID) returns (Card);
  rpc UpdateCard(UpdateCardIn) returns (OpChanges);
  rpc RemoveCards(RemoveCardsIn) returns (Empty);
  rpc SetDeck(SetDeckIn) returns (OpChanges);
  rpc SetFlag(SetFlagIn) returns (OpChanges);
}

// Protobuf stored in .anki2 files
// These should be moved to a separate file in the future
///////////////////////////////////////////////////////////

message DeckConfigInner {
  enum NewCardOrder {
    NEW_CARD_ORDER_DUE = 0;
    NEW_CARD_ORDER_RANDOM = 1;
  }
  enum ReviewCardOrder {
    REVIEW_CARD_ORDER_SHUFFLED_BY_DAY = 0;
    REVIEW_CARD_ORDER_SHUFFLED = 1;
    REVIEW_CARD_ORDER_INTERVALS_ASCENDING = 2;
    REVIEW_CARD_ORDER_INTERVALS_DESCENDING = 3;
  }
  enum ReviewMix {
    REVIEW_MIX_MIX_WITH_REVIEWS = 0;
    REVIEW_MIX_AFTER_REVIEWS = 1;
    REVIEW_MIX_BEFORE_REVIEWS = 2;
  }
  enum LeechAction {
    LEECH_ACTION_SUSPEND = 0;
    LEECH_ACTION_TAG_ONLY = 1;
  }

  repeated float learn_steps = 1;
  repeated float relearn_steps = 2;

  reserved 3 to 8;

  uint32 new_per_day = 9;
  uint32 reviews_per_day = 10;
  uint32 new_per_day_minimum = 29;

  float initial_ease = 11;
  float easy_multiplier = 12;
  float hard_multiplier = 13;
  float lapse_multiplier = 14;
  float interval_multiplier = 15;

  uint32 maximum_review_interval = 16;
  uint32 minimum_lapse_interval = 17;

  uint32 graduating_interval_good = 18;
  uint32 graduating_interval_easy = 19;

  NewCardOrder new_card_order = 20;
  ReviewCardOrder review_order = 32;

  ReviewMix new_mix = 30;
  ReviewMix interday_learning_mix = 31;

  LeechAction leech_action = 21;
  uint32 leech_threshold = 22;

  bool disable_autoplay = 23;
  uint32 cap_answer_time_to_secs = 24;
  uint32 visible_timer_secs = 25;
  bool skip_question_when_replaying_answer = 26;

  bool bury_new = 27;
  bool bury_reviews = 28;

  bytes other = 255;
}

message DeckCommon {
  bool study_collapsed = 1;
  bool browser_collapsed = 2;

  uint32 last_day_studied = 3;
  int32 new_studied = 4;
  int32 review_studied = 5;
  int32 milliseconds_studied = 7;

  // previously set in the v1 scheduler,
  // but not currently used for anything
  int32 learning_studied = 6;

  reserved 8 to 13;

  bytes other = 255;
}

message DeckKind {
  oneof kind {
    NormalDeck normal = 1;
    FilteredDeck filtered = 2;
  }
}

message NormalDeck {
  int64 config_id = 1;
  uint32 extend_new = 2;
  uint32 extend_review = 3;
  string description = 4;
  bool markdown_description = 5;

  reserved 6 to 11;
}

message FilteredDeck {
  bool reschedule = 1;
  repeated FilteredSearchTerm search_terms = 2;
  // v1 scheduler only
  repeated float delays = 3;
  // v2 scheduler only
  uint32 preview_delay = 4;
}

message FilteredSearchTerm {
  enum FilteredSearchOrder {
    FILTERED_SEARCH_ORDER_OLDEST_FIRST = 0;
    FILTERED_SEARCH_ORDER_RANDOM = 1;
    FILTERED_SEARCH_ORDER_INTERVALS_ASCENDING = 2;
    FILTERED_SEARCH_ORDER_INTERVALS_DESCENDING = 3;
    FILTERED_SEARCH_ORDER_LAPSES = 4;
    FILTERED_SEARCH_ORDER_ADDED = 5;
    FILTERED_SEARCH_ORDER_DUE = 6;
    FILTERED_SEARCH_ORDER_REVERSE_ADDED = 7;
    FILTERED_SEARCH_ORDER_DUE_PRIORITY = 8;
  }

  string search = 1;
  uint32 limit = 2;
  FilteredSearchOrder order = 3;
}

message NoteFieldConfig {
  bool sticky = 1;
  bool rtl = 2;
  string font_name = 3;
  uint32 font_size = 4;

  bytes other = 255;
}

message CardTemplateConfig {
  string q_format = 1;
  string a_format = 2;
  string q_format_browser = 3;
  string a_format_browser = 4;
  int64 target_deck_id = 5;
  string browser_font_name = 6;
  uint32 browser_font_size = 7;

  bytes other = 255;
}

message NoteTypeConfig {
  enum Kind {
    KIND_NORMAL = 0;
    KIND_CLOZE = 1;
  }
  Kind kind = 1;
  uint32 sort_field_idx = 2;
  string css = 3;
  int64 target_deck_id = 4;  // moved into config var
  string latex_pre = 5;
  string latex_post = 6;
  bool latex_svg = 7;
  repeated CardRequirement reqs = 8;

  bytes other = 255;
}

message CardRequirement {
  enum Kind {
    KIND_NONE = 0;
    KIND_ANY = 1;
    KIND_ALL = 2;
  }
  uint32 card_ord = 1;
  Kind kind = 2;
  repeated uint32 field_ords = 3;
}

// Containers for passing around database objects
///////////////////////////////////////////////////////////

message Deck {
  int64 id = 1;
  string name = 2;
  uint32 mtime_secs = 3;
  int32 usn = 4;
  DeckCommon common = 5;
  oneof kind {
    NormalDeck normal = 6;
    FilteredDeck filtered = 7;
  }
}

message NoteType {
  int64 id = 1;
  string name = 2;
  uint32 mtime_secs = 3;
  sint32 usn = 4;
  NoteTypeConfig config = 7;
  repeated NoteField fields = 8;
  repeated CardTemplate templates = 9;
}

message NoteField {
  OptionalUInt32 ord = 1;
  string name = 2;
  NoteFieldConfig config = 5;
}

message CardTemplate {
  OptionalUInt32 ord = 1;
  string name = 2;
  uint32 mtime_secs = 3;
  sint32 usn = 4;
  CardTemplateConfig config = 5;
}

message Note {
  int64 id = 1;
  string guid = 2;
  int64 notetype_id = 3;
  uint32 mtime_secs = 4;
  int32 usn = 5;
  repeated string tags = 6;
  repeated string fields = 7;
}

message Card {
  int64 id = 1;
  int64 note_id = 2;
  int64 deck_id = 3;
  uint32 template_idx = 4;
  int64 mtime_secs = 5;
  sint32 usn = 6;
  uint32 ctype = 7;
  sint32 queue = 8;
  sint32 due = 9;
  uint32 interval = 10;
  uint32 ease_factor = 11;
  uint32 reps = 12;
  uint32 lapses = 13;
  uint32 remaining_steps = 14;
  sint32 original_due = 15;
  int64 original_deck_id = 16;
  uint32 flags = 17;
  string data = 18;
}

// Backend
///////////////////////////////////////////////////////////

message BackendInit {
  repeated string preferred_langs = 1;
  string locale_folder_path = 2;
  bool server = 3;
}

message I18nBackendInit {
  repeated string preferred_langs = 4;
  string locale_folder_path = 5;
}

// Errors
///////////////////////////////////////////////////////////

message BackendError {
  // localized error description suitable for displaying to the user
  string localized = 1;
  // error specifics
  oneof value {
    Empty invalid_input = 2;
    Empty template_parse = 3;
    Empty io_error = 4;
    Empty db_error = 5;
    NetworkError network_error = 6;
    SyncError sync_error = 7;
    // user interrupted operation
    Empty interrupted = 8;
    string json_error = 9;
    string proto_error = 10;
    Empty not_found_error = 11;
    Empty exists = 12;
    Empty deck_is_filtered = 13;
  }
}

message NetworkError {
  enum NetworkErrorKind {
    OTHER = 0;
    OFFLINE = 1;
    TIMEOUT = 2;
    PROXY_AUTH = 3;
  }
  NetworkErrorKind kind = 1;
}

message SyncError {
  enum SyncErrorKind {
    OTHER = 0;
    CONFLICT = 1;
    SERVER_ERROR = 2;
    CLIENT_TOO_OLD = 3;
    AUTH_FAILED = 4;
    SERVER_MESSAGE = 5;
    MEDIA_CHECK_REQUIRED = 6;
    RESYNC_REQUIRED = 7;
    CLOCK_INCORRECT = 8;
    DATABASE_CHECK_REQUIRED = 9;
    SYNC_NOT_STARTED = 10;
  }
  SyncErrorKind kind = 1;
}

// Progress
///////////////////////////////////////////////////////////

message Progress {
  message MediaSync {
    string checked = 1;
    string added = 2;
    string removed = 3;
  }

  message FullSync {
    uint32 transferred = 1;
    uint32 total = 2;
  }

  message NormalSync {
    string stage = 1;
    string added = 2;
    string removed = 3;
  }

  message DatabaseCheck {
    string stage = 1;
    uint32 stage_total = 2;
    uint32 stage_current = 3;
  }
  oneof value {
    Empty none = 1;
    MediaSync media_sync = 2;
    string media_check = 3;
    FullSync full_sync = 4;
    NormalSync normal_sync = 5;
    DatabaseCheck database_check = 6;
  }
}

// Messages
///////////////////////////////////////////////////////////

message SchedTimingTodayOut {
  uint32 days_elapsed = 1;
  int64 next_day_at = 2;
}

message DeckTreeIn {
  // if non-zero, counts for the provided timestamp will be included
  int64 now = 1;
  int64 top_deck_id = 2;
}

message DeckTreeNode {
  int64 deck_id = 1;
  string name = 2;
  repeated DeckTreeNode children = 3;
  uint32 level = 4;
  bool collapsed = 5;

  uint32 review_count = 6;
  uint32 learn_count = 7;
  uint32 new_count = 8;

  bool filtered = 16;
}

message RenderExistingCardIn {
  int64 card_id = 1;
  bool browser = 2;
}

message RenderUncommittedCardIn {
  Note note = 1;
  uint32 card_ord = 2;
  bytes template = 3;
  bool fill_empty = 4;
}

message RenderCardOut {
  repeated RenderedTemplateNode question_nodes = 1;
  repeated RenderedTemplateNode answer_nodes = 2;
}

message RenderedTemplateNode {
  oneof value {
    string text = 1;
    RenderedTemplateReplacement replacement = 2;
  }
}

message RenderedTemplateReplacement {
  string field_name = 1;
  string current_text = 2;
  repeated string filters = 3;
}

message ExtractAVTagsIn {
  string text = 1;
  bool question_side = 2;
}

message ExtractAVTagsOut {
  string text = 1;
  repeated AVTag av_tags = 2;
}

message AVTag {
  oneof value {
    string sound_or_video = 1;
    TTSTag tts = 2;
  }
}

message TTSTag {
  string field_text = 1;
  string lang = 2;
  repeated string voices = 3;
  float speed = 4;
  repeated string other_args = 5;
}

message ExtractLatexIn {
  string text = 1;
  bool svg = 2;
  bool expand_clozes = 3;
}

message ExtractLatexOut {
  string text = 1;
  repeated ExtractedLatex latex = 2;
}

message ExtractedLatex {
  string filename = 1;
  string latex_body = 2;
}

message AddMediaFileIn {
  string desired_name = 1;
  bytes data = 2;
}

message CheckMediaOut {
  repeated string unused = 1;
  repeated string missing = 2;
  string report = 3;
  bool have_trash = 4;
}

message TrashMediaFilesIn {
  repeated string fnames = 1;
}

message TranslateStringIn {
  int32 key = 2;
  map<string, TranslateArgValue> args = 3;
}

message TranslateArgValue {
  oneof value {
    string str = 1;
    double number = 2;
  }
}

message FormatTimespanIn {
  enum Context {
    PRECISE = 0;
    ANSWER_BUTTONS = 1;
    INTERVALS = 2;
  }

  float seconds = 1;
  Context context = 2;
}

message StudiedTodayMessageIn {
  uint32 cards = 1;
  double seconds = 2;
}

message CongratsLearnMessageIn {
  float next_due = 1;
  uint32 remaining = 2;
}

message OpenCollectionIn {
  string collection_path = 1;
  string media_folder_path = 2;
  string media_db_path = 3;
  string log_path = 4;
}

message SearchCardsIn {
  string search = 1;
  SortOrder order = 2;
}

message SearchCardsOut {
  repeated int64 card_ids = 1;
}

message SortOrder {
  message Builtin {
    enum Kind {
      NOTE_CREATION = 0;
      NOTE_MOD = 1;
      NOTE_FIELD = 2;
      NOTE_TAGS = 3;
      NOTE_TYPE = 4;
      CARD_MOD = 5;
      CARD_REPS = 6;
      CARD_DUE = 7;
      CARD_EASE = 8;
      CARD_LAPSES = 9;
      CARD_INTERVAL = 10;
      CARD_DECK = 11;
      CARD_TEMPLATE = 12;
    }
    Kind kind = 1;
    bool reverse = 2;
  }
  oneof value {
    Empty from_config = 1;
    Empty none = 2;
    string custom = 3;
    Builtin builtin = 4;
  }
}

message SearchNotesIn {
  string search = 1;
}

message SearchNotesOut {
  repeated int64 note_ids = 2;
}

message SearchNode {
  message Dupe {
    int64 notetype_id = 1;
    string first_field = 2;
  }
  enum Flag {
    FLAG_NONE = 0;
    FLAG_ANY = 1;
    FLAG_RED = 2;
    FLAG_ORANGE = 3;
    FLAG_GREEN = 4;
    FLAG_BLUE = 5;
  }
  enum Rating {
    RATING_ANY = 0;
    RATING_AGAIN = 1;
    RATING_HARD = 2;
    RATING_GOOD = 3;
    RATING_EASY = 4;
    RATING_BY_RESCHEDULE = 5;
  }
  message Rated {
    uint32 days = 1;
    Rating rating = 2;
  }
  enum CardState {
    CARD_STATE_NEW = 0;
    CARD_STATE_LEARN = 1;
    CARD_STATE_REVIEW = 2;
    CARD_STATE_DUE = 3;
    CARD_STATE_SUSPENDED = 4;
    CARD_STATE_BURIED = 5;
  }
  message IdList {
    repeated int64 ids = 1;
  }
  message Group {
    enum Joiner {
      AND = 0;
      OR = 1;
    }
    repeated SearchNode nodes = 1;
    Joiner joiner = 2;
  }
  oneof filter {
    Group group = 1;
    SearchNode negated = 2;
    string parsable_text = 3;
    uint32 template = 4;
    int64 nid = 5;
    Dupe dupe = 6;
    string field_name = 7;
    Rated rated = 8;
    uint32 added_in_days = 9;
    int32 due_in_days = 10;
    Flag flag = 11;
    CardState card_state = 12;
    IdList nids = 13;
    uint32 edited_in_days = 14;
    string deck = 15;
    int32 due_on_day = 16;
    string tag = 17;
    string note = 18;
  }
}

message JoinSearchNodesIn {
  SearchNode.Group.Joiner joiner = 1;
  SearchNode existing_node = 2;
  SearchNode additional_node = 3;
}

message ReplaceSearchNodeIn {
  SearchNode existing_node = 1;
  SearchNode replacement_node = 2;
}

message CloseCollectionIn {
  bool downgrade_to_schema11 = 1;
}

message AddOrUpdateDeckConfigLegacyIn {
  bytes config = 1;
  bool preserve_usn_and_mtime = 2;
}

message SetTagExpandedIn {
  string name = 1;
  bool expanded = 2;
}

message GetChangedTagsOut {
  repeated string tags = 1;
}

message TagTreeNode {
  string name = 1;
  repeated TagTreeNode children = 2;
  uint32 level = 3;
  bool expanded = 4;
}

message ReparentTagsIn {
  repeated string tags = 1;
  string new_parent = 2;
}

message RenameTagsIn {
  string current_prefix = 1;
  string new_prefix = 2;
}

message SetConfigJsonIn {
  string key = 1;
  bytes value_json = 2;
}

message StockNoteType {
  enum Kind {
    BASIC = 0;
    BASIC_AND_REVERSED = 1;
    BASIC_OPTIONAL_REVERSED = 2;
    BASIC_TYPING = 3;
    CLOZE = 4;
  }

  Kind kind = 1;
}

message NoteTypeNames {
  repeated NoteTypeNameID entries = 1;
}

message NoteTypeUseCounts {
  repeated NoteTypeNameIDUseCount entries = 1;
}

message NoteTypeNameID {
  int64 id = 1;
  string name = 2;
}

message NoteTypeNameIDUseCount {
  int64 id = 1;
  string name = 2;
  uint32 use_count = 3;
}

message AddOrUpdateNotetypeIn {
  bytes json = 1;
  bool preserve_usn_and_mtime = 2;
}

message AddNoteIn {
  Note note = 1;
  int64 deck_id = 2;
}

message AddNoteOut {
  int64 note_id = 1;
  OpChanges changes = 2;
}

message UpdateNoteIn {
  Note note = 1;
  bool skip_undo_entry = 2;
}

message UpdateCardIn {
  Card card = 1;
  bool skip_undo_entry = 2;
}

message EmptyCardsReport {
  message NoteWithEmptyCards {
    int64 note_id = 1;
    repeated int64 card_ids = 2;
    bool will_delete_note = 3;
  }
  string report = 1;
  repeated NoteWithEmptyCards notes = 2;
}

message DeckNames {
  repeated DeckNameID entries = 1;
}

message DeckNameID {
  int64 id = 1;
  string name = 2;
}

message AddOrUpdateDeckLegacyIn {
  bytes deck = 1;
  bool preserve_usn_and_mtime = 2;
}

message FieldNamesForNotesIn {
  repeated int64 nids = 1;
}

message FieldNamesForNotesOut {
  repeated string fields = 1;
}

message FindAndReplaceIn {
  repeated int64 nids = 1;
  string search = 2;
  string replacement = 3;
  bool regex = 4;
  bool match_case = 5;
  string field_name = 6;
}

message BrowserRow {
  message Cell {
    string text = 1;
    bool is_rtl = 2;
  }
  enum Color {
    COLOR_DEFAULT = 0;
    COLOR_MARKED = 1;
    COLOR_SUSPENDED = 2;
    COLOR_FLAG_RED = 3;
    COLOR_FLAG_ORANGE = 4;
    COLOR_FLAG_GREEN = 5;
    COLOR_FLAG_BLUE = 6;
  }
  repeated Cell cells = 1;
  Color color = 2;
  string font_name = 3;
  uint32 font_size = 4;
}

message AfterNoteUpdatesIn {
  repeated int64 nids = 1;
  bool mark_notes_modified = 2;
  bool generate_cards = 3;
}

message NoteIDsAndTagsIn {
  repeated int64 note_ids = 1;
  string tags = 2;
}

message FindAndReplaceTagIn {
  repeated int64 note_ids = 1;
  string search = 2;
  string replacement = 3;
  bool regex = 4;
  bool match_case = 5;
}

message CheckDatabaseOut {
  repeated string problems = 1;
}

message Preferences {
  message Scheduling {
    enum NewReviewMix {
      DISTRIBUTE = 0;
      REVIEWS_FIRST = 1;
      NEW_FIRST = 2;
    }

    // read only
    uint32 scheduler_version = 1;

    uint32 rollover = 2;
    uint32 learn_ahead_secs = 3;
    NewReviewMix new_review_mix = 4;

    // v2 only
    bool new_timezone = 5;
    bool day_learn_first = 6;
  }
  message Reviewing {
    bool hide_audio_play_buttons = 1;
    bool interrupt_audio_when_answering = 2;
    bool show_remaining_due_counts = 3;
    bool show_intervals_on_buttons = 4;
    uint32 time_limit_secs = 5;
  }
  message Editing {
    bool adding_defaults_to_current_deck = 1;
    bool paste_images_as_png = 2;
    bool paste_strips_formatting = 3;
  }

  Scheduling scheduling = 1;
  Reviewing reviewing = 2;
  Editing editing = 3;
}

message ClozeNumbersInNoteOut {
  repeated uint32 numbers = 1;
}

message GetDeckNamesIn {
  bool skip_empty_default = 1;
  // if unset, implies skip_empty_default
  bool include_filtered = 2;
}

message DragDropDecksIn {
  repeated int64 source_deck_ids = 1;
  int64 target_deck_id = 2;
}

message NoteIsDuplicateOrEmptyOut {
  enum State {
    NORMAL = 0;
    EMPTY = 1;
    DUPLICATE = 2;
  }
  State state = 1;
}

message SyncLoginIn {
  string username = 1;
  string password = 2;
}

message SyncStatusOut {
  enum Required {
    NO_CHANGES = 0;
    NORMAL_SYNC = 1;
    FULL_SYNC = 2;
  }
  Required required = 1;
}

message SyncCollectionOut {
  enum ChangesRequired {
    NO_CHANGES = 0;
    NORMAL_SYNC = 1;
    FULL_SYNC = 2;
    // local collection has no cards; upload not an option
    FULL_DOWNLOAD = 3;
    // remote collection has no cards; download not an option
    FULL_UPLOAD = 4;
  }

  uint32 host_number = 1;
  string server_message = 2;
  ChangesRequired required = 3;
}

message SyncAuth {
  string hkey = 1;
  uint32 host_number = 2;
}

message SyncServerMethodIn {
  enum Method {
    HOST_KEY = 0;
    META = 1;
    START = 2;
    APPLY_GRAVES = 3;
    APPLY_CHANGES = 4;
    CHUNK = 5;
    APPLY_CHUNK = 6;
    SANITY_CHECK = 7;
    FINISH = 8;
    ABORT = 9;
    // caller must reopen after these two are called
    FULL_UPLOAD = 10;
    FULL_DOWNLOAD = 11;
  }
  Method method = 1;
  bytes data = 2;
}

message RemoveNotesIn {
  repeated int64 note_ids = 1;
  repeated int64 card_ids = 2;
}

message RemoveCardsIn {
  repeated int64 card_ids = 1;
}

message UpdateStatsIn {
  int64 deck_id = 1;
  int32 new_delta = 2;
  int32 review_delta = 4;
  int32 millisecond_delta = 5;
}

message ExtendLimitsIn {
  int64 deck_id = 1;
  int32 new_delta = 2;
  int32 review_delta = 3;
}

message CountsForDeckTodayOut {
  int32 new = 1;
  int32 review = 2;
}

message GraphsIn {
  string search = 1;
  uint32 days = 2;
}

message GraphsOut {
  repeated Card cards = 1;
  repeated RevlogEntry revlog = 2;
  uint32 days_elapsed = 3;
  // Based on rollover hour
  uint32 next_day_at_secs = 4;
  uint32 scheduler_version = 5;
  /// Seconds to add to UTC timestamps to get local time.
  int32 local_offset_secs = 7;
}

message GraphPreferences {
  enum Weekday {
    SUNDAY = 0;
    MONDAY = 1;
    FRIDAY = 5;
    SATURDAY = 6;
  }
  Weekday calendar_first_day_of_week = 1;
  bool card_counts_separate_inactive = 2;
  bool browser_links_supported = 3;
  bool future_due_show_backlog = 4;
}

message RevlogEntry {
  enum ReviewKind {
    LEARNING = 0;
    REVIEW = 1;
    RELEARNING = 2;
    EARLY_REVIEW = 3;
    MANUAL = 4;
  }
  int64 id = 1;
  int64 cid = 2;
  int32 usn = 3;
  uint32 button_chosen = 4;
  int32 interval = 5;
  int32 last_interval = 6;
  uint32 ease_factor = 7;
  uint32 taken_millis = 8;
  ReviewKind review_kind = 9;
}

message CongratsInfoOut {
  uint32 learn_remaining = 1;
  uint32 secs_until_next_learn = 2;
  bool review_remaining = 3;
  bool new_remaining = 4;
  bool have_sched_buried = 5;
  bool have_user_buried = 6;
  bool is_filtered_deck = 7;
  bool bridge_commands_supported = 8;
  string deck_description = 9;
}

message UnburyCardsInCurrentDeckIn {
  enum Mode {
    ALL = 0;
    SCHED_ONLY = 1;
    USER_ONLY = 2;
  }
  Mode mode = 1;
}

message BuryOrSuspendCardsIn {
  enum Mode {
    SUSPEND = 0;
    BURY_SCHED = 1;
    BURY_USER = 2;
  }
  repeated int64 card_ids = 1;
  Mode mode = 2;
}

message ScheduleCardsAsNewIn {
  repeated int64 card_ids = 1;
  bool log = 2;
}

message SetDueDateIn {
  repeated int64 card_ids = 1;
  string days = 2;
  Config.String config_key = 3;
}

message SortCardsIn {
  repeated int64 card_ids = 1;
  uint32 starting_from = 2;
  uint32 step_size = 3;
  bool randomize = 4;
  bool shift_existing = 5;
}

message SortDeckIn {
  int64 deck_id = 1;
  bool randomize = 2;
}

message SetDeckIn {
  repeated int64 card_ids = 1;
  int64 deck_id = 2;
}

message Config {
  message Bool {
    enum Key {
      BROWSER_SORT_BACKWARDS = 0;
      PREVIEW_BOTH_SIDES = 1;
      COLLAPSE_TAGS = 2;
      COLLAPSE_NOTETYPES = 3;
      COLLAPSE_DECKS = 4;
      COLLAPSE_SAVED_SEARCHES = 5;
      COLLAPSE_TODAY = 6;
      COLLAPSE_CARD_STATE = 7;
      COLLAPSE_FLAGS = 8;
      SCHED_2021 = 9;
      ADDING_DEFAULTS_TO_CURRENT_DECK = 10;
      HIDE_AUDIO_PLAY_BUTTONS = 11;
      INTERRUPT_AUDIO_WHEN_ANSWERING = 12;
      PASTE_IMAGES_AS_PNG = 13;
      PASTE_STRIPS_FORMATTING = 14;
    }
    Key key = 1;
  }

  message String {
    enum Key {
      SET_DUE_BROWSER = 0;
      SET_DUE_REVIEWER = 1;
    }
    Key key = 1;
  }
}

message SetConfigBoolIn {
  Config.Bool.Key key = 1;
  bool value = 2;
}

message SetConfigStringIn {
  Config.String.Key key = 1;
  string value = 2;
}

message RenderMarkdownIn {
  string markdown = 1;
  bool sanitize = 2;
}

message SchedulingState {
  message New {
    uint32 position = 1;
  }
  message Learning {
    uint32 remaining_steps = 1;
    uint32 scheduled_secs = 2;
  }
  message Review {
    uint32 scheduled_days = 1;
    uint32 elapsed_days = 2;
    float ease_factor = 3;
    uint32 lapses = 4;
    bool leeched = 5;
  }
  message Relearning {
    Review review = 1;
    Learning learning = 2;
  }
  message Normal {
    oneof value {
      New new = 1;
      Learning learning = 2;
      Review review = 3;
      Relearning relearning = 4;
    }
  }
  message Preview {
    uint32 scheduled_secs = 1;
    bool finished = 2;
  }
  message ReschedulingFilter {
    Normal original_state = 1;
  }
  message Filtered {
    oneof value {
      Preview preview = 1;
      ReschedulingFilter rescheduling = 2;
    }
  }

  oneof value {
    Normal normal = 1;
    Filtered filtered = 2;
  }
}

message NextCardStates {
  SchedulingState current = 1;
  SchedulingState again = 2;
  SchedulingState hard = 3;
  SchedulingState good = 4;
  SchedulingState easy = 5;
}

message AnswerCardIn {
  enum Rating {
    AGAIN = 0;
    HARD = 1;
    GOOD = 2;
    EASY = 3;
  }

  int64 card_id = 1;
  SchedulingState current_state = 2;
  SchedulingState new_state = 3;
  Rating rating = 4;
  int64 answered_at_millis = 5;
  uint32 milliseconds_taken = 6;
}

message GetQueuedCardsIn {
  uint32 fetch_limit = 1;
  bool intraday_learning_only = 2;
}

message GetQueuedCardsOut {
  enum Queue {
    New = 0;
    Learning = 1;
    Review = 2;
  }

  message QueuedCard {
    Card card = 1;
    Queue queue = 2;
    NextCardStates next_states = 3;
  }

  message QueuedCards {
    repeated QueuedCard cards = 1;
    uint32 new_count = 2;
    uint32 learning_count = 3;
    uint32 review_count = 4;
  }

  oneof value {
    QueuedCards queued_cards = 1;
    CongratsInfoOut congrats_info = 2;
  }
}

message OpChanges {
  // this is not an exhaustive list; we can add more cases as we need them
  enum Kind {
    OTHER = 0;
    UPDATE_NOTE_TAGS = 1;
    SET_CARD_FLAG = 2;
    UPDATE_NOTE = 3;
  }

  Kind kind = 1;
  bool card = 2;
  bool note = 3;
  bool deck = 4;
  bool tag = 5;
  bool notetype = 6;
  bool preference = 7;
}

message UndoStatus {
  string undo = 1;
  string redo = 2;
}

message DefaultsForAddingIn {
  int64 home_deck_of_current_review_card = 1;
}

message DeckAndNotetype {
  int64 deck_id = 1;
  int64 notetype_id = 2;
}

message RenameDeckIn {
  int64 deck_id = 1;
  string new_name = 2;
}

message SetFlagIn {
  repeated int64 card_ids = 1;
  uint32 flag = 2;
}
