package logicalplan

import (
	
	
	
	

	
	
)

type Op uint32

const (
	OpUnknown Op = iota
	OpEq
	OpNotEq
	OpLt
	OpLtEq
	OpGt
	OpGtEq
	OpRegexMatch
	OpRegexNotMatch
	OpAnd
	OpOr
	OpAdd
	OpSub
	OpMul
	OpDiv
	OpContains
	OpNotContains
)

func ( Op) () string {
	switch  {
	case OpEq:
		return "=="
	case OpNotEq:
		return "!="
	case OpLt:
		return "<"
	case OpLtEq:
		return "<="
	case OpGt:
		return ">"
	case OpGtEq:
		return ">="
	case OpRegexMatch:
		return "=~"
	case OpRegexNotMatch:
		return "!~"
	case OpAnd:
		return "&&"
	case OpOr:
		return "||"
	case OpAdd:
		return "+"
	case OpSub:
		return "-"
	case OpMul:
		return "*"
	case OpDiv:
		return "/"
	case OpContains:
		return "contains"
	case OpNotContains:
		return "not contains"
	default:
		panic("unknown operator")
	}
}

func ( Op) () string {
	switch  {
	case OpEq:
		return "equal"
	case OpNotEq:
		return "not_equal"
	case OpLt:
		return "less"
	case OpLtEq:
		return "less_equal"
	case OpGt:
		return "greater"
	case OpGtEq:
		return "greater_equal"
	case OpAnd:
		return "and"
	case OpOr:
		return "or"
	case OpAdd:
		return "add"
	case OpSub:
		return "subtract"
	case OpMul:
		return "multiply"
	case OpDiv:
		return "divide"
	default:
		panic("unknown arrow operator")
	}
}

type BinaryExpr struct {
	Left  Expr
	Op    Op
	Right Expr
}

func ( *BinaryExpr) ( Expr) bool {
	if  == nil {
		// if both are nil, they are equal
		return  == nil
	}

	if ,  := .(*BinaryExpr);  {
		return .Op == .Op && .Left.Equal(.Left) && .Right.Equal(.Right)
	}

	return false
}

func ( *BinaryExpr) () Expr {
	return &BinaryExpr{
		Left:  .Left.Clone(),
		Op:    .Op,
		Right: .Right.Clone(),
	}
}

func ( *BinaryExpr) ( Visitor) bool {
	 := .PreVisit()
	if ! {
		return false
	}

	 = .Left.Accept()
	if ! {
		return false
	}

	 = .Visit()
	if ! {
		return false
	}

	 = .Right.Accept()
	if ! {
		return false
	}

	return .PostVisit()
}

func ( *BinaryExpr) ( ExprTypeFinder) (arrow.DataType, error) {
	,  := .Left.DataType()
	if  != nil {
		return nil, fmt.Errorf("left operand: %w", )
	}

	,  := .Right.DataType()
	if  != nil {
		return nil, fmt.Errorf("right operand: %w", )
	}

	if !arrow.TypeEqual(, ) {
		return nil, fmt.Errorf("left and right operands must be of the same type, got %s and %s", , )
	}

	switch .Op {
	case OpEq, OpNotEq, OpLt, OpLtEq, OpGt, OpGtEq, OpAnd, OpOr:
		return arrow.FixedWidthTypes.Boolean, nil
	case OpAdd, OpSub, OpMul, OpDiv:
		return , nil
	default:
		return nil, errors.New("unknown operator")
	}
}

func ( *BinaryExpr) () string {
	return .Left.Name() + " " + .Op.String() + " " + .Right.Name()
}

func ( *BinaryExpr) () string { return .Name() }

func ( *BinaryExpr) () []Expr {
	return append(.Left.ColumnsUsedExprs(), .Right.ColumnsUsedExprs()...)
}

func ( *BinaryExpr) ( string) bool {
	return strings.HasPrefix(.Name(), )
}

func ( *BinaryExpr) ( string) bool {
	return .Name() == 
}

func ( *BinaryExpr) () bool {
	return true
}

func ( *BinaryExpr) ( string) *AliasExpr {
	return &AliasExpr{Expr: , Alias: }
}

func ( Expr,  arrow.DataType) *ConvertExpr {
	return &ConvertExpr{Expr: , Type: }
}

type ConvertExpr struct {
	Expr Expr
	Type arrow.DataType
}

func ( *ConvertExpr) ( Expr) bool {
	if  == nil {
		// if both are nil, they are equal
		return  == nil
	}

	if ,  := .(*ConvertExpr);  {
		return arrow.TypeEqual(.Type, .Type) && .Expr.Equal(.Expr)
	}

	return false
}

func ( *ConvertExpr) () Expr {
	return &ConvertExpr{
		Expr: .Expr.Clone(),
		Type: .Type,
	}
}

func ( *ConvertExpr) ( Visitor) bool {
	 := .PreVisit()
	if ! {
		return false
	}

	 = .Expr.Accept()
	if ! {
		return false
	}

	 = .Visit()
	if ! {
		return false
	}

	return .PostVisit()
}

func ( *ConvertExpr) ( ExprTypeFinder) (arrow.DataType, error) {
	// We don't care about the result, but we want to make sure the expression
	// tree typing is correct.
	,  := .Expr.DataType()
	if  != nil {
		return nil, fmt.Errorf("convert type: %w", )
	}

	return .Type, nil
}

func ( *ConvertExpr) () string {
	return "convert(" + .Expr.Name() + ", " + .Type.String() + ")"
}

func ( *ConvertExpr) () string { return .Name() }

func ( *ConvertExpr) () []Expr {
	return .Expr.ColumnsUsedExprs()
}

func ( *ConvertExpr) ( string) bool {
	return strings.HasPrefix(.Name(), )
}

func ( *ConvertExpr) ( string) bool {
	return .Name() == 
}

func ( *ConvertExpr) () bool {
	return true
}

func ( *ConvertExpr) ( string) *AliasExpr {
	return &AliasExpr{Expr: , Alias: }
}

type Column struct {
	ColumnName string
}

func ( *Column) ( Expr) bool {
	if  == nil {
		// if both are nil, they are equal
		return  == nil
	}

	if ,  := .(*Column);  {
		return .ColumnName == .ColumnName
	}

	return false
}

func ( *Column) () Expr {
	return &Column{ColumnName: .ColumnName}
}

func ( *Column) () bool {
	return false
}

func ( *Column) ( Visitor) bool {
	 := .PreVisit()
	if ! {
		return false
	}

	return .PostVisit()
}

func ( *Column) () string {
	return .ColumnName
}

func ( *Column) () string { return .Name() }

func ( *Column) ( ExprTypeFinder) (arrow.DataType, error) {
	,  := .DataTypeForExpr()
	if  != nil {
		return nil, fmt.Errorf("column %q type: %w", .ColumnName, )
	}

	return , nil
}

func ( *Column) ( string) *AliasExpr {
	return &AliasExpr{Expr: , Alias: }
}

func ( *Column) () []Expr {
	return []Expr{}
}

func ( *Column) ( string) bool {
	return strings.HasPrefix(.Name(), )
}

func ( *Column) ( string) bool {
	return .ColumnName == 
}

func ( *Column) ( Expr) *BinaryExpr {
	return &BinaryExpr{
		Left:  ,
		Op:    OpEq,
		Right: ,
	}
}

func ( *Column) ( Expr) *BinaryExpr {
	return &BinaryExpr{
		Left:  ,
		Op:    OpNotEq,
		Right: ,
	}
}

func ( *Column) ( Expr) *BinaryExpr {
	return &BinaryExpr{
		Left:  ,
		Op:    OpGt,
		Right: ,
	}
}

func ( *Column) ( Expr) *BinaryExpr {
	return &BinaryExpr{
		Left:  ,
		Op:    OpGtEq,
		Right: ,
	}
}

func ( *Column) ( Expr) *BinaryExpr {
	return &BinaryExpr{
		Left:  ,
		Op:    OpLt,
		Right: ,
	}
}

func ( *Column) ( Expr) *BinaryExpr {
	return &BinaryExpr{
		Left:  ,
		Op:    OpLtEq,
		Right: ,
	}
}

func ( *Column) ( string) *BinaryExpr {
	return &BinaryExpr{
		Left:  ,
		Op:    OpRegexMatch,
		Right: Literal(),
	}
}

func ( *Column) ( string) *BinaryExpr {
	return &BinaryExpr{
		Left:  ,
		Op:    OpRegexNotMatch,
		Right: Literal(),
	}
}

func ( *Column) ( string) *BinaryExpr {
	return &BinaryExpr{
		Left:  ,
		Op:    OpContains,
		Right: Literal(),
	}
}

func ( *Column) ( string) *BinaryExpr {
	return &BinaryExpr{
		Left:  ,
		Op:    OpNotContains,
		Right: Literal(),
	}
}

func ( string) *Column {
	return &Column{ColumnName: }
}

func ( ...Expr) Expr {
	return and()
}

func (,  Expr) *BinaryExpr {
	return &BinaryExpr{
		Left:  ,
		Op:    OpAdd,
		Right: ,
	}
}

func (,  Expr) *BinaryExpr {
	return &BinaryExpr{
		Left:  ,
		Op:    OpSub,
		Right: ,
	}
}

func (,  Expr) *BinaryExpr {
	return &BinaryExpr{
		Left:  ,
		Op:    OpMul,
		Right: ,
	}
}

func (,  Expr) *BinaryExpr {
	return &BinaryExpr{
		Left:  ,
		Op:    OpDiv,
		Right: ,
	}
}

func and( []Expr) Expr {
	return computeBinaryExpr(, OpAnd)
}

func ( ...Expr) Expr {
	return or()
}

func or( []Expr) Expr {
	return computeBinaryExpr(, OpOr)
}

func computeBinaryExpr( []Expr,  Op) Expr {
	 := make([]Expr, 0, len())
	for ,  := range  {
		if  != nil {
			 = append(, )
		}
	}

	if len() == 0 {
		return nil
	}
	if len() == 1 {
		return [0]
	}
	if len() == 2 {
		return &BinaryExpr{
			Left:  [0],
			Op:    ,
			Right: [1],
		}
	}

	return &BinaryExpr{
		Left:  [0],
		Op:    ,
		Right: ([1:], ),
	}
}

type DynamicColumn struct {
	ColumnName string
}

func ( *DynamicColumn) ( Expr) bool {
	if  == nil {
		// if both are nil, they are equal
		return  == nil
	}

	if ,  := .(*DynamicColumn);  {
		return .ColumnName == .ColumnName
	}

	return false
}

func ( *DynamicColumn) () Expr {
	return &DynamicColumn{ColumnName: .ColumnName}
}

func ( *DynamicColumn) () bool {
	return false
}

func ( string) *DynamicColumn {
	return &DynamicColumn{ColumnName: }
}

func ( *DynamicColumn) ( ExprTypeFinder) (arrow.DataType, error) {
	,  := .DataTypeForExpr()
	if  != nil {
		return nil, fmt.Errorf("dynamic column %q type: %w", .ColumnName, )
	}

	return , nil
}

func ( *DynamicColumn) () []Expr {
	return []Expr{}
}

func ( *DynamicColumn) ( string) bool {
	return strings.HasPrefix(.Name(), )
}

func ( *DynamicColumn) ( string) bool {
	return strings.HasPrefix(, .ColumnName+".")
}

func ( *DynamicColumn) () string {
	return .ColumnName
}

func ( *DynamicColumn) () string { return .Name() }

func ( *DynamicColumn) ( Visitor) bool {
	return .PreVisit() && .PostVisit()
}

func ( ...string) []Expr {
	 := make([]Expr, len())
	for ,  := range  {
		[] = Col()
	}
	return 
}

type LiteralExpr struct {
	Value scalar.Scalar
}

func ( *LiteralExpr) ( Expr) bool {
	if  == nil {
		// if both are nil, they are equal
		return  == nil
	}

	if ,  := .(*LiteralExpr);  {
		return scalar.Equals(.Value, .Value)
	}

	return false
}

func ( *LiteralExpr) () Expr {
	return &LiteralExpr{
		Value: .Value,
	}
}

func ( *LiteralExpr) () bool {
	return false
}

func ( interface{}) *LiteralExpr {
	return &LiteralExpr{
		Value: scalar.MakeScalar(),
	}
}

func ( *LiteralExpr) ( ExprTypeFinder) (arrow.DataType, error) {
	return .Value.DataType(), nil
}

func ( *LiteralExpr) () string {
	return .Value.String()
}

func ( *LiteralExpr) () string { return .Name() }

func ( *LiteralExpr) ( Visitor) bool {
	 := .PreVisit()
	if ! {
		return false
	}

	return .PostVisit()
}

func ( *LiteralExpr) () []Expr { return nil }

func ( *LiteralExpr) ( string) bool {
	return strings.HasPrefix(.Name(), )
}

func ( *LiteralExpr) ( string) bool {
	return .Name() == 
}

type AggregationFunction struct {
	Func AggFunc
	Expr Expr
}

func ( *AggregationFunction) ( Expr) bool {
	if  == nil {
		// if both are nil, they are equal
		return  == nil
	}

	if ,  := .(*AggregationFunction);  {
		return .Func == .Func && .Expr.Equal(.Expr)
	}

	return false
}

func ( *AggregationFunction) () Expr {
	return &AggregationFunction{
		Func: .Func,
		Expr: .Expr.Clone(),
	}
}

func ( *AggregationFunction) ( ExprTypeFinder) (arrow.DataType, error) {
	return .Expr.DataType()
}

func ( *AggregationFunction) ( Visitor) bool {
	 := .PreVisit()
	if ! {
		return false
	}

	 = .Expr.Accept()
	if ! {
		return false
	}

	 = .Visit()
	if ! {
		return false
	}

	return .PostVisit()
}

func ( *AggregationFunction) () bool {
	return true
}

func ( *AggregationFunction) () string {
	return .Func.String() + "(" + .Expr.Name() + ")"
}

func ( *AggregationFunction) () string { return .Name() }

func ( *AggregationFunction) () []Expr {
	return .Expr.ColumnsUsedExprs()
}

func ( *AggregationFunction) ( string) bool {
	return .Name() == 
}

func ( *AggregationFunction) ( string) bool {
	return strings.HasPrefix(.Name(), )
}

type AggFunc uint32

const (
	AggFuncUnknown AggFunc = iota
	AggFuncSum
	AggFuncMin
	AggFuncMax
	AggFuncCount
	AggFuncAvg
	AggFuncUnique
	AggFuncAnd
)

func ( AggFunc) () string {
	switch  {
	case AggFuncSum:
		return "sum"
	case AggFuncMin:
		return "min"
	case AggFuncMax:
		return "max"
	case AggFuncCount:
		return "count"
	case AggFuncAvg:
		return "avg"
	case AggFuncUnique:
		return "unique"
	case AggFuncAnd:
		return "and"
	default:
		panic("unknown aggregation function")
	}
}

func ( Expr) *AggregationFunction {
	return &AggregationFunction{
		Func: AggFuncSum,
		Expr: ,
	}
}

func ( Expr) *AggregationFunction {
	return &AggregationFunction{
		Func: AggFuncMin,
		Expr: ,
	}
}

func ( Expr) *AggregationFunction {
	return &AggregationFunction{
		Func: AggFuncMax,
		Expr: ,
	}
}

func ( Expr) *AggregationFunction {
	return &AggregationFunction{
		Func: AggFuncCount,
		Expr: ,
	}
}

func ( Expr) *AggregationFunction {
	return &AggregationFunction{
		Func: AggFuncUnique,
		Expr: ,
	}
}

func ( Expr) *AggregationFunction {
	return &AggregationFunction{
		Func: AggFuncAnd,
		Expr: ,
	}
}

func ( Expr) *AggregationFunction {
	return &AggregationFunction{
		Func: AggFuncAvg,
		Expr: ,
	}
}

func ( Expr) *IsNullExpr {
	return &IsNullExpr{
		Expr: ,
	}
}

type IsNullExpr struct {
	Expr Expr
}

func ( *IsNullExpr) ( Expr) bool {
	if  == nil {
		// if both are nil, they are equal
		return  == nil
	}

	if ,  := .(*IsNullExpr);  {
		return .Expr.Equal(.Expr)
	}

	return false
}

func ( *IsNullExpr) () Expr {
	return &IsNullExpr{
		Expr: .Expr.Clone(),
	}
}

func ( *IsNullExpr) ( ExprTypeFinder) (arrow.DataType, error) {
	,  := .Expr.DataType()
	if  != nil {
		return nil, 
	}

	return arrow.FixedWidthTypes.Boolean, nil
}

func ( *IsNullExpr) ( Visitor) bool {
	 := .PreVisit()
	if ! {
		return false
	}

	 = .Expr.Accept()
	if ! {
		return false
	}

	 = .Visit()
	if ! {
		return false
	}

	return .PostVisit()
}

func ( *IsNullExpr) () bool {
	return true
}

func ( *IsNullExpr) () string {
	return "isnull(" + .Expr.Name() + ")"
}

func ( *IsNullExpr) () string { return .Name() }

func ( *IsNullExpr) () []Expr {
	return .Expr.ColumnsUsedExprs()
}

func ( *IsNullExpr) ( string) bool {
	return .Name() == 
}

func ( *IsNullExpr) ( string) bool {
	return strings.HasPrefix(.Name(), )
}

func (, ,  Expr) *IfExpr {
	return &IfExpr{
		Cond: ,
		Then: ,
		Else: ,
	}
}

type IfExpr struct {
	Cond Expr
	Then Expr
	Else Expr
}

func ( *IfExpr) ( Expr) bool {
	if  == nil {
		// if both are nil, they are equal
		return  == nil
	}

	if ,  := .(*IfExpr);  {
		return .Cond.Equal(.Cond) && .Then.Equal(.Then) && .Else.Equal(.Else)
	}

	return false
}

func ( *IfExpr) () Expr {
	return &IfExpr{
		Cond: .Cond.Clone(),
		Then: .Then.Clone(),
		Else: .Else.Clone(),
	}
}

func ( *IfExpr) ( ExprTypeFinder) (arrow.DataType, error) {
	,  := .Cond.DataType()
	if  != nil {
		return nil, 
	}

	if !arrow.TypeEqual(, arrow.FixedWidthTypes.Boolean) {
		return nil, fmt.Errorf("condition expression must be of type bool, got %s", )
	}

	,  := .Then.DataType()
	if  != nil {
		return nil, 
	}

	,  := .Else.DataType()
	if  != nil {
		return nil, 
	}

	if !arrow.TypeEqual(, ) {
		return nil, fmt.Errorf("then and else expression must have the same type, got %s and %s", , )
	}

	return , nil
}

func ( *IfExpr) ( Visitor) bool {
	 := .PreVisit()
	if ! {
		return false
	}

	 = .Cond.Accept()
	if ! {
		return false
	}

	 = .Then.Accept()
	if ! {
		return false
	}

	 = .Else.Accept()
	if ! {
		return false
	}

	 = .Visit()
	if ! {
		return false
	}

	return .PostVisit()
}

func ( *IfExpr) ( string) *AliasExpr {
	return &AliasExpr{
		Expr:  ,
		Alias: ,
	}
}

func ( *IfExpr) () bool {
	return true
}

func ( *IfExpr) () string {
	return "if(" + .Cond.Name() + ") { " + .Then.Name() + " } else { " + .Else.Name() + "}"
}

func ( *IfExpr) () string { return .Name() }

func ( *IfExpr) () []Expr {
	return append(append(.Cond.ColumnsUsedExprs(), .Then.ColumnsUsedExprs()...), .Else.ColumnsUsedExprs()...)
}

func ( *IfExpr) ( string) bool {
	return .Cond.MatchColumn() || .Then.MatchColumn() || .Else.MatchColumn()
}

func ( *IfExpr) ( string) bool {
	return .Cond.MatchPath() || .Then.MatchPath() || .Else.MatchPath()
}

type AliasExpr struct {
	Expr  Expr
	Alias string
}

func ( *AliasExpr) ( Expr) bool {
	if  == nil {
		// if both are nil, they are equal
		return  == nil
	}

	if ,  := .(*AliasExpr);  {
		return .Alias == .Alias && .Expr.Equal(.Expr)
	}

	return false
}

func ( *AliasExpr) () Expr {
	return &AliasExpr{
		Expr:  .Expr.Clone(),
		Alias: .Alias,
	}
}

func ( *AliasExpr) ( ExprTypeFinder) (arrow.DataType, error) {
	return .Expr.DataType()
}

func ( *AliasExpr) () string {
	return .Alias
}

func ( *AliasExpr) () string { return fmt.Sprintf("%s as %s", .Expr.String(), .Alias) }

func ( *AliasExpr) () bool {
	return .Expr.Computed()
}

func ( *AliasExpr) () []Expr {
	return .Expr.ColumnsUsedExprs()
}

func ( *AliasExpr) ( string) bool {
	return strings.HasPrefix(.Name(), )
}

func ( *AliasExpr) ( string) bool {
	return .Name() == 
}

func ( *AliasExpr) ( Visitor) bool {
	 := .PreVisit()
	if ! {
		return false
	}

	 = .Expr.Accept()
	if ! {
		return false
	}

	return .PostVisit()
}

func ( *AggregationFunction) ( string) *AliasExpr {
	return &AliasExpr{
		Expr:  ,
		Alias: ,
	}
}

func ( time.Duration) *DurationExpr {
	return &DurationExpr{duration: }
}

type DurationExpr struct {
	duration time.Duration
}

func ( *DurationExpr) ( Expr) bool {
	if  == nil {
		// if both are nil, they are equal
		return  == nil
	}

	if ,  := .(*DurationExpr);  {
		return .duration == .duration
	}

	return false
}

func ( *DurationExpr) () Expr {
	return &DurationExpr{
		duration: .duration,
	}
}

func ( *DurationExpr) ( ExprTypeFinder) (arrow.DataType, error) {
	return &arrow.DurationType{}, nil
}

func ( *DurationExpr) ( string) bool {
	return false
}

func ( *DurationExpr) ( Visitor) bool {
	 := .PreVisit()
	if ! {
		return false
	}

	return .PostVisit()
}

func ( *DurationExpr) () string {
	return fmt.Sprintf("second(%d)", int(.duration.Seconds()))
}

func ( *DurationExpr) () string { return .Name() }

func ( *DurationExpr) () []Expr {
	// DurationExpr expect to work on a timestamp column
	return []Expr{Col("timestamp")}
}

func ( *DurationExpr) ( string) bool {
	return  == "timestamp"
}

func ( *DurationExpr) () bool {
	return false
}

func ( *DurationExpr) () time.Duration {
	return .duration
}

type AllExpr struct{}

func () *AllExpr {
	return &AllExpr{}
}

func ( *AllExpr) ( Expr) bool {
	if  == nil {
		// if both are nil, they are equal
		return  == nil
	}

	,  := .(*AllExpr)
	return 
}

func ( *AllExpr) (ExprTypeFinder) (arrow.DataType, error) { return nil, nil }
func ( *AllExpr) ( Visitor) bool {
	 := .PreVisit()
	if ! {
		return false
	}

	return .PostVisit()
}
func ( *AllExpr) () string   { return "all" }
func ( *AllExpr) () string { return .Name() }
func ( *AllExpr) () []Expr {
	return []Expr{&AllExpr{}}
}
func ( *AllExpr) ( string) bool { return true }
func ( *AllExpr) ( string) bool   { return true }
func ( *AllExpr) () bool            { return false }
func ( *AllExpr) () Expr               { return &AllExpr{} }

type NotExpr struct {
	Expr Expr
}

func ( Expr) *NotExpr {
	return &NotExpr{
		Expr: ,
	}
}

func ( *NotExpr) ( Expr) bool {
	if  == nil {
		// if both are nil, they are equal
		return  == nil
	}

	if ,  := .(*NotExpr);  {
		return .Expr.Equal(.Expr)
	}

	return false
}

func ( *NotExpr) ( ExprTypeFinder) (arrow.DataType, error) {
	,  := .Expr.DataType()
	if  != nil {
		return nil, 
	}

	if !arrow.TypeEqual(, arrow.FixedWidthTypes.Boolean) {
		return nil, fmt.Errorf("not expression can only be applied to boolean expressions, got %s", )
	}

	return arrow.FixedWidthTypes.Boolean, nil
}

func ( *NotExpr) ( Visitor) bool {
	 := .PreVisit()
	if ! {
		return false
	}

	return .PostVisit()
}
func ( *NotExpr) () string   { return "not " + .Expr.Name() }
func ( *NotExpr) () string { return .Name() }
func ( *NotExpr) () []Expr {
	return []Expr{&NotExpr{Expr: .Expr}}
}
func ( *NotExpr) ( string) bool { return !.Expr.MatchColumn() }
func ( *NotExpr) ( string) bool         { return !.Expr.MatchPath() }
func ( *NotExpr) () bool                     { return false }
func ( *NotExpr) () Expr                        { return &NotExpr{Expr: .Expr} }