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

package fuse

import (
	"context"
	"syscall"

	fusefs "github.com/hanwen/go-fuse/v2/fs"
	"github.com/hanwen/go-fuse/v2/fuse"
	"gitlink.org.cn/cloudream/common/pkgs/logger"
)

type FileNode struct {
	NodeBase
	file FsFile
}

func newFileNode(fs *Fuse, file FsFile) *FileNode {
	return &FileNode{
		NodeBase: NodeBase{fs: fs},
		file:     file,
	}
}

func (n *FileNode) Getattr(ctx context.Context, f fusefs.FileHandle, out *fuse.AttrOut) syscall.Errno {
	logger.Tracef("FileNode.Getattr: %v", n.file.Name())

	n.fs.fillAttrOut(n.file, out)
	return 0
}

func (n *FileNode) Setattr(ctx context.Context, f fusefs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) (errno syscall.Errno) {
	log := logger.WithField("F", "FileNode.Setattr")

	n.fs.fillAttrOut(n.file, out)

	size, ok := in.GetSize()
	if ok {
		err := n.file.Truncate(size)
		if err != nil {
			log.Warnf("truncate: %v", err)
			return translateError(err)
		}
		out.Size = size
	}

	modTime, ok := in.GetMTime()
	if ok {
		err := n.file.SetModTime(modTime)
		if err != nil {
			log.Warnf("set mod time: %v", err)
			return translateError(err)
		}
		out.Mtime = uint64(modTime.Unix())
		out.Mtimensec = uint32(modTime.Nanosecond())
	}

	return 0
}

var _ = (fusefs.NodeSetattrer)((*FileNode)(nil))

func (n *FileNode) Open(ctx context.Context, flags uint32) (fh fusefs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
	log := logger.WithField("F", "FileNode.Open")
	log.Tracef("args: %v", flags)

	hd, flags, err := n.file.Open(flags)
	if err != nil {
		log.Warnf("open: %v", err)
		return nil, 0, translateError(err)
	}

	return newFileHandle(hd), flags, 0
}

var _ = (fusefs.NodeOpener)((*FileNode)(nil))

func (n *FileNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (inode *fusefs.Inode, errno syscall.Errno) {
	return nil, syscall.ENOTDIR
}

var _ = (fusefs.NodeLookuper)((*FileNode)(nil))

func (n *FileNode) Opendir(ctx context.Context) syscall.Errno {
	return syscall.ENOTDIR
}

var _ = (fusefs.NodeOpendirer)((*FileNode)(nil))

func (n *FileNode) Readdir(ctx context.Context) (ds fusefs.DirStream, errno syscall.Errno) {
	return nil, syscall.ENOTDIR
}

var _ = (fusefs.NodeReaddirer)((*FileNode)(nil))

func (n *FileNode) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (inode *fusefs.Inode, errno syscall.Errno) {
	return nil, syscall.ENOTDIR
}

var _ = (fusefs.NodeMkdirer)((*FileNode)(nil))

func (n *FileNode) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (node *fusefs.Inode, fh fusefs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
	return nil, nil, 0, syscall.ENOTDIR
}

var _ = (fusefs.NodeCreater)((*FileNode)(nil))

func (n *FileNode) Unlink(ctx context.Context, name string) (errno syscall.Errno) {
	return syscall.ENOTDIR
}

var _ = (fusefs.NodeUnlinker)((*FileNode)(nil))

func (n *FileNode) Rmdir(ctx context.Context, name string) (errno syscall.Errno) {
	return syscall.ENOTDIR
}

var _ = (fusefs.NodeRmdirer)((*FileNode)(nil))

func (n *FileNode) Rename(ctx context.Context, oldName string, newParent fusefs.InodeEmbedder, newName string, flags uint32) (errno syscall.Errno) {
	return syscall.ENOTDIR
}

var _ = (fusefs.NodeRenamer)((*FileNode)(nil))
