package fuse

import (
	"fmt"
	"io"
	"os"
	"syscall"
)

var _ = fmt.Println

// DataFile is for implementing read-only filesystems.  This
// assumes we already have the data in memory.
type DataFile struct {
	data []byte

	DefaultFile
}

func (me *DataFile) String() string {
	l := len(me.data)
	if l > 10 {
		l = 10
	}

	return fmt.Sprintf("DataFile(%x)", me.data[:l])
}

func (me *DataFile) GetAttr() (*Attr, Status) {
	return &Attr{Mode: S_IFREG | 0644, Size: uint64(len(me.data))}, OK
}

func NewDataFile(data []byte) *DataFile {
	f := new(DataFile)
	f.data = data
	return f
}

func (me *DataFile) Read(input *ReadIn, bp BufferPool) ([]byte, Status) {
	end := int(input.Offset) + int(input.Size)
	if end > len(me.data) {
		end = len(me.data)
	}

	return me.data[input.Offset:end], OK
}

////////////////

// DevNullFile accepts any write, and always returns EOF.
type DevNullFile struct {
	DefaultFile
}

func NewDevNullFile() *DevNullFile {
	return new(DevNullFile)
}

func (me *DevNullFile) String() string {
	return "DevNullFile"
}

func (me *DevNullFile) Read(input *ReadIn, bp BufferPool) ([]byte, Status) {
	return []byte{}, OK
}

func (me *DevNullFile) Write(input *WriteIn, content []byte) (uint32, Status) {
	return uint32(len(content)), OK
}

func (me *DevNullFile) Flush() Status {
	return OK
}

func (me *DevNullFile) Fsync(flags int) (code Status) {
	return OK
}

func (me *DevNullFile) Truncate(size uint64) (code Status) {
	return OK
}

////////////////

// LoopbackFile delegates all operations back to an underlying os.File.
type LoopbackFile struct {
	File *os.File

	DefaultFile
}

func (me *LoopbackFile) String() string {
	return fmt.Sprintf("LoopbackFile(%s)", me.File.Name())
}

func (me *LoopbackFile) Read(input *ReadIn, buffers BufferPool) ([]byte, Status) {
	slice := buffers.AllocBuffer(input.Size)

	n, err := me.File.ReadAt(slice, int64(input.Offset))
	if err == io.EOF {
		err = nil
	}
	return slice[:n], ToStatus(err)
}

func (me *LoopbackFile) Write(input *WriteIn, data []byte) (uint32, Status) {
	n, err := me.File.WriteAt(data, int64(input.Offset))
	return uint32(n), ToStatus(err)
}

func (me *LoopbackFile) Release() {
	me.File.Close()
}

func (me *LoopbackFile) Fsync(flags int) (code Status) {
	return ToStatus(syscall.Fsync(int(me.File.Fd())))
}

func (me *LoopbackFile) Truncate(size uint64) Status {
	return ToStatus(syscall.Ftruncate(int(me.File.Fd()), int64(size)))
}

// futimens missing from 6g runtime.

func (me *LoopbackFile) Chmod(mode uint32) Status {
	return ToStatus(me.File.Chmod(os.FileMode(mode)))
}

func (me *LoopbackFile) Chown(uid uint32, gid uint32) Status {
	return ToStatus(me.File.Chown(int(uid), int(gid)))
}

func (me *LoopbackFile) GetAttr() (*Attr, Status) {
	st := syscall.Stat_t{}
	err := syscall.Fstat(int(me.File.Fd()), &st)
	if err != nil {
		return nil, ToStatus(err)
	}
	a := &Attr{}
	a.FromStat(&st)
	return a, OK
}

////////////////////////////////////////////////////////////////

// ReadOnlyFile is a wrapper that denies writable operations
type ReadOnlyFile struct {
	File
}

func (me *ReadOnlyFile) String() string {
	return fmt.Sprintf("ReadOnlyFile(%s)", me.File.String())
}

func (me *ReadOnlyFile) Write(input *WriteIn, data []byte) (uint32, Status) {
	return 0, EPERM
}

func (me *ReadOnlyFile) Fsync(flag int) (code Status) {
	return OK
}

func (me *ReadOnlyFile) Truncate(size uint64) Status {
	return EPERM
}

func (me *ReadOnlyFile) Chmod(mode uint32) Status {
	return EPERM
}

func (me *ReadOnlyFile) Chown(uid uint32, gid uint32) Status {
	return EPERM
}