package physicalplan

import (
	
	
)

type planOrderingInfoState int

const (
	planOrderingInfoStateInit planOrderingInfoState = iota
	planOrderingInfoStateNewNode
	planOrderingInfoStateMaintained
	planOrderingInfoStateInvalidated
)

// planOrderingInfo is a helper struct that stores the ordering that an operator
// can expect from its input stream.
type planOrderingInfo struct {
	// state stores the ordering info state. This is a little more complex than
	// a boolean because we want a visit to a logical plan node to explicitly
	// validate the ordering to avoid programming errors in the future. This
	// requires notifying that a new node visit will occur, and invalidating the
	// ordering if a node visit does not explicitly call nodeMaintainsOrdering
	// in between node visits.
	state       planOrderingInfoState
	sortingCols []dynparquet.ColumnDefinition
	// filterCoverIdx is an index into sortingCols that specifies the first
	// column that is not "covered" by a filter. What this means is best
	// explained by an example:
	// Say that our ordering consists of columns a, b, c, d. At some point
	// during planning, we apply a filter that has binary equality expressions
	// on columns a and b. This means that a and b are covered by this filter
	// so don't really matter for ordering purposes given that all values after
	// the filter will be the same. This is helpful to operators downstream that
	// can only perform optimizations if they expect some ordering on c and d
	// only.
	filterCoverIdx int
}

// newNode should be called when visiting a new node in the logical plan.
func ( *planOrderingInfo) () {
	if .state == planOrderingInfoStateNewNode {
		// If newNode is called twice in a row, the previous node did not
		// maintain ordering, so invalidate the ordering.
		.state = planOrderingInfoStateInvalidated
		return
	}
	.state = planOrderingInfoStateNewNode
}

// nodeMaintainsOrdering should be called when a logical plan node maintains
// input ordering.
func ( *planOrderingInfo) () {
	if .state == planOrderingInfoStateNewNode {
		.state = planOrderingInfoStateMaintained
		return
	}
}

func ( *planOrderingInfo) () bool {
	return .state != planOrderingInfoStateInvalidated
}

// preExprVisitorFunc is an expr visitor that returns true on PostVisit as well.
// TODO(asubiotto): Remove this in favor of PreExprVisitorFunc, just that the
// latter aborts visitation on post-visit.
type preExprVisitorFunc func(expr logicalplan.Expr) bool

func ( preExprVisitorFunc) ( logicalplan.Expr) bool {
	return ()
}

func ( preExprVisitorFunc) ( logicalplan.Expr) bool {
	return true
}

func ( preExprVisitorFunc) ( logicalplan.Expr) bool {
	return true
}

func ( *planOrderingInfo) ( logicalplan.Expr) {
	if .state == planOrderingInfoStateInvalidated {
		return
	}
	 := make(map[string]struct{})
	.Accept(preExprVisitorFunc(func( logicalplan.Expr) bool {
		switch e := .(type) {
		case *logicalplan.BinaryExpr:
			switch .Op {
			case logicalplan.OpAnd:
				// Continue visiting.
				return true
			case logicalplan.OpEq:
				if ,  := .Left.(*logicalplan.Column);  {
					[.ColumnName] = struct{}{}
				}
			}
		}
		return true
	}))
	for ,  := range .sortingCols {
		if .Dynamic {
			// Equality filters on dynamic columns cannot be considered to
			// be covering.
			return
		}
		if ,  := [.Name]; ! {
			return
		}
		.filterCoverIdx++
	}
}

func ( *planOrderingInfo) () []dynparquet.ColumnDefinition {
	return .sortingCols[.filterCoverIdx:]
}