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

option java_multiple_files = true;
option java_package = "org.datafusioncompute.protobuf";
option java_outer_classname = "DatafusionProto";

message ColumnRelation {
  string relation = 1;
}

message Column {
  string name = 1;
  ColumnRelation relation = 2;
}

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

message DfSchema {
  repeated DfField columns = 1;
}

// logical expressions
message LogicalExprNode {
  oneof ExprType {
    // column references
    Column column = 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;

    // window expressions
    WindowExprNode window_expr = 18;
  }
}

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 {
  Abs=0;
  Acos=1;
  Asin=2;
  Atan=3;
  Ascii=4;
  Ceil=5;
  Cos=6;
  Digest=7;
  Exp=8;
  Floor=9;
  Ln=10;
  Log=11;
  Log10=12;
  Log2=13;
  Round=14;
  Signum=15;
  Sin=16;
  Sqrt=17;
  Tan=18;
  Trunc=19;
  Array=20;
  RegexpMatch=21;
  BitLength=22;
  Btrim=23;
  CharacterLength=24;
  Chr=25;
  Concat=26;
  ConcatWithSeparator=27;
  DatePart=28;
  DateTrunc=29;
  InitCap=30;
  Left=31;
  Lpad=32;
  Lower=33;
  Ltrim=34;
  MD5=35;
  NullIf=36;
  OctetLength=37;
  Random=38;
  RegexpReplace=39;
  Repeat=40;
  Replace=41;
  Reverse=42;
  Right=43;
  Rpad=44;
  Rtrim=45;
  SHA224=46;
  SHA256=47;
  SHA384=48;
  SHA512=49;
  SplitPart=50;
  StartsWith=51;
  Strpos=52;
  Substr=53;
  ToHex=54;
  ToTimestamp=55;
  ToTimestampMillis=56;
  ToTimestampMicros=57;
  ToTimestampSeconds=58;
  Now=59;
  Translate=60;
  Trim=61;
  Upper=62;
  Coalesce=63;
}

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

enum AggregateFunction {
  MIN = 0;
  MAX = 1;
  SUM = 2;
  AVG = 3;
  COUNT = 4;
  APPROX_DISTINCT = 5;
  ARRAY_AGG = 6;
  VARIANCE=7;
  VARIANCE_POP=8;
  COVARIANCE=9;
  COVARIANCE_POP=10;
  STDDEV=11;
  STDDEV_POP=12;
  CORRELATION=13;
  APPROX_PERCENTILE_CONT = 14;
  APPROX_MEDIAN=15;
  APPROX_PERCENTILE_CONT_WITH_WEIGHT = 16;
}

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

enum BuiltInWindowFunction {
  ROW_NUMBER = 0;
  RANK = 1;
  DENSE_RANK = 2;
  PERCENT_RANK = 3;
  CUME_DIST = 4;
  NTILE = 5;
  LAG = 6;
  LEAD = 7;
  FIRST_VALUE = 8;
  LAST_VALUE = 9;
  NTH_VALUE = 10;
}

message WindowExprNode {
  oneof window_function {
    AggregateFunction aggr_function = 1;
    BuiltInWindowFunction built_in_function = 2;
    // udaf = 3
  }
  LogicalExprNode expr = 4;
  repeated LogicalExprNode partition_by = 5;
  repeated LogicalExprNode order_by = 6;
  // repeated LogicalExprNode filter = 7;
  oneof window_frame {
    WindowFrame frame = 8;
  }
}

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

enum WindowFrameUnits {
  ROWS = 0;
  RANGE = 1;
  GROUPS = 2;
}

message WindowFrame {
  WindowFrameUnits window_frame_units = 1;
  WindowFrameBound start_bound = 2;
  // "optional" keyword is stable in protoc 3.15 but prost is still on 3.14 (see https://github.com/tokio-rs/prost/issues/430 and https://github.com/tokio-rs/prost/pull/455)
  // this syntax is ugly but is binary compatible with the "optional" keyword (see https://stackoverflow.com/questions/42622015/how-to-define-an-optional-field-in-protobuf-3)
  oneof end_bound {
    WindowFrameBound bound = 3;
  }
}

enum WindowFrameBoundType {
  CURRENT_ROW = 0;
  PRECEDING = 1;
  FOLLOWING = 2;
}

message WindowFrameBound {
  WindowFrameBoundType window_frame_bound_type = 1;
  // "optional" keyword is stable in protoc 3.15 but prost is still on 3.14 (see https://github.com/tokio-rs/prost/issues/430 and https://github.com/tokio-rs/prost/pull/455)
  // this syntax is ugly but is binary compatible with the "optional" keyword (see https://stackoverflow.com/questions/42622015/how-to-define-an-optional-field-in-protobuf-3)
  oneof bound_value {
    uint64 value = 2;
  }
}

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

message Schema {
  repeated datafusion.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;
    MonthDayNano = 2;
}

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

enum UnionMode{
  sparse = 0;
  dense = 1;
}

message Union{
    repeated Field union_types = 1;
    UnionMode union_mode = 2;
}

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;
        Decimal128 decimal128_value = 20;
        int64 date_64_value = 21;
        int64 time_second_value = 22;
        int64 time_millisecond_value = 23;
        int32 interval_yearmonth_value = 24;
        int64 interval_daytime_value = 25;
    }
}

message Decimal128{
  bytes value = 1;
  int64 p = 2;
  int64 s = 3;
}

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

    DECIMAL128 = 17;
    DATE64 = 20;
    TIME_SECOND = 21;
    TIME_MILLISECOND = 22;
    INTERVAL_YEARMONTH = 23;
    INTERVAL_DAYTIME = 24;
}

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

message OptimizedLogicalPlanType {
  string optimizer_name = 1;
}

message OptimizedPhysicalPlanType {
  string optimizer_name = 1;
}

message PlanType {
  oneof plan_type_enum {
    EmptyMessage InitialLogicalPlan = 1;
    OptimizedLogicalPlanType OptimizedLogicalPlan = 2;
    EmptyMessage FinalLogicalPlan = 3;
    EmptyMessage InitialPhysicalPlan = 4;
    OptimizedPhysicalPlanType OptimizedPhysicalPlan = 5;
    EmptyMessage FinalPhysicalPlan = 6;
  }
}

message StringifiedPlan {
  PlanType plan_type = 1;
  string plan = 2;
}
