syntax = "proto3";
package influxdata.platform.storage;
option go_package = "datatypes";

import "gogoproto/gogo.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/any.proto";
import "storage/reads/datatypes/predicate.proto";

option (gogoproto.marshaler_all) = true;
option (gogoproto.sizer_all) = true;
option (gogoproto.unmarshaler_all) = true;
option (gogoproto.goproto_getters_all) = false;

service Storage {
  // Read performs a read operation using the given ReadRequest
  rpc Read (ReadRequest) returns (stream ReadResponse);

  // Capabilities returns a map of keys and values identifying the capabilities supported by the storage engine
  rpc Capabilities (google.protobuf.Empty) returns (CapabilitiesResponse);

  rpc Hints (google.protobuf.Empty) returns (HintsResponse);

  // Explain describes the costs associated with executing a given Read request
  // rpc Explain(google.protobuf.Empty) returns (ExplainResponse){}
}

// Request message for Storage.Read.
message ReadRequest {
  enum Group {
    option (gogoproto.goproto_enum_prefix) = false;

    // GroupNone returns all series as a single group.
    // The single GroupFrame.TagKeys will be the union of all tag keys.
    GROUP_NONE = 0 [(gogoproto.enumvalue_customname) = "GroupNone"];

    // GroupAll returns a unique group for each series.
    // As an optimization, no GroupFrames will be generated.
    GROUP_ALL = 1 [(gogoproto.enumvalue_customname) = "GroupAll"];

    // GroupBy returns a group for each unique value of the specified GroupKeys.
    GROUP_BY = 2 [(gogoproto.enumvalue_customname) = "GroupBy"];

    // GroupExcept in not implemented.
    GROUP_EXCEPT = 3 [(gogoproto.enumvalue_customname) = "GroupExcept"];
  }

  enum HintFlags {
    option (gogoproto.goproto_enum_prefix) = false;

    HINT_NONE      = 0x00 [(gogoproto.enumvalue_customname) = "HintNone"];
    HINT_NO_POINTS = 0x01 [(gogoproto.enumvalue_customname) = "HintNoPoints"];
    HINT_NO_SERIES = 0x02 [(gogoproto.enumvalue_customname) = "HintNoSeries"];
    // HintSchemaAllTime performs schema queries without using time ranges
    HINT_SCHEMA_ALL_TIME = 0x04 [(gogoproto.enumvalue_customname) = "HintSchemaAllTime"];
  }

  google.protobuf.Any read_source = 13 [(gogoproto.customname) = "ReadSource"];

  TimestampRange timestamp_range = 2 [(gogoproto.customname) = "TimestampRange", (gogoproto.nullable) = false];

  // Descending indicates whether points should be returned in descending order.
  bool descending = 3;

  // GroupKeys specifies a list of tag keys used to order the data. It is dependent on the Group property to determine
  // its behavior.
  repeated string group_keys = 4 [(gogoproto.customname) = "GroupKeys"];

  //
  Group group = 11;

  // Aggregate specifies an optional aggregate to apply to the data.
  // TODO(sgc): switch to slice for multiple aggregates in a single request
  Aggregate aggregate = 9;

  Predicate predicate = 5;

  // SeriesLimit determines the maximum number of series to be returned for the request. Specify 0 for no limit.
  int64 series_limit = 6 [(gogoproto.customname) = "SeriesLimit"];

  // SeriesOffset determines how many series to skip before processing the request.
  int64 series_offset = 7 [(gogoproto.customname) = "SeriesOffset"];

  // PointsLimit determines the maximum number of values per series to be returned for the request.
  // Specify 0 for no limit. -1 to return series frames only.
  int64 points_limit = 8 [(gogoproto.customname) = "PointsLimit"];

  // Trace contains opaque data if a trace is active.
  map<string, string> trace = 10 [(gogoproto.customname) = "Trace"];

  // Hints is a bitwise OR of HintFlags to control the behavior
  // of the read request.
  fixed32 hints = 12 [(gogoproto.customname) = "Hints", (gogoproto.casttype) = "HintFlags"];
}

message Aggregate {
  enum AggregateType {
    option (gogoproto.goproto_enum_prefix) = false;

    NONE = 0 [(gogoproto.enumvalue_customname) = "AggregateTypeNone"];
    SUM = 1 [(gogoproto.enumvalue_customname) = "AggregateTypeSum"];
    COUNT = 2 [(gogoproto.enumvalue_customname) = "AggregateTypeCount"];
  }

  AggregateType type = 1;

  // additional arguments?
}

message Tag {
  bytes key = 1;
  bytes value = 2;
}

// Response message for Storage.Read.
message ReadResponse {
  enum FrameType {
    option (gogoproto.goproto_enum_prefix) = false;

    SERIES = 0 [(gogoproto.enumvalue_customname) = "FrameTypeSeries"];
    POINTS = 1 [(gogoproto.enumvalue_customname) = "FrameTypePoints"];
  }

  enum DataType {
    option (gogoproto.goproto_enum_prefix) = false;

    FLOAT = 0 [(gogoproto.enumvalue_customname) = "DataTypeFloat"];
    INTEGER = 1 [(gogoproto.enumvalue_customname) = "DataTypeInteger"];
    UNSIGNED = 2 [(gogoproto.enumvalue_customname) = "DataTypeUnsigned"];
    BOOLEAN = 3 [(gogoproto.enumvalue_customname) = "DataTypeBoolean"];
    STRING = 4 [(gogoproto.enumvalue_customname) = "DataTypeString"];
  }

  message Frame {
    oneof data {
      GroupFrame group = 7;
      SeriesFrame series = 1;
      FloatPointsFrame float_points = 2 [(gogoproto.customname) = "FloatPoints"];
      IntegerPointsFrame integer_points = 3 [(gogoproto.customname) = "IntegerPoints"];
      UnsignedPointsFrame unsigned_points = 4 [(gogoproto.customname) = "UnsignedPoints"];
      BooleanPointsFrame boolean_points = 5 [(gogoproto.customname) = "BooleanPoints"];
      StringPointsFrame string_points = 6 [(gogoproto.customname) = "StringPoints"];
    }
  }

  message GroupFrame {
    // TagKeys
    repeated bytes tag_keys = 1 [(gogoproto.customname) = "TagKeys"];
    // PartitionKeyVals is the values of the partition key for this group, order matching ReadRequest.GroupKeys
    repeated bytes partition_key_vals = 2 [(gogoproto.customname) = "PartitionKeyVals"];
  }

  message SeriesFrame {
    repeated Tag tags = 1 [(gogoproto.nullable) = false];
    DataType data_type = 2;
  }

  message FloatPointsFrame {
    repeated sfixed64 timestamps = 1;
    repeated double values = 2;
  }

  message IntegerPointsFrame {
    repeated sfixed64 timestamps = 1;
    repeated int64 values = 2;
  }

  message UnsignedPointsFrame {
    repeated sfixed64 timestamps = 1;
    repeated uint64 values = 2;
  }

  message BooleanPointsFrame {
    repeated sfixed64 timestamps = 1;
    repeated bool values = 2;
  }

  message StringPointsFrame {
    repeated sfixed64 timestamps = 1;
    repeated string values = 2;
  }

  repeated Frame frames = 1 [(gogoproto.nullable) = false];
}

message CapabilitiesResponse {
  map<string, string> caps = 1;
}

message HintsResponse {
}

// Specifies a continuous range of nanosecond timestamps.
message TimestampRange {
  // Start defines the inclusive lower bound.
  int64 start = 1;

  // End defines the inclusive upper bound.
  int64 end = 2;
}

//message ExplainRequest {
//  ReadRequest read_request = 1 [(gogoproto.customname) = "ReadRequest"];
//}
//
//message ExplainResponse {}
