package sqlite3

import (
	
	
	

	

	
)

// CreateModule registers a new virtual table module name.
// If create is nil, the virtual table is eponymous.
//
// https://sqlite.org/c3ref/create_module.html
func [ VTab]( *Conn,  string, ,  VTabConstructor[]) error {
	var  int

	const (
		     = 0x001
		   = 0x002
		     = 0x004
		     = 0x008
		  = 0x010
		     = 0x020
		         = 0x040
		 = 0x080
		  = 0x100
	)

	if  != nil {
		 |= 
	}

	 := reflect.TypeOf().Out(0)
	if implements[VTabDestroyer]() {
		 |= 
	}
	if implements[VTabUpdater]() {
		 |= 
	}
	if implements[VTabRenamer]() {
		 |= 
	}
	if implements[VTabOverloader]() {
		 |= 
	}
	if implements[VTabChecker]() {
		 |= 
	}
	if implements[VTabTxn]() {
		 |= 
	}
	if implements[VTabSavepointer]() {
		 |= 
	}
	if implements[VTabShadowTabler]() {
		 |= 
	}

	var  ptr_t
	defer .arena.mark()()
	 := .arena.string()
	if  != nil {
		 = util.AddHandle(.ctx, module[]{, })
	}
	 := res_t(.call("sqlite3_create_module_go", stk_t(.handle),
		stk_t(), stk_t(), stk_t()))
	return .error()
}

func implements[ any]( reflect.Type) bool {
	var  *
	return .Implements(reflect.TypeOf().Elem())
}

// DeclareVTab declares the schema of a virtual table.
//
// https://sqlite.org/c3ref/declare_vtab.html
func ( *Conn) ( string) error {
	if .interrupt.Err() != nil {
		return INTERRUPT
	}
	defer .arena.mark()()
	 := .arena.string()
	 := res_t(.call("sqlite3_declare_vtab", stk_t(.handle), stk_t()))
	return .error()
}

// VTabConflictMode is a virtual table conflict resolution mode.
//
// https://sqlite.org/c3ref/c_fail.html
type VTabConflictMode uint8

const (
	VTAB_ROLLBACK VTabConflictMode = 1
	VTAB_IGNORE   VTabConflictMode = 2
	VTAB_FAIL     VTabConflictMode = 3
	VTAB_ABORT    VTabConflictMode = 4
	VTAB_REPLACE  VTabConflictMode = 5
)

// VTabOnConflict determines the virtual table conflict policy.
//
// https://sqlite.org/c3ref/vtab_on_conflict.html
func ( *Conn) () VTabConflictMode {
	return VTabConflictMode(.call("sqlite3_vtab_on_conflict", stk_t(.handle)))
}

// VTabConfigOption is a virtual table configuration option.
//
// https://sqlite.org/c3ref/c_vtab_constraint_support.html
type VTabConfigOption uint8

const (
	VTAB_CONSTRAINT_SUPPORT VTabConfigOption = 1
	VTAB_INNOCUOUS          VTabConfigOption = 2
	VTAB_DIRECTONLY         VTabConfigOption = 3
	VTAB_USES_ALL_SCHEMAS   VTabConfigOption = 4
)

// VTabConfig configures various facets of the virtual table interface.
//
// https://sqlite.org/c3ref/vtab_config.html
func ( *Conn) ( VTabConfigOption,  ...any) error {
	var  int32
	if  == VTAB_CONSTRAINT_SUPPORT && len() > 0 {
		if ,  := [0].(bool);  &&  {
			 = 1
		}
	}
	 := res_t(.call("sqlite3_vtab_config_go", stk_t(.handle), stk_t(), stk_t()))
	return .error()
}

// VTabConstructor is a virtual table constructor function.
type VTabConstructor[ VTab] func(db *Conn, module, schema, table string, arg ...string) (, error)

type module[ VTab] [2]VTabConstructor[]

type vtabConstructor int

const (
	xCreate  vtabConstructor = 0
	xConnect vtabConstructor = 1
)

// A VTab describes a particular instance of the virtual table.
// A VTab may optionally implement [io.Closer] to free resources.
//
// https://sqlite.org/c3ref/vtab.html
type VTab interface {
	// https://sqlite.org/vtab.html#xbestindex
	BestIndex(*IndexInfo) error
	// https://sqlite.org/vtab.html#xopen
	Open() (VTabCursor, error)
}

// A VTabDestroyer allows a virtual table to drop persistent state.
type VTabDestroyer interface {
	VTab
	// https://sqlite.org/vtab.html#sqlite3_module.xDestroy
	Destroy() error
}

// A VTabUpdater allows a virtual table to be updated.
// Implementations must not retain arg.
type VTabUpdater interface {
	VTab
	// https://sqlite.org/vtab.html#xupdate
	Update(arg ...Value) (rowid int64, err error)
}

// A VTabRenamer allows a virtual table to be renamed.
type VTabRenamer interface {
	VTab
	// https://sqlite.org/vtab.html#xrename
	Rename(new string) error
}

// A VTabOverloader allows a virtual table to overload SQL functions.
type VTabOverloader interface {
	VTab
	// https://sqlite.org/vtab.html#xfindfunction
	FindFunction(arg int, name string) (ScalarFunction, IndexConstraintOp)
}

// A VTabShadowTabler allows a virtual table to protect the content
// of shadow tables from being corrupted by hostile SQL.
//
// Implementing this interface signals that a virtual table named
// "mumble" reserves all table names starting with "mumble_".
type VTabShadowTabler interface {
	VTab
	// https://sqlite.org/vtab.html#the_xshadowname_method
	ShadowTables()
}

// A VTabChecker allows a virtual table to report errors
// to the PRAGMA integrity_check and PRAGMA quick_check commands.
//
// Integrity should return an error if it finds problems in the content of the virtual table,
// but should avoid returning a (wrapped) [Error], [ErrorCode] or [ExtendedErrorCode],
// as those indicate the Integrity method itself encountered problems
// while trying to evaluate the virtual table content.
type VTabChecker interface {
	VTab
	// https://sqlite.org/vtab.html#xintegrity
	Integrity(schema, table string, flags int) error
}

// A VTabTxn allows a virtual table to implement
// transactions with two-phase commit.
//
// Anything that is required as part of a commit that may fail
// should be performed in the Sync() callback.
// Current versions of SQLite ignore any errors
// returned by Commit() and Rollback().
type VTabTxn interface {
	VTab
	// https://sqlite.org/vtab.html#xBegin
	Begin() error
	// https://sqlite.org/vtab.html#xsync
	Sync() error
	// https://sqlite.org/vtab.html#xcommit
	Commit() error
	// https://sqlite.org/vtab.html#xrollback
	Rollback() error
}

// A VTabSavepointer allows a virtual table to implement
// nested transactions.
//
// https://sqlite.org/vtab.html#xsavepoint
type VTabSavepointer interface {
	VTabTxn
	Savepoint(id int) error
	Release(id int) error
	RollbackTo(id int) error
}

// A VTabCursor describes cursors that point
// into the virtual table and are used
// to loop through the virtual table.
// A VTabCursor may optionally implement
// [io.Closer] to free resources.
// Implementations of Filter must not retain arg.
//
// https://sqlite.org/c3ref/vtab_cursor.html
type VTabCursor interface {
	// https://sqlite.org/vtab.html#xfilter
	Filter(idxNum int, idxStr string, arg ...Value) error
	// https://sqlite.org/vtab.html#xnext
	Next() error
	// https://sqlite.org/vtab.html#xeof
	EOF() bool
	// https://sqlite.org/vtab.html#xcolumn
	Column(ctx Context, n int) error
	// https://sqlite.org/vtab.html#xrowid
	RowID() (int64, error)
}

// An IndexInfo describes virtual table indexing information.
//
// https://sqlite.org/c3ref/index_info.html
type IndexInfo struct {
	// Inputs
	Constraint  []IndexConstraint
	OrderBy     []IndexOrderBy
	ColumnsUsed uint64
	// Outputs
	ConstraintUsage []IndexConstraintUsage
	IdxNum          int
	IdxStr          string
	IdxFlags        IndexScanFlag
	OrderByConsumed bool
	EstimatedCost   float64
	EstimatedRows   int64
	// Internal
	c      *Conn
	handle ptr_t
}

// An IndexConstraint describes virtual table indexing constraint information.
//
// https://sqlite.org/c3ref/index_info.html
type IndexConstraint struct {
	Column int
	Op     IndexConstraintOp
	Usable bool
}

// An IndexOrderBy describes virtual table indexing order by information.
//
// https://sqlite.org/c3ref/index_info.html
type IndexOrderBy struct {
	Column int
	Desc   bool
}

// An IndexConstraintUsage describes how virtual table indexing constraints will be used.
//
// https://sqlite.org/c3ref/index_info.html
type IndexConstraintUsage struct {
	ArgvIndex int
	Omit      bool
}

// RHSValue returns the value of the right-hand operand of a constraint
// if the right-hand operand is known.
//
// https://sqlite.org/c3ref/vtab_rhs_value.html
func ( *IndexInfo) ( int) (Value, error) {
	defer .c.arena.mark()()
	 := .c.arena.new(ptrlen)
	 := res_t(.c.call("sqlite3_vtab_rhs_value", stk_t(.handle),
		stk_t(), stk_t()))
	if  := .c.error();  != nil {
		return Value{}, 
	}
	return Value{
		c:      .c,
		handle: util.Read32[ptr_t](.c.mod, ),
	}, nil
}

// Collation returns the name of the collation for a virtual table constraint.
//
// https://sqlite.org/c3ref/vtab_collation.html
func ( *IndexInfo) ( int) string {
	 := ptr_t(.c.call("sqlite3_vtab_collation", stk_t(.handle),
		stk_t()))
	return util.ReadString(.c.mod, , _MAX_NAME)
}

// Distinct determines if a virtual table query is DISTINCT.
//
// https://sqlite.org/c3ref/vtab_distinct.html
func ( *IndexInfo) () int {
	 := int32(.c.call("sqlite3_vtab_distinct", stk_t(.handle)))
	return int()
}

// In identifies and handles IN constraints.
//
// https://sqlite.org/c3ref/vtab_in.html
func ( *IndexInfo) (,  int) bool {
	 := int32(.c.call("sqlite3_vtab_in", stk_t(.handle),
		stk_t(), stk_t()))
	return  != 0
}

func ( *IndexInfo) () {
	// https://sqlite.org/c3ref/index_info.html
	 := .c.mod
	 := .handle

	 := util.Read32[int32](, +0)
	.Constraint = make([]IndexConstraint, )
	.ConstraintUsage = make([]IndexConstraintUsage, )
	.OrderBy = make([]IndexOrderBy, util.Read32[int32](, +8))

	 := util.Read32[ptr_t](, +4)
	 := .Constraint
	for  := range .Constraint {
		[] = IndexConstraint{
			Column: int(util.Read32[int32](, +0)),
			Op:     util.Read[IndexConstraintOp](, +4),
			Usable: util.Read[byte](, +5) != 0,
		}
		 += 12
	}

	 := util.Read32[ptr_t](, +12)
	 := .OrderBy
	for  := range  {
		[] = IndexOrderBy{
			Column: int(util.Read32[int32](, +0)),
			Desc:   util.Read[byte](, +4) != 0,
		}
		 += 8
	}

	.EstimatedCost = util.ReadFloat64(, +40)
	.EstimatedRows = util.Read64[int64](, +48)
	.ColumnsUsed = util.Read64[uint64](, +64)
}

func ( *IndexInfo) () {
	// https://sqlite.org/c3ref/index_info.html
	 := .c.mod
	 := .handle

	 := util.Read32[ptr_t](, +16)
	for ,  := range .ConstraintUsage {
		util.Write32(, +0, int32(.ArgvIndex))
		if .Omit {
			util.Write(, +4, int8(1))
		}
		 += 8
	}

	util.Write32(, +20, int32(.IdxNum))
	if .IdxStr != "" {
		util.Write32(, +24, .c.newString(.IdxStr))
		util.WriteBool(, +28, true) // needToFreeIdxStr
	}
	if .OrderByConsumed {
		util.WriteBool(, +32, true)
	}
	util.WriteFloat64(, +40, .EstimatedCost)
	util.Write64(, +48, .EstimatedRows)
	util.Write32(, +56, .IdxFlags)
}

// IndexConstraintOp is a virtual table constraint operator code.
//
// https://sqlite.org/c3ref/c_index_constraint_eq.html
type IndexConstraintOp uint8

const (
	INDEX_CONSTRAINT_EQ        IndexConstraintOp = 2
	INDEX_CONSTRAINT_GT        IndexConstraintOp = 4
	INDEX_CONSTRAINT_LE        IndexConstraintOp = 8
	INDEX_CONSTRAINT_LT        IndexConstraintOp = 16
	INDEX_CONSTRAINT_GE        IndexConstraintOp = 32
	INDEX_CONSTRAINT_MATCH     IndexConstraintOp = 64
	INDEX_CONSTRAINT_LIKE      IndexConstraintOp = 65
	INDEX_CONSTRAINT_GLOB      IndexConstraintOp = 66
	INDEX_CONSTRAINT_REGEXP    IndexConstraintOp = 67
	INDEX_CONSTRAINT_NE        IndexConstraintOp = 68
	INDEX_CONSTRAINT_ISNOT     IndexConstraintOp = 69
	INDEX_CONSTRAINT_ISNOTNULL IndexConstraintOp = 70
	INDEX_CONSTRAINT_ISNULL    IndexConstraintOp = 71
	INDEX_CONSTRAINT_IS        IndexConstraintOp = 72
	INDEX_CONSTRAINT_LIMIT     IndexConstraintOp = 73
	INDEX_CONSTRAINT_OFFSET    IndexConstraintOp = 74
	INDEX_CONSTRAINT_FUNCTION  IndexConstraintOp = 150
)

// IndexScanFlag is a virtual table scan flag.
//
// https://sqlite.org/c3ref/c_index_scan_unique.html
type IndexScanFlag uint32

const (
	INDEX_SCAN_UNIQUE IndexScanFlag = 0x00000001
	INDEX_SCAN_HEX    IndexScanFlag = 0x00000002
)

func vtabModuleCallback( vtabConstructor) func( context.Context,  api.Module,  ptr_t,  int32, , ,  ptr_t) res_t {
	return func( context.Context,  api.Module,  ptr_t,  int32, , ,  ptr_t) res_t {
		 := make([]reflect.Value, 1+)
		[0] = reflect.ValueOf(.Value(connKey{}))

		for  := range  {
			 := util.Read32[ptr_t](, +ptr_t()*ptrlen)
			[+1] = reflect.ValueOf(util.ReadString(, , _MAX_SQL_LENGTH))
		}

		 := vtabGetHandle(, , )
		 := reflect.ValueOf().Index(int()).Call()
		,  := [1].Interface().(error)
		if  == nil {
			vtabPutHandle(, , , [0].Interface())
		}

		return vtabError(, , , _PTR_ERROR, , ERROR)
	}
}

func vtabDisconnectCallback( context.Context,  api.Module,  ptr_t) res_t {
	 := vtabDelHandle(, , )
	return vtabError(, , 0, _PTR_ERROR, , ERROR)
}

func vtabDestroyCallback( context.Context,  api.Module,  ptr_t) res_t {
	 := vtabGetHandle(, , ).(VTabDestroyer)
	 := errors.Join(.Destroy(), vtabDelHandle(, , ))
	return vtabError(, , 0, _PTR_ERROR, , ERROR)
}

func vtabBestIndexCallback( context.Context,  api.Module, ,  ptr_t) res_t {
	var  IndexInfo
	.handle = 
	.c = .Value(connKey{}).(*Conn)
	.load()

	 := vtabGetHandle(, , ).(VTab)
	 := .BestIndex(&)

	.save()
	return vtabError(, , , _VTAB_ERROR, , ERROR)
}

func vtabUpdateCallback( context.Context,  api.Module,  ptr_t,  int32, ,  ptr_t) res_t {
	 := .Value(connKey{}).(*Conn)
	 := callbackArgs(, , )
	defer returnArgs()

	 := vtabGetHandle(, , ).(VTabUpdater)
	,  := .Update(*...)
	if  == nil {
		util.Write64(, , )
	}

	return vtabError(, , , _VTAB_ERROR, , ERROR)
}

func vtabRenameCallback( context.Context,  api.Module, ,  ptr_t) res_t {
	 := vtabGetHandle(, , ).(VTabRenamer)
	 := .Rename(util.ReadString(, , _MAX_NAME))
	return vtabError(, , , _VTAB_ERROR, , ERROR)
}

func vtabFindFuncCallback( context.Context,  api.Module,  ptr_t,  int32, ,  ptr_t) int32 {
	 := vtabGetHandle(, , ).(VTabOverloader)
	,  := .FindFunction(int(), util.ReadString(, , _MAX_NAME))
	if  != 0 {
		var  ptr_t
		 = util.AddHandle(, func( Context,  ...Value) {
			defer util.DelHandle(, )
			(, ...)
		})
		util.Write32(, , )
	}
	return int32()
}

func vtabIntegrityCallback( context.Context,  api.Module, , ,  ptr_t,  uint32,  ptr_t) res_t {
	 := vtabGetHandle(, , ).(VTabChecker)
	 := util.ReadString(, , _MAX_NAME)
	 := util.ReadString(, , _MAX_NAME)
	 := .Integrity(, , int())
	// xIntegrity should return OK - even if it finds problems in the content of the virtual table.
	// https://sqlite.org/vtab.html#xintegrity
	return vtabError(, , , _PTR_ERROR, , _OK)
}

func vtabBeginCallback( context.Context,  api.Module,  ptr_t) res_t {
	 := vtabGetHandle(, , ).(VTabTxn)
	 := .Begin()
	return vtabError(, , , _VTAB_ERROR, , ERROR)
}

func vtabSyncCallback( context.Context,  api.Module,  ptr_t) res_t {
	 := vtabGetHandle(, , ).(VTabTxn)
	 := .Sync()
	return vtabError(, , , _VTAB_ERROR, , ERROR)
}

func vtabCommitCallback( context.Context,  api.Module,  ptr_t) res_t {
	 := vtabGetHandle(, , ).(VTabTxn)
	 := .Commit()
	return vtabError(, , , _VTAB_ERROR, , ERROR)
}

func vtabRollbackCallback( context.Context,  api.Module,  ptr_t) res_t {
	 := vtabGetHandle(, , ).(VTabTxn)
	 := .Rollback()
	return vtabError(, , , _VTAB_ERROR, , ERROR)
}

func vtabSavepointCallback( context.Context,  api.Module,  ptr_t,  int32) res_t {
	 := vtabGetHandle(, , ).(VTabSavepointer)
	 := .Savepoint(int())
	return vtabError(, , , _VTAB_ERROR, , ERROR)
}

func vtabReleaseCallback( context.Context,  api.Module,  ptr_t,  int32) res_t {
	 := vtabGetHandle(, , ).(VTabSavepointer)
	 := .Release(int())
	return vtabError(, , , _VTAB_ERROR, , ERROR)
}

func vtabRollbackToCallback( context.Context,  api.Module,  ptr_t,  int32) res_t {
	 := vtabGetHandle(, , ).(VTabSavepointer)
	 := .RollbackTo(int())
	return vtabError(, , , _VTAB_ERROR, , ERROR)
}

func cursorOpenCallback( context.Context,  api.Module, ,  ptr_t) res_t {
	 := vtabGetHandle(, , ).(VTab)

	,  := .Open()
	if  == nil {
		vtabPutHandle(, , , )
	}

	return vtabError(, , , _VTAB_ERROR, , ERROR)
}

func cursorCloseCallback( context.Context,  api.Module,  ptr_t) res_t {
	 := vtabDelHandle(, , )
	return vtabError(, , 0, _PTR_ERROR, , ERROR)
}

func cursorFilterCallback( context.Context,  api.Module,  ptr_t,  int32,  ptr_t,  int32,  ptr_t) res_t {
	 := .Value(connKey{}).(*Conn)
	 := callbackArgs(, , )
	defer returnArgs()

	var  string
	if  != 0 {
		 = util.ReadString(, , _MAX_LENGTH)
	}

	 := vtabGetHandle(, , ).(VTabCursor)
	 := .Filter(int(), , *...)
	return vtabError(, , , _CURSOR_ERROR, , ERROR)
}

func cursorEOFCallback( context.Context,  api.Module,  ptr_t) int32 {
	 := vtabGetHandle(, , ).(VTabCursor)
	if .EOF() {
		return 1
	}
	return 0
}

func cursorNextCallback( context.Context,  api.Module,  ptr_t) res_t {
	 := vtabGetHandle(, , ).(VTabCursor)
	 := .Next()
	return vtabError(, , , _CURSOR_ERROR, , ERROR)
}

func cursorColumnCallback( context.Context,  api.Module, ,  ptr_t,  int32) res_t {
	 := vtabGetHandle(, , ).(VTabCursor)
	 := .Value(connKey{}).(*Conn)
	 := .Column(Context{, }, int())
	return vtabError(, , , _CURSOR_ERROR, , ERROR)
}

func cursorRowIDCallback( context.Context,  api.Module, ,  ptr_t) res_t {
	 := vtabGetHandle(, , ).(VTabCursor)

	,  := .RowID()
	if  == nil {
		util.Write64(, , )
	}

	return vtabError(, , , _CURSOR_ERROR, , ERROR)
}

const (
	_PTR_ERROR = iota
	_VTAB_ERROR
	_CURSOR_ERROR
)

func vtabError( context.Context,  api.Module,  ptr_t,  uint32,  error,  ErrorCode) res_t {
	const  = 8
	,  := errorCode(, )
	if  != 0 &&  != "" {
		switch  {
		case _VTAB_ERROR:
			 =  +  // zErrMsg
		case _CURSOR_ERROR:
			 = util.Read32[ptr_t](, ) +  // pVTab->zErrMsg
		}
		 := .Value(connKey{}).(*Conn)
		if  := util.Read32[ptr_t](, );  != 0 {
			.free()
		}
		util.Write32(, , .newString())
	}
	return 
}

func vtabGetHandle( context.Context,  api.Module,  ptr_t) any {
	const  = 4
	 := util.Read32[ptr_t](, -)
	return util.GetHandle(, )
}

func vtabDelHandle( context.Context,  api.Module,  ptr_t) error {
	const  = 4
	 := util.Read32[ptr_t](, -)
	return util.DelHandle(, )
}

func vtabPutHandle( context.Context,  api.Module,  ptr_t,  any) {
	const  = 4
	 := util.AddHandle(, )
	 := util.Read32[ptr_t](, )
	util.Write32(, -, )
}