//go:build linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock || sqlite3_dotlk

package vfs

import 

// SupportsFileLocking is false on platforms that do not support file locking.
// To open a database file on those platforms,
// you need to use the [nolock] or [immutable] URI parameters.
//
// [nolock]: https://sqlite.org/uri.html#urinolock
// [immutable]: https://sqlite.org/uri.html#uriimmutable
const SupportsFileLocking = true

const (
	_PENDING_BYTE  = 0x40000000
	_RESERVED_BYTE = (_PENDING_BYTE + 1)
	_SHARED_FIRST  = (_PENDING_BYTE + 2)
	_SHARED_SIZE   = 510
)

func ( *vfsFile) ( LockLevel) error {
	switch {
	case  != LOCK_SHARED &&  != LOCK_RESERVED &&  != LOCK_EXCLUSIVE:
		// Argument check. SQLite never explicitly requests a pending lock.
		panic(util.AssertErr())
	case .lock < LOCK_NONE || .lock > LOCK_EXCLUSIVE:
		// Connection state check.
		panic(util.AssertErr())
	case .lock == LOCK_NONE &&  > LOCK_SHARED:
		// We never move from unlocked to anything higher than a shared lock.
		panic(util.AssertErr())
	case .lock != LOCK_SHARED &&  == LOCK_RESERVED:
		// A shared lock is always held when a reserved lock is requested.
		panic(util.AssertErr())
	}

	// If we already have an equal or more restrictive lock, do nothing.
	if .lock >=  {
		return nil
	}

	// Do not allow any kind of write-lock on a read-only database.
	if  >= LOCK_RESERVED && .flags&OPEN_READONLY != 0 {
		return _IOERR_LOCK
	}

	switch  {
	case LOCK_SHARED:
		// Must be unlocked to get SHARED.
		if .lock != LOCK_NONE {
			panic(util.AssertErr())
		}
		if  := osGetSharedLock(.File);  != nil {
			return 
		}
		.lock = LOCK_SHARED
		return nil

	case LOCK_RESERVED:
		// Must be SHARED to get RESERVED.
		if .lock != LOCK_SHARED {
			panic(util.AssertErr())
		}
		if  := osGetReservedLock(.File);  != nil {
			return 
		}
		.lock = LOCK_RESERVED
		return nil

	case LOCK_EXCLUSIVE:
		// Must be SHARED, RESERVED or PENDING to get EXCLUSIVE.
		if .lock <= LOCK_NONE || .lock >= LOCK_EXCLUSIVE {
			panic(util.AssertErr())
		}
		if  := osGetExclusiveLock(.File, &.lock);  != nil {
			return 
		}
		.lock = LOCK_EXCLUSIVE
		return nil

	default:
		panic(util.AssertErr())
	}
}

func ( *vfsFile) ( LockLevel) error {
	switch {
	case  != LOCK_NONE &&  != LOCK_SHARED:
		// Argument check.
		panic(util.AssertErr())
	case .lock < LOCK_NONE || .lock > LOCK_EXCLUSIVE:
		// Connection state check.
		panic(util.AssertErr())
	}

	// If we don't have a more restrictive lock, do nothing.
	if .lock <=  {
		return nil
	}

	switch  {
	case LOCK_SHARED:
		 := osDowngradeLock(.File, .lock)
		.lock = LOCK_SHARED
		return 

	case LOCK_NONE:
		 := osReleaseLock(.File, .lock)
		.lock = LOCK_NONE
		return 

	default:
		panic(util.AssertErr())
	}
}

func ( *vfsFile) () (bool, error) {
	// Connection state check.
	if .lock < LOCK_NONE || .lock > LOCK_EXCLUSIVE {
		panic(util.AssertErr())
	}

	if .lock >= LOCK_RESERVED {
		return true, nil
	}
	return osCheckReservedLock(.File)
}