package sqlite3

import (
	
	
	
	
	
)

// CollationNeeded registers a callback to be invoked
// whenever an unknown collation sequence is required.
//
// https://sqlite.org/c3ref/collation_needed.html
func ( *Conn) ( func( *Conn,  string)) error {
	var  int32
	if  != nil {
		 = 1
	}
	 := res_t(.wrp.Xsqlite3_collation_needed_go(int32(.handle), ))
	if  := .error();  != nil {
		return 
	}
	.collation = 
	return nil
}

// AnyCollationNeeded uses [Conn.CollationNeeded] to register
// a fake collating function for any unknown collating sequence.
// The fake collating function works like BINARY.
//
// This can be used to load schemas that contain
// one or more unknown collating sequences.
func ( *Conn) () error {
	return .CollationNeeded(func( *Conn,  string) {
		.CreateCollation(, bytes.Compare)
	})
}

// CreateCollation defines a new collating sequence.
//
// https://sqlite.org/c3ref/create_collation.html
func ( *Conn) ( string,  CollatingFunction) error {
	var  ptr_t
	defer .arena.Mark()()
	 := .arena.String()
	if  != nil {
		 = .wrp.AddHandle()
	}
	 := res_t(.wrp.Xsqlite3_create_collation_go(
		int32(.handle), int32(), int32()))
	return .error()
}

// CollatingFunction is the type of a collation callback.
// Implementations must not retain a or b.
type CollatingFunction func(a, b []byte) int

// CreateFunction defines a new scalar SQL function.
//
// https://sqlite.org/c3ref/create_function.html
func ( *Conn) ( string,  int,  FunctionFlag,  ScalarFunction) error {
	var  ptr_t
	defer .arena.Mark()()
	 := .arena.String()
	if  != nil {
		 = .wrp.AddHandle()
	}
	 := res_t(.wrp.Xsqlite3_create_function_go(
		int32(.handle), int32(), int32(),
		int32(), int32()))
	return .error()
}

// ScalarFunction is the type of a scalar SQL function.
// Implementations must not retain arg.
type ScalarFunction func(ctx Context, arg ...Value)

// CreateAggregateFunction defines a new aggregate SQL function.
//
// https://sqlite.org/c3ref/create_function.html
func ( *Conn) ( string,  int,  FunctionFlag,  AggregateSeqFunction) error {
	var  ptr_t
	defer .arena.Mark()()
	 := .arena.String()
	if  != nil {
		 = .wrp.AddHandle(AggregateConstructor(func() AggregateFunction {
			var  aggregateFunc
			 := func( func(struct{}) bool) {
				 := func( func([]Value) bool) {
					for (.arg) {
						if !(struct{}{}) {
							break
						}
					}
				}
				(&.ctx, )
			}
			.next, .stop = iter.Pull()
			return &
		}))
	}
	 := res_t(.wrp.Xsqlite3_create_aggregate_function_go(
		int32(.handle), int32(), int32(),
		int32(), int32()))
	return .error()
}

// AggregateSeqFunction is the type of an aggregate SQL function.
// Implementations must not retain the slices yielded by seq.
type AggregateSeqFunction func(ctx *Context, seq iter.Seq[[]Value])

// CreateWindowFunction defines a new aggregate or aggregate window SQL function.
// If fn returns a [WindowFunction], an aggregate window function is created.
// If fn returns an [io.Closer], it will be called to free resources.
//
// https://sqlite.org/c3ref/create_function.html
func ( *Conn) ( string,  int,  FunctionFlag,  AggregateConstructor) error {
	var  ptr_t
	defer .arena.Mark()()
	 := .arena.String()
	if  != nil {
		 = .wrp.AddHandle(AggregateConstructor(func() AggregateFunction {
			 := ()
			if ,  := .(WindowFunction);  {
				return 
			}
			return 
		}))
	}
	 := res_t(.wrp.Xsqlite3_create_window_function_go(
		int32(.handle), int32(), int32(),
		int32(), int32()))
	return .error()
}

// AggregateConstructor is a an [AggregateFunction] constructor.
type AggregateConstructor func() AggregateFunction

// AggregateFunction is the interface an aggregate function should implement.
//
// https://sqlite.org/appfunc.html
type AggregateFunction interface {
	// Step is invoked to add a row to the current window.
	// The function arguments, if any, corresponding to the row being added, are passed to Step.
	// Implementations must not retain arg.
	Step(ctx Context, arg ...Value)

	// Value is invoked to return the current (or final) value of the aggregate.
	Value(ctx Context)
}

// WindowFunction is the interface an aggregate window function should implement.
//
// https://sqlite.org/windowfunctions.html
type WindowFunction interface {
	AggregateFunction

	// Inverse is invoked to remove the oldest presently aggregated result of Step from the current window.
	// The function arguments, if any, are those passed to Step for the row being removed.
	// Implementations must not retain arg.
	Inverse(ctx Context, arg ...Value)
}

// OverloadFunction overloads a function for a virtual table.
//
// https://sqlite.org/c3ref/overload_function.html
func ( *Conn) ( string,  int) error {
	defer .arena.Mark()()
	 := .arena.String()
	 := res_t(.wrp.Xsqlite3_overload_function(
		int32(.handle), int32(), int32()))
	return .error()
}

func ( *env) ( int32) {
	.DelHandle(ptr_t())
}

func ( *env) (, , ,  int32) {
	if ,  := .DB.(*Conn);  && .handle == ptr_t() && .collation != nil {
		 := .ReadString(ptr_t(), _MAX_NAME)
		.collation(, )
	}
}

func ( *env) (, , , ,  int32) int32 {
	 := .GetHandle(ptr_t()).(CollatingFunction)
	return int32((
		.Bytes(ptr_t(), int64()),
		.Bytes(ptr_t(), int64())))
}

func ( *env) (, , ,  int32) {
	 := .DB.(*Conn)
	 := callbackArgs(, , ptr_t())
	defer returnArgs()
	 := .wrp.GetHandle(ptr_t()).(ScalarFunction)
	(Context{, ptr_t()}, *...)
}

func ( *env) (, , , ,  int32) {
	 := .DB.(*Conn)
	 := callbackArgs(, , ptr_t())
	defer returnArgs()
	,  := callbackAggregate(, ptr_t(), ptr_t())
	.Step(Context{, ptr_t()}, *...)
}

func ( *env) (, , ,  int32) {
	 := .DB.(*Conn)
	,  := callbackAggregate(, ptr_t(), ptr_t())
	.Value(Context{, ptr_t()})

	// Cleanup.
	if  != 0 {
		var  error
		if  != 0 {
			 = .DelHandle()
		} else if ,  := .(io.Closer);  {
			 = .Close()
		}
		if  != nil {
			Context{, ptr_t()}.ResultError()
			return // notest
		}
	}
}

func ( *env) (, , ,  int32) {
	 := .DB.(*Conn)
	 := callbackArgs(, , ptr_t())
	defer returnArgs()
	 := .wrp.GetHandle(ptr_t()).(WindowFunction)
	.Inverse(Context{, ptr_t()}, *...)
}

func callbackAggregate( *Conn, ,  ptr_t) (AggregateFunction, ptr_t) {
	if  == 0 {
		 := ptr_t(.wrp.Read32())
		return .wrp.GetHandle().(AggregateFunction), 
	}

	// We need to create the aggregate.
	 := .wrp.GetHandle().(AggregateConstructor)()
	if  != 0 {
		 := .wrp.AddHandle()
		.wrp.Write32(, uint32())
		return , 
	}
	return , 0
}

var (
	valueArgsPool sync.Pool
	valueArgsLen  atomic.Int32
)

func callbackArgs( *Conn,  int32,  ptr_t) *[]Value {
	,  := valueArgsPool.Get().(*[]Value)
	if ! || cap(*) < int() {
		 := valueArgsLen.Or() | 
		 := make([]Value, )
		 = &
	}
	 := (*)[:]
	for  := range  {
		[] = Value{
			c:      ,
			handle: ptr_t(.wrp.Read32( + ptr_t()*ptrlen)),
		}
	}
	* = 
	return 
}

func returnArgs( *[]Value) {
	valueArgsPool.Put()
}

type aggregateFunc struct {
	next func() (struct{}, bool)
	stop func()
	ctx  Context
	arg  []Value
}

func ( *aggregateFunc) ( Context,  ...Value) {
	.ctx = 
	.arg = append(.arg[:0], ...)
	if ,  := .next(); ! {
		.stop()
	}
}

func ( *aggregateFunc) ( Context) {
	.ctx = 
	.stop()
}

func ( *aggregateFunc) () error {
	.stop()
	return nil
}