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

	
)

type FunctionRegistry interface {
	CanAddFunction(fn Function, allowOverwrite bool) bool
	AddFunction(fn Function, allowOverwrite bool) bool
	CanAddAlias(target, source string) bool
	AddAlias(target, source string) bool
	GetFunction(name string) (Function, bool)
	GetFunctionNames() []string
	NumFunctions() int

	canAddFuncName(string, bool) bool
}

var (
	registry FunctionRegistry
	once     sync.Once
)

func () FunctionRegistry {
	once.Do(func() {
		registry = NewRegistry()
		RegisterScalarCast(registry)
		RegisterVectorSelection(registry)
		RegisterScalarBoolean(registry)
		RegisterScalarArithmetic(registry)
		RegisterScalarComparisons(registry)
		RegisterVectorHash(registry)
		RegisterVectorRunEndFuncs(registry)
		RegisterScalarSetLookup(registry)
	})
	return registry
}

func () FunctionRegistry {
	return &funcRegistry{
		nameToFunction: make(map[string]Function)}
}

func ( FunctionRegistry) FunctionRegistry {
	return &funcRegistry{
		parent:         .(*funcRegistry),
		nameToFunction: make(map[string]Function)}
}

type funcRegistry struct {
	parent *funcRegistry

	mx             sync.RWMutex
	nameToFunction map[string]Function
}

func ( *funcRegistry) ( bool) sync.Locker {
	if  {
		return &.mx
	}
	return .mx.RLocker()
}

func ( *funcRegistry) ( Function,  bool) bool {
	if .parent != nil && !.parent.(, ) {
		return false
	}

	return .doAddFunction(, , false)
}

func ( *funcRegistry) ( Function,  bool) bool {
	if .parent != nil && !.parent.CanAddFunction(, ) {
		return false
	}

	return .doAddFunction(, , true)
}

func ( *funcRegistry) (,  string) bool {
	if .parent != nil && !.parent.canAddFuncName(, false) {
		return false
	}
	return .doAddAlias(, , false)
}

func ( *funcRegistry) (,  string) bool {
	if .parent != nil && !.parent.canAddFuncName(, false) {
		return false
	}

	return .doAddAlias(, , true)
}

func ( *funcRegistry) ( string) (Function, bool) {
	.mx.RLock()
	defer .mx.RUnlock()

	if ,  := .nameToFunction[];  {
		return , 
	}

	if .parent != nil {
		return .parent.()
	}

	return nil, false
}

func ( *funcRegistry) () ( []string) {
	if .parent != nil {
		 = .parent.()
	} else {
		 = make([]string, 0, len(.nameToFunction))
	}
	.mx.RLock()
	defer .mx.RUnlock()

	 = append(, slices.Collect(maps.Keys(.nameToFunction))...)
	slices.Sort()
	return
}

func ( *funcRegistry) () ( int) {
	if .parent != nil {
		 = .parent.()
	}
	.mx.RLock()
	defer .mx.RUnlock()
	return  + len(.nameToFunction)
}

func ( *funcRegistry) ( string,  bool) bool {
	if .parent != nil {
		.parent.mx.RLock()
		defer .parent.mx.RUnlock()

		if !.parent.(, ) {
			return false
		}
	}
	if ! {
		,  := .nameToFunction[]
		return !
	}
	return true
}

func ( *funcRegistry) ( Function,  bool,  bool) bool {
	debug.Assert(.Validate() == nil, "invalid function")

	 := .getLocker()
	.Lock()
	defer .Unlock()

	 := .Name()
	if !.canAddFuncName(, ) {
		return false
	}

	if  {
		.nameToFunction[] = 
	}
	return true
}

func ( *funcRegistry) (,  string,  bool) bool {
	// source name must exist in the registry or the parent
	// check outside the mutex, in case GetFunction has a mutex
	// acquisition
	,  := .GetFunction()
	if ! {
		return false
	}

	 := .getLocker()
	.Lock()
	defer .Unlock()

	if !.canAddFuncName(, false) {
		return false
	}

	if  {
		.nameToFunction[] = 
	}
	return true
}