import db.sqlite
import rand

const (
	max_file_name_len = 256
)

fn test_vfs_register() {
	org_default_vfs := sqlite.get_default_vfs()?

	assert org_default_vfs.zName != 0

	vfs_name := 'sometest'
	mut vfs_descr := &sqlite.Sqlite3_vfs{
		zName: vfs_name.str
		iVersion: 2
	}

	if _ := sqlite.get_vfs(vfs_name) {
		panic('expected that vfs is not known')
	}

	vfs_descr.register_as_nondefault() or { panic('vfs register failed ${err}') }

	sqlite.get_vfs(vfs_name)?

	now_default_vfs := sqlite.get_default_vfs()?

	assert now_default_vfs.zName == org_default_vfs.zName

	vfs_descr.unregister() or { panic('vfs unregister failed ${err}') }

	if _ := sqlite.get_vfs(vfs_name) {
		panic('vfs supposedly unregistered yet somehow still foundable')
	}
}

// minimal vfs based on example https://www.sqlite.org/src/doc/trunk/src/test_demovfs.c
fn test_verify_vfs_is_actually_used() {
	wrapped := sqlite.get_default_vfs()?

	vfs_name := 'sometest'
	mut vfs_state := &ExampleVfsState{
		log: []string{cap: 100}
	}
	mut vfs_descr := &sqlite.Sqlite3_vfs{
		iVersion: 2
		szOsFile: int(sizeof(ExampleVfsOpenedFile))
		mxPathname: max_file_name_len
		zName: vfs_name.str
		pAppData: vfs_state
		xOpen: example_vfs_open
		xDelete: example_vfs_delete
		xAccess: example_vfs_access
		xFullPathname: example_vfs_fullpathname
		xDlOpen: wrapped.xDlOpen
		xDlError: wrapped.xDlError
		xDlSym: wrapped.xDlSym
		xDlClose: wrapped.xDlClose
		xRandomness: wrapped.xRandomness
		xSleep: wrapped.xSleep
		xCurrentTime: wrapped.xCurrentTime
		xGetLastError: example_vfs_getlasterror
		xCurrentTimeInt64: wrapped.xCurrentTimeInt64
	}

	vfs_descr.register_as_nondefault()?

	// normally this would be written to disk
	mut db := sqlite.connect_full('foo.db', [.readwrite, .create], vfs_name)!
	assert ['fullpathname from=foo.db to=foo.db}', 'open temp?=false name=foo.db', 'read file=foo.db'] == vfs_state.log
	vfs_state.log.clear()

	db.close()!
	assert ['close file=foo.db'] == vfs_state.log
}

struct ExampleVfsState {
mut:
	log []string
}

struct ExampleVfsOpenedFile {
mut:
	base      sqlite.Sqlite3_file
	name      string
	vfs_state &ExampleVfsState
}

fn to_vfsstate(t &sqlite.Sqlite3_vfs) &ExampleVfsState {
	unsafe {
		p := t.pAppData
		if p == 0 {
			assert false, 'p should not be 0'
		}
		return &ExampleVfsState(p)
	}
}

fn to_vfsopenedfile(t &sqlite.Sqlite3_file) &ExampleVfsOpenedFile {
	unsafe {
		if t == 0 {
			assert false, 't should not be 0'
		}
		return &ExampleVfsOpenedFile(t)
	}
}

fn example_vfs_fullpathname(vfs &sqlite.Sqlite3_vfs, input &char, size_of_output int, output &char) int {
	println('fullpathname called')

	mut vfs_state := to_vfsstate(vfs)

	from := unsafe { cstring_to_vstring(input) }

	unsafe {
		vmemcpy(output, input, from.len)
		output[from.len] = u8(0)
	}
	result := unsafe { cstring_to_vstring(output) }

	vfs_state.log << 'fullpathname from=${from} to=${result}}'

	return sqlite.sqlite_ok
}

fn example_vfs_access(vfs &sqlite.Sqlite3_vfs, zPath &char, flags int, pResOut &int) int {
	println('access called')
	mut vfs_state := &ExampleVfsState{}

	unsafe {
		assert 0 != vfs.pAppData
		vfs_state = &ExampleVfsState(vfs.pAppData)
	}
	vfs_state.log << 'accessed'

	return sqlite.sqlite_ok
}

fn example_vfs_open(vfs &sqlite.Sqlite3_vfs, file_name_or_null_for_tempfile &char, vfs_opened_file &sqlite.Sqlite3_file, in_flags int, out_flags &int) int {
	println('open called')

	mut is_temp := false
	mut file_name := ''

	unsafe {
		if file_name_or_null_for_tempfile == nil {
			is_temp = true
			file_name = rand.uuid_v4()
		} else {
			file_name = cstring_to_vstring(file_name_or_null_for_tempfile)
		}
	}
	mut vfs_state := to_vfsstate(vfs)

	unsafe {
		mut outp := to_vfsopenedfile(vfs_opened_file)
		outp.base.pMethods = &sqlite.Sqlite3_io_methods{
			iVersion: 1
			xClose: example_vfsfile_close
			xRead: example_vfsfile_read
			xWrite: example_vfsfile_write
			xTruncate: example_vfsfile_truncate
			xSync: example_vfsfile_sync
			xFileSize: example_vfsfile_size
			xLock: example_vfsfile_lock
			xUnlock: example_vfsfile_unlock
			xCheckReservedLock: example_vfsfile_checkreservedlock
			xFileControl: example_vfsfile_filecontrol
			xSectorSize: example_vfsfile_sectorsize
			xDeviceCharacteristics: example_vfsfile_devicecharacteristics
		}

		outp.name = file_name.clone()
		outp.vfs_state = vfs_state
	}
	vfs_state.log << 'open temp?=${is_temp} name=${file_name}'

	return sqlite.sqlite_ok
}

fn example_vfsfile_checkreservedlock(file &sqlite.Sqlite3_file, pResOut &int) int {
	println('file checkreservedlock')

	unsafe {
		*pResOut = 0
	}
	return sqlite.sqlite_ok
}

fn example_vfsfile_filecontrol(file &sqlite.Sqlite3_file, op int, arg voidptr) int {
	println('file filecontrol')

	return 0
}

fn example_vfsfile_devicecharacteristics(file &sqlite.Sqlite3_file) int {
	println('file devicecharacteristics')

	return 0
}

fn example_vfsfile_size(file &sqlite.Sqlite3_file, result &i64) int {
	println('file size')

	return sqlite.sqlite_ok
}

fn example_vfsfile_read(file &sqlite.Sqlite3_file, output voidptr, amount int, offset i64) int {
	println('file read')

	assert amount > 0

	mut vfsfile := to_vfsopenedfile(file)

	vfsfile.vfs_state.log << 'read file=${vfsfile.name}'

	unsafe {
		C.memset(output, 0, amount)
	}

	return sqlite.sqlite_ioerr_short_read
}

fn example_vfsfile_truncate(file &sqlite.Sqlite3_file, size i64) int {
	println('file truncate')

	return sqlite.sqlite_ok
}

fn example_vfsfile_sectorsize(file &sqlite.Sqlite3_file) int {
	println('file sectorsize')

	return 0
}

fn example_vfsfile_sync(file &sqlite.Sqlite3_file, flags int) int {
	println('file sync called')

	return sqlite.sqlite_ok
}

fn example_vfsfile_lock(file &sqlite.Sqlite3_file, elock int) int {
	println('file lock called')

	return sqlite.sqlite_ok
}

fn example_vfsfile_unlock(file &sqlite.Sqlite3_file, elock int) int {
	println('file unlock called')

	return sqlite.sqlite_ok
}

fn example_vfsfile_write(file &sqlite.Sqlite3_file, buf voidptr, amount int, offset i64) int {
	println('file write called')

	return sqlite.sqlite_ok
}

fn example_vfsfile_close(file &sqlite.Sqlite3_file) int {
	println('file close called')

	mut vfsfile := to_vfsopenedfile(file)

	vfsfile.vfs_state.log << 'close file=${vfsfile.name}'

	return sqlite.sqlite_ok
}

fn example_vfs_delete(vfs &sqlite.Sqlite3_vfs, name &char, sync_dir int) int {
	println('vfs delete called')

	return sqlite.sqlite_ok
}

fn example_vfs_getlasterror(vfs &sqlite.Sqlite3_vfs, i int, o &char) int {
	println('vfs getlasterror called')

	unsafe {
		*o = 0
	}
	return sqlite.sqlite_ok
}
