// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
// ┃ This file is part of the Perspective library, distributed under the terms ┃
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

syntax = "proto3";

import "google/protobuf/struct.proto";

package perspective.proto;

option optimize_for = LITE_RUNTIME;

////////////////////////////////////////////////////////////////////////////////
//
// Common

enum StatusCode {
    SERVER_ERROR = 0;
    VIEW_NOT_FOUND = 1;
    TRANSPORT_ERROR = 2;
}

// Recoverable, user-readable error reporting from the engine.
message ServerError {
    string message = 1;
    StatusCode status_code = 2;
}

message Schema {
    repeated KeyTypePair schema = 1;
    message KeyTypePair {
        string name = 1;
        ColumnType type = 2;
    }
}

// The data type constructors Perspective supports.
message MakeTableData {
    oneof data {
        Schema from_schema = 1;
        string from_csv = 2;
        bytes from_arrow = 3;
        string from_rows = 4;
        string from_cols = 5;
        string from_view = 6;
        string from_ndjson = 7;
    };
}

// Filter type scalars - this is _not_ the same as a Columns scalar, as this
// value is used in the view config and must be JSON safe!
message Scalar {
    oneof scalar {
        bool bool = 1;
        // int64 date = 2; // TODO these are the wrong type
        // int64 datetime = 3;
        double float = 4;
        // int32 int = 5;
        string string = 6;
        google.protobuf.NullValue null = 7;
    }
}

// View types
enum ColumnType {
    STRING = 0;
    DATE = 1;
    DATETIME = 2;
    INTEGER = 3;
    FLOAT = 4;
    BOOLEAN = 5;
}

// Options for requresting a slice of data, starting with the rectangular
// viewport.
message ViewPort {
    optional uint32 start_row = 1;
    optional uint32 start_col = 2;
    optional uint32 end_row = 3;
    optional uint32 end_col = 4;
    optional bool emit_legacy_row_path_names = 5;
//   optional bool id = 5;
//   optional bool index = 3;
//   optional bool formatted = 6;
//   optional bool leaves_only = 7;
//   optional bool compression = 3;
}

// TODO This belongs in features
enum SortOp {
    SORT_NONE = 0;
    SORT_ASC = 1;
    SORT_DESC = 2;
    SORT_COL_ASC = 3;
    SORT_COL_DESC = 4;
    SORT_ASC_ABS = 5;
    SORT_DESC_ABS = 6;
    SORT_COL_ASC_ABS = 7;
    SORT_COL_DESC_ABS = 8;
}



////////////////////////////////////////////////////////////////////////////////
//
// RPC

message Request {
    uint32 msg_id = 1;
    string entity_id = 2;
    oneof client_req {
        // Minimum Virtual API (theoretical).
        GetFeaturesReq get_features_req = 3;
        GetHostedTablesReq get_hosted_tables_req = 4;
        RemoveHostedTablesUpdateReq remove_hosted_tables_update_req = 37;
        TableMakePortReq table_make_port_req = 5;
        TableMakeViewReq table_make_view_req = 6;
        TableSchemaReq table_schema_req = 7;
        TableSizeReq table_size_req = 8;
        TableValidateExprReq table_validate_expr_req = 9;
        ViewColumnPathsReq view_column_paths_req = 10;
        ViewDeleteReq view_delete_req = 11;
        ViewDimensionsReq view_dimensions_req = 12;
        ViewExpressionSchemaReq view_expression_schema_req = 13;
        ViewGetConfigReq view_get_config_req = 14;
        ViewSchemaReq view_schema_req = 15;
        ViewToArrowReq view_to_arrow_req = 16;

        // Optional (we can enable real-time/autocomplete/etc with these, but
        // not required).
        ServerSystemInfoReq server_system_info_req = 17;
        ViewCollapseReq view_collapse_req = 18;
        ViewExpandReq view_expand_req = 19;
        ViewGetMinMaxReq view_get_min_max_req = 20;
        ViewOnUpdateReq view_on_update_req = 21;
        ViewRemoveOnUpdateReq view_remove_on_update_req = 22;
        ViewSetDepthReq view_set_depth_req = 23;
        ViewToColumnsStringReq view_to_columns_string_req = 24;
        ViewToCSVReq view_to_csv_req = 25;
        ViewToRowsStringReq view_to_rows_string_req = 26;
        ViewToNdjsonStringReq view_to_ndjson_string_req = 36;

        // External (we don't need these for viewer, but the developer may).
        MakeTableReq make_table_req = 27;
        TableDeleteReq table_delete_req = 28;
        TableOnDeleteReq table_on_delete_req = 29;
        TableRemoveDeleteReq table_remove_delete_req = 30;
        TableRemoveReq table_remove_req = 31;
        TableReplaceReq table_replace_req = 32;
        TableUpdateReq table_update_req = 33;
        ViewOnDeleteReq view_on_delete_req = 34;
        ViewRemoveDeleteReq view_remove_delete_req = 35;
        MakeJoinTableReq make_join_table_req = 38;
    }
}

message Response {
    uint32 msg_id = 1;
    string entity_id = 2;
    oneof client_resp {
        GetFeaturesResp get_features_resp = 3;
        GetHostedTablesResp get_hosted_tables_resp = 4;
        RemoveHostedTablesUpdateResp remove_hosted_tables_update_resp = 37;
        TableMakePortResp table_make_port_resp = 5;
        TableMakeViewResp table_make_view_resp = 6;
        TableSchemaResp table_schema_resp = 7;
        TableSizeResp table_size_resp = 8;
        TableValidateExprResp table_validate_expr_resp = 9;
        ViewColumnPathsResp view_column_paths_resp = 10;
        ViewDeleteResp view_delete_resp = 11;
        ViewDimensionsResp view_dimensions_resp = 12;
        ViewExpressionSchemaResp view_expression_schema_resp = 13;
        ViewGetConfigResp view_get_config_resp = 14;
        ViewSchemaResp view_schema_resp = 15;
        ViewToArrowResp view_to_arrow_resp = 16;
        ServerSystemInfoResp server_system_info_resp = 17;
        ViewCollapseResp view_collapse_resp = 18;
        ViewExpandResp view_expand_resp = 19;
        ViewGetMinMaxResp view_get_min_max_resp = 20;
        ViewOnUpdateResp view_on_update_resp = 21;
        ViewRemoveOnUpdateResp view_remove_on_update_resp = 22;
        ViewSetDepthResp view_set_depth_resp = 23;
        ViewToColumnsStringResp view_to_columns_string_resp = 24;
        ViewToCSVResp view_to_csv_resp = 25;
        ViewToRowsStringResp view_to_rows_string_resp = 26;
        ViewToNdjsonStringResp view_to_ndjson_string_resp = 36;
        MakeTableResp make_table_resp = 27;
        TableDeleteResp table_delete_resp = 28;
        TableOnDeleteResp table_on_delete_resp = 29;
        TableRemoveDeleteResp table_remove_delete_resp = 30;
        TableRemoveResp table_remove_resp = 31;
        TableReplaceResp table_replace_resp = 32;
        TableUpdateResp table_update_resp = 33;
        ViewOnDeleteResp view_on_delete_resp = 34;
        ViewRemoveDeleteResp view_remove_delete_resp = 35;
        MakeJoinTableResp make_join_table_resp = 38;
        ServerError server_error = 50;
    }
}

////////////////////////////////////////////////////////////////////////////////
//
// Virtual API


enum GroupRollupMode {
    ROLLUP = 0;
    FLAT = 1;
    TOTAL = 2;
}

// Informs the client of the feature set, e.g. what to expect in the
// `ViewConfig` message.
message GetFeaturesReq {}
message GetFeaturesResp {
    bool group_by = 1;
    bool split_by = 2;
    bool expressions = 3;
    bool on_update = 4;
    bool sort = 5;
    map<uint32, ColumnTypeOptions> filter_ops = 6;
    map<uint32, AggregateOptions> aggregates = 7;
    repeated GroupRollupMode group_rollup_mode = 8;

    message ColumnTypeOptions {
        repeated string options = 1;
    }

    message AggregateOptions {
        repeated AggregateArgs aggregates = 1;
    }

    message AggregateArgs {
        string name = 1;
        repeated ColumnType args = 2;
    }
}

// `Client::get_hosted_tables`
message GetHostedTablesReq {
    bool subscribe = 1;
}

message GetHostedTablesResp {
    repeated HostedTable table_infos = 1;
}

message HostedTable {
    string entity_id = 1;
    optional string index = 2;
    optional uint32 limit = 3;
}

message RemoveHostedTablesUpdateReq {
    uint32 id = 1;
}
message RemoveHostedTablesUpdateResp {}

// `Table::size`
message TableSizeReq {}
message TableSizeResp {
    uint32 size = 2;
}

// `Table::schema`
message TableSchemaReq {}
message TableSchemaResp {
    Schema schema = 1;
}

// `Table::validate_expressions`
// TODO: This should be just `validate()`
message TableValidateExprReq {
    map<string, string> column_to_expr = 1;
}
message TableValidateExprResp {
    map<string, ColumnType> expression_schema = 1;
    map<string, ExprValidationError> errors = 2;
    map<string, string> expression_alias = 3;
    message ExprValidationError {
        string error_message = 1;
        uint32 line = 2;
        uint32 column = 3;
    }
}

// `Table::view`
message TableMakeViewReq {
    string view_id = 1;
    ViewConfig config = 2;
}
message TableMakeViewResp {
    string view_id = 1;
}

// `View::schema`
message ViewSchemaReq {}
message ViewSchemaResp {
    map<string, ColumnType> schema = 1;
}

// `View::dimensions`
message ViewDimensionsReq {}
message ViewDimensionsResp {
    uint32 num_table_rows = 1;
    uint32 num_table_columns = 2;
    uint32 num_view_rows = 3;
    uint32 num_view_columns = 4;
}

// `View::get_config`
message ViewGetConfigReq {}
message ViewGetConfigResp {
    ViewConfig config = 1;
}


////////////////////////////////////////////////////////////////////////////////

// `Client::table`.
message MakeTableReq {
    MakeTableData data = 1;
    optional MakeTableOptions options = 2;
    message MakeTableOptions {
        oneof make_table_type {
            string make_index_table = 1;
            uint32 make_limit_table = 2;
        };

        // Back this Table's canonical data with the on-disk storage backend
        // (memory-mapped file on native; OPFS on WASM) instead of memory.
        // Orthogonal to `make_table_type`, so it is a standalone field.
        optional bool page_to_disk = 3;
    }
}
message MakeTableResp {}

enum JoinType {
    INNER = 0;
    LEFT = 1;
    OUTER = 2;
}

// `Client::join` — create a read-only table from a JOIN of two tables.
message MakeJoinTableReq {
    string left_table_id = 1;
    string right_table_id = 2;
    string on_column = 3;
    JoinType join_type = 4;
    string right_on_column = 5;
}
message MakeJoinTableResp {}

// `Table::delete`
message TableDeleteReq { 
    bool is_immediate = 1; 
}
message TableDeleteResp {}

// `Table::on_delete`
message TableOnDeleteReq {}
message TableOnDeleteResp {}

// `Table::make_port`
message TableMakePortReq {}
message TableMakePortResp {
    uint32 port_id = 1;
}

// `Table::remove_delete`
message TableRemoveDeleteReq {
    uint32 id = 1;
}
message TableRemoveDeleteResp {}

// `Table::update`
message TableUpdateReq {
    MakeTableData data = 1;
    uint32 port_id = 2;
}
message TableUpdateResp {}

// `Table::replace`
message TableReplaceReq {
    MakeTableData data = 1;
}
message TableReplaceResp {}

// `Table::remove`
message TableRemoveReq {
    MakeTableData data = 1;
}
message TableRemoveResp {}

message ViewOnUpdateReq {
    enum Mode {
        ROW = 0;
    }
    optional Mode mode = 1;
}
message ViewOnUpdateResp {
    optional bytes delta = 1;
    uint32 port_id = 2;
}

message ViewOnDeleteReq {}
message ViewOnDeleteResp {}

message ViewRemoveDeleteReq {
    uint32 id = 1;
}
message ViewRemoveDeleteResp {}

message ViewToColumnsStringReq {
    ViewPort viewport = 1;
    optional bool id = 2;
    optional bool index = 3;
    optional bool formatted = 4;
}

message ViewToColumnsStringResp {
    string json_string = 1;
}

message ViewToRowsStringReq {
    ViewPort viewport = 1;
    optional bool id = 2;
    optional bool index = 3;
    optional bool formatted = 4;
}

message ViewToRowsStringResp {
    string json_string = 1;
}

message ViewToNdjsonStringReq {
    ViewPort viewport = 1;
    optional bool id = 2;
    optional bool index = 3;
    optional bool formatted = 4;
}

message ViewToNdjsonStringResp {
    string ndjson_string = 1;
}

message ViewToArrowReq {
    ViewPort viewport = 1;
    optional string compression = 2;
}

message ViewToArrowResp {
    bytes arrow = 1;
}

message ViewColumnPathsReq {
    optional uint32 start_col = 1;
    optional uint32 end_col = 2;
}

// // TODO This is a better paths representations but its not compatible with
// // the legacy API. Let's do this when we can fix the API.

// message ColumnPath {
//     repeated string path = 1;
// }

message ViewColumnPathsResp {
    repeated string paths = 1;
    // repeated ColumnPath paths = 1;
}

message ViewDeleteReq {}
message ViewDeleteResp {}

message ViewGetMinMaxReq {
    string column_name = 1;
}

message ViewGetMinMaxResp {
    Scalar min = 1;
    Scalar max = 2;
}

message ViewExpressionSchemaReq {}
message ViewExpressionSchemaResp {
    map<string, ColumnType> schema = 1;
}

message ViewToCSVReq {
    ViewPort viewport = 1;
}

message ViewToCSVResp {
    string csv = 1;
}

message ViewRemoveOnUpdateReq {
    uint32 id = 1;
}
message ViewRemoveOnUpdateResp {}

message ViewCollapseReq {
    uint32 row_index = 1;
}

message ViewCollapseResp {
    uint32 num_changed = 1;
}

message ViewExpandReq {
    uint32 row_index = 1;
}

message ViewExpandResp {
    uint32 num_changed = 1;
}

// `View::set_depth`
message ViewSetDepthReq {
    uint32 depth = 1;
}
message ViewSetDepthResp {}

message ServerSystemInfoReq {}
message ServerSystemInfoResp {
    uint64 heap_size = 1;
    uint64 used_size = 2;
    uint32 cpu_time = 3;
    uint32 cpu_time_epoch = 4;
}


message ViewConfig {
    repeated string group_by = 1;
    repeated string split_by = 2;
    ColumnsUpdate columns = 3;
    repeated Filter filter = 4;
    repeated Sort sort = 5;
    map<string, string> expressions = 6;
    map<string, AggList> aggregates = 7;
    FilterReducer filter_op = 8;
    optional uint32 group_by_depth = 9;
    optional GroupRollupMode group_rollup_mode = 10;

    message AggList {
        repeated string aggregations = 1;
    }

    message Sort {
        string column = 1;
        SortOp op = 2;
    }

    message Filter {
        string column = 1;
        string op = 2;
        repeated Scalar value = 3;
    }

    enum FilterReducer {
        AND = 0;
        OR = 1;
    }
}

message ColumnsUpdate {
    oneof opt_columns {
        google.protobuf.NullValue default_columns = 1;
        Columns columns = 2;
    }

    message Columns {
        repeated string columns = 1;
    }
}
