// 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
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// 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.

//go:build go1.18

package compute

import (
	
	

	
	
	
)

func haveChunkedArray( []Datum) bool {
	for ,  := range  {
		if .Kind() == KindChunked {
			return true
		}
	}
	return false
}

// ExecSpanFromBatch constructs and returns a new ExecSpan from the values
// inside of the ExecBatch which could be scalar or arrays.
//
// This is mostly used for tests but is also a convenience method for other
// cases.
func ( *ExecBatch) *exec.ExecSpan {
	 := &exec.ExecSpan{Len: .Len, Values: make([]exec.ExecValue, len(.Values))}
	for ,  := range .Values {
		 := &.Values[]
		if .Kind() == KindScalar {
			.Scalar = .(*ScalarDatum).Value
		} else {
			.Array.SetMembers(.(*ArrayDatum).Value)
			.Scalar = nil
		}
	}
	return 
}

// this is the primary driver of execution
func execInternal( context.Context,  Function,  FunctionOptions,  int64,  ...Datum) ( Datum,  error) {
	if  == nil {
		if  = checkOptions(, );  != nil {
			return
		}
		 = .DefaultOptions()
	}

	// we only allow Array, ChunkedArray, and Scalars for now.
	// RecordBatch and Table datums are disallowed.
	if  = checkAllIsValue();  != nil {
		return
	}

	 := make([]arrow.DataType, len())
	for ,  := range  {
		[] = .(ArrayLikeDatum).Type()
	}

	var (
		        exec.Kernel
		 KernelExecutor
	)

	switch .Kind() {
	case FuncScalar:
		 = scalarExecPool.Get().(*scalarExecutor)
		defer func() {
			.Clear()
			scalarExecPool.Put(.(*scalarExecutor))
		}()
	case FuncVector:
		 = vectorExecPool.Get().(*vectorExecutor)
		defer func() {
			.Clear()
			vectorExecPool.Put(.(*vectorExecutor))
		}()
	default:
		return nil, fmt.Errorf("%w: direct execution of %s", arrow.ErrNotImplemented, .Kind())
	}

	if ,  = .DispatchBest(...);  != nil {
		return
	}

	var  []Datum
	// cast arguments if necessary
	for ,  := range  {
		if !arrow.TypeEqual([], .(ArrayLikeDatum).Type()) {
			if  == nil {
				 = make([]Datum, len())
				copy(, )
			}
			[],  = CastDatum(, , SafeCastOptions([]))
			if  != nil {
				return nil, 
			}
			defer [].Release()
		}
	}
	if  != nil {
		 = 
	}

	 := &exec.KernelCtx{Ctx: , Kernel: }
	 := .GetInitFn()
	 := exec.KernelInitArgs{Kernel: , Inputs: , Options: }
	if  != nil {
		.State,  = (, )
		if  != nil {
			return
		}
	}

	if  = .Init(, );  != nil {
		return
	}

	 := ExecBatch{Values: , Len: 0}
	if .NumValues() == 0 {
		if  != -1 {
			.Len = 
		}
	} else {
		,  := inferBatchLength(.Values)
		.Len = 
		switch .Kind() {
		case FuncScalar:
			if  != -1 &&  !=  {
				return nil, fmt.Errorf("%w: passed batch length for execution did not match actual length for scalar fn execution",
					arrow.ErrInvalid)
			}
		case FuncVector:
			 := .(*exec.VectorKernel)
			if ! && .CanExecuteChunkWise {
				return nil, fmt.Errorf("%w: vector kernel arguments must all be the same length", arrow.ErrInvalid)
			}
		}
	}

	 := GetExecCtx()

	,  := context.WithCancel(context.Background())
	defer ()

	 := make(chan Datum, .ExecChannelSize)
	go func() {
		defer close()
		if  = .Execute(, &, );  != nil {
			()
		}
	}()

	 = .WrapResults(, , haveChunkedArray(.Values))
	if  == nil {
		debug.Assert(.CheckResultType() == nil, "invalid result type")
	}

	if .Err() == context.Canceled &&  != nil {
		.Release()
	}

	return
}

// CallFunction is a one-shot invoker for all types of functions.
//
// It will perform kernel-dispatch, argument checking, iteration of
// ChunkedArray inputs and wrapping of outputs.
//
// To affect the execution options, you must call SetExecCtx and pass
// the resulting context in here.
func ( context.Context,  string,  FunctionOptions,  ...Datum) (Datum, error) {
	 := GetExecCtx()
	,  := .Registry.GetFunction()
	if ! {
		return nil, fmt.Errorf("%w: function '%s' not found", arrow.ErrKey, )
	}

	return .Execute(, , ...)
}