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;
}

// 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 DeckConfigID {
    int64 dcid = 1;
}

// New style RPC definitions
///////////////////////////////////////////////////////////

service BackendService {
    rpc LatestProgress (Empty) returns (Progress);
    rpc SetWantsAbort (Empty) returns (Empty);

    // card rendering

    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);

    // searching

    rpc SearchCards (SearchCardsIn) returns (SearchCardsOut);
    rpc SearchNotes (SearchNotesIn) returns (SearchNotesOut);
    rpc FindAndReplace (FindAndReplaceIn) returns (UInt32);

    // scheduling

    rpc LocalMinutesWest (Int64) returns (Int32);
    rpc SetLocalMinutesWest (Int32) returns (Empty);
    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 (Empty);
    rpc UnburyCardsInCurrentDeck (UnburyCardsInCurrentDeckIn) returns (Empty);
    rpc BuryOrSuspendCards (BuryOrSuspendCardsIn) returns (Empty);
    rpc EmptyFilteredDeck (DeckID) returns (Empty);
    rpc RebuildFilteredDeck (DeckID) returns (UInt32);
    rpc ScheduleCardsAsReviews (ScheduleCardsAsReviewsIn) returns (Empty);
    rpc ScheduleCardsAsNew (ScheduleCardsAsNewIn) returns (Empty);
    rpc SortCards (SortCardsIn) returns (Empty);
    rpc SortDeck (SortDeckIn) returns (Empty);

    // stats

    rpc CardStats (CardID) returns (String);
    rpc Graphs(GraphsIn) returns (GraphsOut);

    // media

    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);

    // decks

    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 RemoveDeck (DeckID) returns (Empty);

    // deck config

    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);

    // cards

    rpc GetCard (CardID) returns (Card);
    rpc UpdateCard (Card) returns (Empty);
    rpc AddCard (Card) returns (CardID);
    rpc RemoveCards (RemoveCardsIn) returns (Empty);
    rpc SetDeck (SetDeckIn) returns (Empty);

    // notes

    rpc NewNote (NoteTypeID) returns (Note);
    rpc AddNote (AddNoteIn) returns (NoteID);
    rpc UpdateNote (Note) returns (Empty);
    rpc GetNote (NoteID) returns (Note);
    rpc RemoveNotes (RemoveNotesIn) returns (Empty);
    rpc AddNoteTags (AddNoteTagsIn) returns (UInt32);
    rpc UpdateNoteTags (UpdateNoteTagsIn) returns (UInt32);
    rpc ClozeNumbersInNote (Note) returns (ClozeNumbersInNoteOut);
    rpc AfterNoteUpdates (AfterNoteUpdatesIn) returns (Empty);
    rpc FieldNamesForNotes (FieldNamesForNotesIn) returns (FieldNamesForNotesOut);
    rpc NoteIsDuplicateOrEmpty (Note) returns (NoteIsDuplicateOrEmptyOut);
    rpc CardsOfNote (NoteID) returns (CardIDs);

    // note types

    rpc AddOrUpdateNotetype (AddOrUpdateNotetypeIn) returns (NoteTypeID);
    rpc GetStockNotetypeLegacy (GetStockNotetypeIn) 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);

    // collection

    rpc OpenCollection (OpenCollectionIn) returns (Empty);
    rpc CloseCollection (CloseCollectionIn) returns (Empty);
    rpc CheckDatabase (Empty) returns (CheckDatabaseOut);

    // sync

    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);

    // translation/messages

    rpc TranslateString (TranslateStringIn) returns (String);
    rpc FormatTimespan (FormatTimespanIn) returns (String);
    rpc I18nResources (Empty) returns (Json);

    // tags

    rpc RegisterTags (RegisterTagsIn) returns (Bool);
    rpc AllTags (Empty) returns (AllTagsOut);

    // config/preferences

    rpc GetConfigJson (String) returns (Json);
    rpc SetConfigJson (SetConfigJsonIn) returns (Empty);
    rpc RemoveConfig (String) returns (Empty);
    rpc SetAllConfig (Json) returns (Empty);
    rpc GetAllConfig (Empty) returns (Json);
    rpc GetPreferences (Empty) returns (Preferences);
    rpc SetPreferences (Preferences) returns (Empty);
}

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

    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_review_interval = 17;

    uint32 graduating_interval_good = 18;
    uint32 graduating_interval_easy = 19;

    NewCardOrder new_card_order = 20;

    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;

    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;
}

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;
    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;
    }
    SyncErrorKind kind = 1;
}

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

message Progress {
    oneof value {
        Empty none = 1;
        MediaSyncProgress media_sync = 2;
        string media_check = 3;
        FullSyncProgress full_sync = 4;
        NormalSyncProgress normal_sync = 5;
        DatabaseCheckProgress database_check = 6;
    }
}

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

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

message MediaSyncUploadProgress {
    uint32 files = 1;
    uint32 deletions = 2;
}

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

message DatabaseCheckProgress {
    string stage = 1;
    uint32 stage_total = 2;
    uint32 stage_current = 3;
}


// 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 {
    oneof value {
        Empty from_config = 1;
        Empty none = 2;
        string custom = 3;
        BuiltinSearchOrder builtin = 4;
    }
}

message SearchNotesIn {
    string search = 1;
}

message SearchNotesOut {
    repeated int64 note_ids = 2;
}

message BuiltinSearchOrder {
    enum BuiltinSortKind {
        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;
    }
    BuiltinSortKind kind = 1;
    bool reverse = 2;
}

message CloseCollectionIn {
    bool downgrade_to_schema11 = 1;
}

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

message RegisterTagsIn {
    string tags = 1;
    bool preserve_usn = 2;
    int32 usn = 3;
    bool clear_first = 4;
}

message AllTagsOut {
    repeated TagUsnTuple tags = 1;
}

message TagUsnTuple {
    string tag = 1;
    sint32 usn = 2;
}

message GetChangedTagsOut {
    repeated string tags = 1;
}

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

enum StockNoteType {
    STOCK_NOTE_TYPE_BASIC = 0;
    STOCK_NOTE_TYPE_BASIC_AND_REVERSED = 1;
    STOCK_NOTE_TYPE_BASIC_OPTIONAL_REVERSED = 2;
    STOCK_NOTE_TYPE_BASIC_TYPING = 3;
    STOCK_NOTE_TYPE_CLOZE = 4;
}

message GetStockNotetypeIn {
    StockNoteType 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 EmptyCardsReport {
    string report = 1;
    repeated NoteWithEmptyCards notes = 2;
}

message NoteWithEmptyCards {
    int64 note_id = 1;
    repeated int64 card_ids = 2;
    bool will_delete_note = 3;
}

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 AfterNoteUpdatesIn {
    repeated int64 nids = 1;
    bool mark_notes_modified = 2;
    bool generate_cards = 3;
}

message AddNoteTagsIn {
    repeated int64 nids = 1;
    string tags = 2;
}

message UpdateNoteTagsIn {
    repeated int64 nids = 1;
    string tags = 2;
    string replacement = 3;
    bool regex = 4;
}

message CheckDatabaseOut {
    repeated string problems = 1;
}

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

    uint32 scheduler_version = 1;
    uint32 rollover = 2;
    uint32 learn_ahead_secs = 3;
    NewReviewMix new_review_mix = 4;
    bool show_remaining_due_counts = 5;
    bool show_intervals_on_buttons = 6;
    uint32 time_limit_secs = 7;

    // v2 only
    bool new_timezone = 8;
    bool day_learn_first = 9;
}

message Preferences {
    CollectionSchedulingSettings sched = 1;
}

message ClozeNumbersInNoteOut {
    repeated uint32 numbers = 1;
}

message GetDeckNamesIn {
    bool skip_empty_default = 1;
    // if unset, implies skip_empty_default
    bool include_filtered = 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 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 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;
}

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 ScheduleCardsAsReviewsIn {
    repeated int64 card_ids = 1;
    uint32 min_interval = 2;
    uint32 max_interval = 3;
}

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

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;
}
