//go:build linux || (darwin && amd64)

package fuse

import (
	"context"
	"io"
	"syscall"

	fusefs "github.com/hanwen/go-fuse/v2/fs"
	"github.com/hanwen/go-fuse/v2/fuse"
)

// 作为本模块的FileHandle与fusefs.FileHandle的桥梁，用于实现fusefs.FileHandle接口
type fileHandle struct {
	hd FileHandle
}

func newFileHandle(hd FileHandle) *fileHandle {
	return &fileHandle{
		hd: hd,
	}
}

// Read data from a file. The data should be returned as
// ReadResult, which may be constructed from the incoming
// `dest` buffer.
func (f *fileHandle) Read(ctx context.Context, dest []byte, off int64) (res fuse.ReadResult, errno syscall.Errno) {
	var n int
	var err error
	n, err = f.hd.ReadAt(dest, off)
	if err == io.EOF {
		err = nil
	}
	return fuse.ReadResultData(dest[:n]), translateError(err)
}

var _ fusefs.FileReader = (*fileHandle)(nil)

// Write the data into the file handle at given offset. After
// returning, the data will be reused and may not referenced.
func (f *fileHandle) Write(ctx context.Context, data []byte, off int64) (written uint32, errno syscall.Errno) {
	var n int
	var err error
	n, err = f.hd.WriteAt(data, off)
	return uint32(n), translateError(err)
}

var _ fusefs.FileWriter = (*fileHandle)(nil)

// Flush is called for the close(2) call on a file descriptor. In case
// of a descriptor that was duplicated using dup(2), it may be called
// more than once for the same fileHandle.
func (f *fileHandle) Flush(ctx context.Context) syscall.Errno {
	return translateError(f.hd.Flush())
}

var _ fusefs.FileFlusher = (*fileHandle)(nil)

// Release is called to before a fileHandle is forgotten. The
// kernel ignores the return value of this method,
// so any cleanup that requires specific synchronization or
// could fail with I/O errors should happen in Flush instead.
func (f *fileHandle) Release(ctx context.Context) syscall.Errno {
	return translateError(f.hd.Close())
}

var _ fusefs.FileReleaser = (*fileHandle)(nil)

// Fsync is a signal to ensure writes to the Inode are flushed
// to stable storage.
func (f *fileHandle) Fsync(ctx context.Context, flags uint32) (errno syscall.Errno) {
	return translateError(f.hd.Sync())
}

var _ fusefs.FileFsyncer = (*fileHandle)(nil)
