/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

syntax = "proto3";

package ballista.protobuf;

option java_multiple_files = true;
option java_package = "org.ballistacompute.protobuf";
option java_outer_classname = "BallistaProto";

///////////////////////////////////////////////////////////////////////////////////////////////////
// Ballista Logical Plan
///////////////////////////////////////////////////////////////////////////////////////////////////

// logical expressions
message LogicalExprNode {
  oneof ExprType {
    // column references
    string column_name = 1;
    
    // alias
    AliasNode alias = 2;

    ScalarValue literal = 3;


    // binary expressions
    BinaryExprNode binary_expr = 4;
    
    // aggregate expressions
    AggregateExprNode aggregate_expr = 5;
    
    // null checks
    IsNull is_null_expr = 6;
    IsNotNull is_not_null_expr = 7;
    Not not_expr = 8;
    
    BetweenNode between = 9;
    CaseNode case_ = 10;
    CastNode cast = 11;
    SortExprNode sort = 12;
    NegativeNode negative = 13;
    InListNode in_list = 14;
    bool wildcard = 15;
    ScalarFunctionNode scalar_function = 16;
    TryCastNode try_cast = 17;
  }
}

message IsNull {
  LogicalExprNode expr = 1;
}

message IsNotNull {
  LogicalExprNode expr = 1;
}

message Not {
  LogicalExprNode expr = 1;
}

message AliasNode {
  LogicalExprNode expr = 1;
  string alias = 2;
}

message BinaryExprNode {
  LogicalExprNode l = 1;
  LogicalExprNode r = 2;
  string op = 3;
}

message NegativeNode {
  LogicalExprNode expr = 1;
}

message InListNode {
  LogicalExprNode expr = 1;
  repeated LogicalExprNode list = 2;
  bool negated = 3;
}

enum ScalarFunction {
  SQRT = 0;
  SIN = 1;
  COS = 2;
  TAN = 3;
  ASIN = 4;
  ACOS = 5;
  ATAN = 6;
  EXP = 7;
  LOG = 8;
  LOG2 = 9;
  LOG10 = 10;
  FLOOR = 11;
  CEIL = 12;
  ROUND = 13;
  TRUNC = 14;
  ABS = 15;
  SIGNUM = 16;
  OCTETLENGTH = 17;
  CONCAT = 18;
  LOWER = 19;
  UPPER = 20;
  TRIM = 21;
  LTRIM = 22;
  RTRIM = 23;
  TOTIMESTAMP = 24;
  ARRAY = 25;
  NULLIF = 26;
  DATETRUNC = 27;
  MD5 = 28;
  SHA224 = 29;
  SHA256 = 30;
  SHA384 = 31;
  SHA512 = 32;
}

message ScalarFunctionNode {
  ScalarFunction fun = 1;
  repeated LogicalExprNode expr = 2;
}

enum AggregateFunction {
  MIN = 0;
  MAX = 1;
  SUM = 2;
  AVG = 3;
  COUNT = 4;
}

message AggregateExprNode {
  AggregateFunction aggr_function = 1;
  LogicalExprNode expr = 2;
}

message BetweenNode {
  LogicalExprNode expr = 1;
  bool negated = 2;
  LogicalExprNode low = 3;
  LogicalExprNode high = 4;
}

message CaseNode {
  LogicalExprNode expr = 1;
  repeated WhenThen when_then_expr = 2;
  LogicalExprNode else_expr = 3;
}

message WhenThen {
  LogicalExprNode when_expr = 1;
  LogicalExprNode then_expr = 2;
}

message CastNode {
  LogicalExprNode expr = 1;
  ArrowType arrow_type = 2;
}

message TryCastNode {
  LogicalExprNode expr = 1;
  ArrowType arrow_type = 2;
}

message SortExprNode {
  LogicalExprNode expr = 1;
  bool asc = 2;
  bool nulls_first = 3;
}

// LogicalPlan is a nested type
message LogicalPlanNode {
  oneof LogicalPlanType {
    CsvTableScanNode csv_scan = 1;
    ParquetTableScanNode parquet_scan = 2;
    ProjectionNode projection = 3;
    SelectionNode selection = 4;
    LimitNode limit = 5;
    AggregateNode aggregate = 6;
    JoinNode join = 7;
    SortNode sort = 8;
    RepartitionNode repartition = 9;
    EmptyRelationNode empty_relation = 10;
    CreateExternalTableNode create_external_table = 11;
    ExplainNode explain = 12;
  }
}

message ProjectionColumns {
  repeated string columns = 1;
}

message CsvTableScanNode {
  string table_name = 1;
  string path = 2;
  bool has_header = 3;
  string delimiter = 4;
  string file_extension = 5;
  ProjectionColumns projection = 6;
  Schema schema = 7;
  repeated LogicalExprNode filters = 8;
}

message ParquetTableScanNode {
  string table_name = 1;
  string path = 2;
  ProjectionColumns projection = 3;
  Schema schema = 4;
  repeated LogicalExprNode filters = 5;
}

message ProjectionNode {
  LogicalPlanNode input = 1;
  repeated LogicalExprNode expr = 2;
}

message SelectionNode {
  LogicalPlanNode input = 1;
  LogicalExprNode expr = 2;
}

message SortNode{
  LogicalPlanNode input = 1;
  repeated LogicalExprNode expr = 2;
}

message RepartitionNode{
  LogicalPlanNode input = 1;
  oneof partition_method {
    uint64 round_robin = 2;
    HashRepartition hash = 3;
  }
}

message HashRepartition {
  repeated LogicalExprNode hash_expr = 1;
  uint64 partition_count = 2;
}

message EmptyRelationNode{
  bool produce_one_row = 1;
}

message CreateExternalTableNode{
  string name = 1;
  string location = 2;
  FileType file_type = 3;
  bool has_header = 4;
  Schema schema = 5;
}

enum FileType{
  NdJson = 0;
  Parquet = 1;
  CSV = 2;
}

message ExplainNode{
  LogicalPlanNode input = 1;
  bool verbose = 2;
}

message DfField{
  string qualifier = 2;
  Field field = 1;
}

message AggregateNode {
  LogicalPlanNode input = 1;
  repeated LogicalExprNode group_expr = 2;
  repeated LogicalExprNode aggr_expr = 3;
}

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

message JoinNode {
  LogicalPlanNode left = 1;
  LogicalPlanNode right = 2;
  JoinType join_type = 3;
  repeated string left_join_column = 4;
  repeated string right_join_column = 5;
}

message LimitNode {
  LogicalPlanNode input = 1;
  uint32 limit = 2;
}

message SelectionExecNode {
  LogicalExprNode expr = 1;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Ballista Physical Plan
///////////////////////////////////////////////////////////////////////////////////////////////////

// PhysicalPlanNode is a nested type
message PhysicalPlanNode {
  oneof PhysicalPlanType {
    ParquetScanExecNode parquet_scan = 1;
    CsvScanExecNode csv_scan = 2;
    EmptyExecNode empty = 3;
    ProjectionExecNode projection = 4;
    GlobalLimitExecNode global_limit = 6;
    LocalLimitExecNode local_limit = 7;
    HashAggregateExecNode hash_aggregate = 8;
    HashJoinExecNode hash_join = 9;
    ShuffleReaderExecNode shuffle_reader = 10;
    SortExecNode sort = 11;
    CoalesceBatchesExecNode coalesce_batches = 12;
    FilterExecNode filter = 13;
    MergeExecNode merge = 14;
    UnresolvedShuffleExecNode unresolved = 15;
    RepartitionExecNode repartition = 16;
  }
}

message UnresolvedShuffleExecNode {
  repeated uint32 query_stage_ids = 1;
  Schema schema = 2;
  uint32 partition_count = 3;
}

message FilterExecNode {
  PhysicalPlanNode input = 1;
  LogicalExprNode expr = 2;
}

message ParquetScanExecNode {
  repeated string filename = 1;
  repeated uint32 projection = 2;
  uint32 num_partitions = 3;
  uint32 batch_size = 4;
}

message CsvScanExecNode {
  string path = 1;
  repeated uint32 projection = 2;
  Schema schema = 3;
  string file_extension = 4;
  bool has_header = 5;
  uint32 batch_size = 6;
  string delimiter = 7;
  
  // partition filenames
  repeated string filename = 8;
}

message HashJoinExecNode {
  PhysicalPlanNode left = 1;
  PhysicalPlanNode right = 2;
  repeated JoinOn on = 3;
  JoinType join_type = 4;

}

message JoinOn {
   string left = 1;
   string right = 2;
}


message EmptyExecNode {
  bool produce_one_row = 1;
  Schema schema = 2;
}

message ProjectionExecNode {
  PhysicalPlanNode input = 1;
  repeated LogicalExprNode expr = 2;
  repeated string expr_name = 3;
}

enum AggregateMode {
  PARTIAL = 0;
  FINAL = 1;
}

message HashAggregateExecNode {
  repeated LogicalExprNode group_expr = 1;
  repeated LogicalExprNode aggr_expr = 2;
  AggregateMode mode = 3;
  PhysicalPlanNode input = 4;
  repeated string group_expr_name = 5;
  repeated string aggr_expr_name = 6;
  // we need the input schema to the partial aggregate to pass to the final aggregate
  Schema input_schema = 7;
}

message ShuffleReaderExecNode {
  repeated PartitionLocation partition_location = 1;
  Schema schema = 2;
}

message GlobalLimitExecNode {
  PhysicalPlanNode input = 1;
  uint32 limit = 2;
}

message LocalLimitExecNode {
  PhysicalPlanNode input = 1;
  uint32 limit = 2;
}

message SortExecNode {
  PhysicalPlanNode input = 1;
  repeated LogicalExprNode expr = 2;
}

message CoalesceBatchesExecNode {
  PhysicalPlanNode input = 1;
  uint32 target_batch_size = 2;
}

message MergeExecNode {
  PhysicalPlanNode input = 1;
}

message RepartitionExecNode{
  PhysicalPlanNode input = 1;
  oneof partition_method {
    uint64 round_robin = 2;
    HashRepartition hash = 3;
    uint64 unknown = 4;
  }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Ballista Scheduling
///////////////////////////////////////////////////////////////////////////////////////////////////

message KeyValuePair {
  string key = 1;
  string value = 2;
}

message Action {

  oneof ActionType {
    // Execute a logical query plan
    LogicalPlanNode query = 1;

    // Execute one partition of a physical query plan
    ExecutePartition execute_partition = 2;

    // Fetch a partition from an executor
    PartitionId fetch_partition = 3;
  }
  
  // configuration settings
  repeated KeyValuePair settings = 100;
}

message ExecutePartition {
  string job_id = 1;
  uint32 stage_id = 2;
  repeated uint32 partition_id = 3;
  PhysicalPlanNode plan = 4;
  // The task could need to read partitions from other executors
  repeated PartitionLocation partition_location = 5;
}

// Mapping from partition id to executor id
message PartitionLocation {
  PartitionId partition_id = 1;
  ExecutorMetadata executor_meta = 2;
  PartitionStats partition_stats = 3;
}

// Unique identifier for a materialized partition of data
message PartitionId {
  string job_id = 1;
  uint32 stage_id = 2;
  uint32 partition_id = 4;
}

message PartitionStats {
  int64 num_rows = 1;
  int64 num_batches = 2;
  int64 num_bytes = 3;
  repeated ColumnStats column_stats = 4;
}

message ColumnStats {
  ScalarValue min_value = 1;
  ScalarValue max_value = 2;
  uint32 null_count = 3;
  uint32 distinct_count = 4;
}

message ExecutorMetadata {
  string id = 1;
  string host = 2;
  uint32 port = 3;
}

message GetExecutorMetadataParams {}

message GetExecutorMetadataResult {
  repeated ExecutorMetadata metadata = 1;
}

message RunningTask {
  string executor_id = 1;
}

message FailedTask {
  string error = 1;
}

message CompletedTask {
  string executor_id = 1;
}

message TaskStatus {
  PartitionId partition_id = 1;
  oneof status {
    RunningTask running = 2;
    FailedTask failed = 3;
    CompletedTask completed = 4;
  }
}

message PollWorkParams {
  ExecutorMetadata metadata = 1;
  bool can_accept_task = 2;
  // All tasks must be reported until they reach the failed or completed state
  repeated TaskStatus task_status = 3;
}

message TaskDefinition {
  PartitionId task_id = 1;
  PhysicalPlanNode plan = 2;
}

message PollWorkResult {
  TaskDefinition task = 1;
}

message ExecuteQueryParams {
  oneof query {
    LogicalPlanNode logical_plan = 1;
    string sql = 2;
  }}

message ExecuteSqlParams {
  string sql = 1;
}

message ExecuteQueryResult {
  string job_id = 1;
}

message GetJobStatusParams {
  string job_id = 1;
}

message CompletedJob {
  repeated PartitionLocation partition_location = 1;
}

message QueuedJob {}

// TODO: add progress report
message RunningJob {}

message FailedJob {
  string error = 1;
}

message JobStatus {
  oneof status {
    QueuedJob queued = 1;
    RunningJob running = 2;
    FailedJob failed = 3;
    CompletedJob completed = 4;
  }
}

message GetJobStatusResult {
  JobStatus status = 1;
}

message GetFileMetadataParams {
  string path = 1;
  FileType file_type = 2;
}

message GetFileMetadataResult {
  Schema schema = 1;
  repeated FilePartitionMetadata partitions = 2;
}

message FilePartitionMetadata {
  repeated string filename = 1;
}

service SchedulerGrpc {
  rpc GetExecutorsMetadata (GetExecutorMetadataParams) returns (GetExecutorMetadataResult) {}

  // Executors must poll the scheduler for heartbeat and to receive tasks
  rpc PollWork (PollWorkParams) returns (PollWorkResult) {}

  rpc GetFileMetadata (GetFileMetadataParams) returns (GetFileMetadataResult) {}

  rpc ExecuteQuery (ExecuteQueryParams) returns (ExecuteQueryResult) {}

  rpc GetJobStatus (GetJobStatusParams) returns (GetJobStatusResult) {}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Arrow Data Types
///////////////////////////////////////////////////////////////////////////////////////////////////

message Schema {
  repeated Field columns = 1;
}

message Field {
  // name of the field
  string name = 1;
  ArrowType arrow_type = 2;
  bool nullable = 3;
  // for complex data types like structs, unions
  repeated Field children = 4;
}

message FixedSizeBinary{
    int32 length = 1;
}

message Timestamp{
    TimeUnit time_unit = 1;
    string timezone = 2;
}

enum DateUnit{
    Day = 0;
    DateMillisecond = 1;
}

enum TimeUnit{
    Second = 0;
    TimeMillisecond = 1;
    Microsecond = 2;
    Nanosecond = 3;
}

enum IntervalUnit{
    YearMonth = 0;
    DayTime = 1;
}

message Decimal{
    uint64 whole = 1;
    uint64 fractional = 2;
}

message List{
    Field field_type = 1;
}

message FixedSizeList{
    Field field_type = 1;
    int32 list_size = 2;
}

message Dictionary{
    ArrowType key = 1;
    ArrowType value = 2;
}

message Struct{
    repeated Field sub_field_types = 1;
}

message Union{
    repeated Field union_types = 1;
}


message ScalarListValue{
    ScalarType datatype = 1;
    repeated ScalarValue values = 2;
}



message ScalarValue{
    oneof value{
        bool   bool_value = 1;
        string utf8_value = 2;
        string large_utf8_value = 3;
        int32  int8_value = 4;
        int32  int16_value = 5;
        int32  int32_value = 6;
        int64  int64_value = 7;
        uint32 uint8_value = 8;
        uint32 uint16_value = 9;
        uint32 uint32_value = 10;
        uint64 uint64_value = 11;
        float  float32_value = 12;
        double float64_value = 13;
        //Literal Date32 value always has a unit of day
        int32  date_32_value = 14;
        int64  time_microsecond_value = 15;
        int64  time_nanosecond_value = 16;
        ScalarListValue list_value = 17;
        ScalarType null_list_value = 18;

        PrimitiveScalarType null_value = 19;
    }
}

// Contains all valid datafusion scalar type except for 
// List
enum PrimitiveScalarType{
    
    BOOL = 0;     // arrow::Type::BOOL
    UINT8 = 1;    // arrow::Type::UINT8
    INT8 = 2;     // arrow::Type::INT8
    UINT16 = 3;   // represents arrow::Type fields in src/arrow/type.h
    INT16 = 4;
    UINT32 = 5;
    INT32 = 6;
    UINT64 = 7;
    INT64 = 8;
    FLOAT32 = 9;
    FLOAT64 = 10;
    UTF8 = 11;
    LARGE_UTF8 = 12;
    DATE32 = 13;
    TIME_MICROSECOND = 14;
    TIME_NANOSECOND = 15;
    NULL = 16;
}

message ScalarType{
    oneof datatype{
        PrimitiveScalarType scalar = 1;
        ScalarListType list = 2;
    }
}

message ScalarListType{
    repeated string field_names = 3;
    PrimitiveScalarType deepest_type = 2;
}

// Broke out into multiple message types so that type 
// metadata did not need to be in separate message
//All types that are of the empty message types contain no additional metadata
// about the type
message ArrowType{
    oneof arrow_type_enum{
        EmptyMessage NONE = 1;     // arrow::Type::NA
        EmptyMessage BOOL =  2;     // arrow::Type::BOOL
        EmptyMessage UINT8 = 3;    // arrow::Type::UINT8
        EmptyMessage INT8 =  4;     // arrow::Type::INT8
        EmptyMessage UINT16 =5;   // represents arrow::Type fields in src/arrow/type.h
        EmptyMessage INT16 = 6;
        EmptyMessage UINT32 =7;
        EmptyMessage INT32 = 8;
        EmptyMessage UINT64 =9;
        EmptyMessage INT64 =10 ;
        EmptyMessage FLOAT16 =11 ;
        EmptyMessage FLOAT32 =12 ; 
        EmptyMessage FLOAT64 =13 ;
        EmptyMessage UTF8 =14 ;
        EmptyMessage LARGE_UTF8 = 32;
        EmptyMessage BINARY =15 ;
        int32 FIXED_SIZE_BINARY =16 ;
        EmptyMessage LARGE_BINARY = 31;
        EmptyMessage DATE32 =17 ;
        EmptyMessage DATE64 =18 ;
        TimeUnit DURATION = 19;
        Timestamp TIMESTAMP =20 ;
        TimeUnit TIME32 =21 ;
        TimeUnit TIME64 =22 ;
        IntervalUnit INTERVAL =23 ;
        Decimal DECIMAL =24 ;
        List LIST =25;
        List LARGE_LIST = 26;
        FixedSizeList FIXED_SIZE_LIST = 27;
        Struct STRUCT =28;
        Union UNION =29;
        Dictionary DICTIONARY =30;
    }
}





//Useful for representing an empty enum variant in rust
// E.G. enum example{One, Two(i32)}
// maps to 
// message example{
//    oneof{
//        EmptyMessage One = 1;
//        i32 Two = 2;
//   }
//}
message EmptyMessage{}
