// The fuse package provides APIs to implement filesystems in
// userspace.  Typically, each call of the API happens in its own
// goroutine, so take care to make the file system thread-safe.

package fuse

import (
	"github.com/hanwen/go-fuse/raw"
)

// Types for users to implement.

// The result of Read is an array of bytes, but for performance
// reasons, we can also return data as a file-descriptor/offset/size
// tuple.  If the backing store for a file is another filesystem, this
// reduces the amount of copying between the kernel and the FUSE
// server.  The ReadResult interface captures both cases.
type ReadResult interface {
	// Returns the raw bytes for the read, possibly using the
	// passed buffer. The buffer should be larger than the return
	// value from Size.
	Bytes(buf []byte) ([]byte, Status)

	// Size returns how many bytes this return value takes at most.
	Size() int

	// Done() is called after sending the data to the kernel.
	Done()
}

type MountOptions struct {
	AllowOther bool

	// Options are passed as -o string to fusermount.
	Options []string

	// Default is _DEFAULT_BACKGROUND_TASKS, 12.  This numbers
	// controls the allowed number of requests that relate to
	// async I/O.  Concurrency for synchronous I/O is not limited.
	MaxBackground int

	// Write size to use.  If 0, use default. This number is
	// capped at the kernel maximum.
	MaxWrite int

	// If IgnoreSecurityLabels is set, all security related xattr
	// requests will return NO_DATA without passing through the
	// user defined filesystem.  You should only set this if you
	// file system implements extended attributes, and you are not
	// interested in security labels.
	IgnoreSecurityLabels bool // ignoring labels should be provided as a fusermount mount option.

	// If given, use this buffer pool instead of the global one.
	Buffers BufferPool

	// If RememberInodes is set, we will never forget inodes.
	// This may be useful for NFS.
	RememberInodes bool

	// The name will show up on the output of the mount. Keep this string
	// small.
	Name string

	// If set, wrap the file system in a single-threaded wrapper.
	SingleThreaded bool
}

// RawFileSystem is an interface close to the FUSE wire protocol.
//
// Unless you really know what you are doing, you should not implement
// this, but rather the FileSystem interface; the details of getting
// interactions with open files, renames, and threading right etc. are
// somewhat tricky and not very interesting.
//
// A null implementation is provided by NewDefaultRawFileSystem.
type RawFileSystem interface {
	String() string

	// If called, provide debug output through the log package.
	SetDebug(debug bool)

	Lookup(out *raw.EntryOut, context *Context, name string) (status Status)
	Forget(nodeid, nlookup uint64)

	// Attributes.
	GetAttr(out *raw.AttrOut, context *Context, input *raw.GetAttrIn) (code Status)
	SetAttr(out *raw.AttrOut, context *Context, input *raw.SetAttrIn) (code Status)

	// Modifying structure.
	Mknod(out *raw.EntryOut, context *Context, input *raw.MknodIn, name string) (code Status)
	Mkdir(out *raw.EntryOut, context *Context, input *raw.MkdirIn, name string) (code Status)
	Unlink(context *Context, name string) (code Status)
	Rmdir(context *Context, name string) (code Status)
	Rename(context *Context, input *raw.RenameIn, oldName string, newName string) (code Status)
	Link(out *raw.EntryOut, context *Context, input *raw.LinkIn, filename string) (code Status)

	Symlink(out *raw.EntryOut, context *Context, pointedTo string, linkName string) (code Status)
	Readlink(context *Context) (out []byte, code Status)
	Access(context *Context, input *raw.AccessIn) (code Status)

	// Extended attributes.
	GetXAttrSize(context *Context, attr string) (sz int, code Status)
	GetXAttrData(context *Context, attr string) (data []byte, code Status)
	ListXAttr(context *Context) (attributes []byte, code Status)
	SetXAttr(context *Context, input *raw.SetXAttrIn, attr string, data []byte) Status
	RemoveXAttr(context *Context, attr string) (code Status)

	// File handling.
	Create(out *raw.CreateOut, context *Context, input *raw.CreateIn, name string) (code Status)
	Open(out *raw.OpenOut, context *Context, input *raw.OpenIn) (status Status)
	Read(*Context, *raw.ReadIn, []byte) (ReadResult, Status)

	Release(context *Context, input *raw.ReleaseIn)
	Write(*Context, *raw.WriteIn, []byte) (written uint32, code Status)
	Flush(context *Context, input *raw.FlushIn) Status
	Fsync(*Context, *raw.FsyncIn) (code Status)
	Fallocate(Context *Context, in *raw.FallocateIn) (code Status)

	// Directory handling
	OpenDir(out *raw.OpenOut, context *Context, input *raw.OpenIn) (status Status)
	ReadDir(out *DirEntryList, context *Context, input *raw.ReadIn) Status
	ReleaseDir(context *Context, input *raw.ReleaseIn)
	FsyncDir(context *Context, input *raw.FsyncIn) (code Status)

	//
	StatFs(out *raw.StatfsOut, context *Context) (code Status)

	// Provide callbacks for pushing notifications to the kernel.
	Init(params *RawFsInit)
}

// Talk back to FUSE.
//
// InodeNotify invalidates the information associated with the inode
// (ie. data cache, attributes, etc.)
//
// EntryNotify should be used if the existence status of an entry changes,
// (ie. to notify of creation or deletion of the file).
//
// Somewhat confusingly, InodeNotify for a file that stopped to exist
// will give the correct result for Lstat (ENOENT), but the kernel
// will still issue file Open() on the inode.
type RawFsInit struct {
	InodeNotify  func(*raw.NotifyInvalInodeOut) Status
	EntryNotify  func(parent uint64, name string) Status
	DeleteNotify func(parent uint64, child uint64, name string) Status
}