package squashfs

import (
	"io/fs"
	"time"

	"github.com/CalebQ42/squashfs/low/directory"
	"github.com/CalebQ42/squashfs/low/inode"
)

type FileInfo struct {
	name     string
	uid      uint32
	gid      uint32
	size     int64
	target   string
	perm     uint32
	modTime  uint32
	fileType uint16
}

func (r Reader) newFileInfo(e directory.Entry) (FileInfo, error) {
	b, err := r.Low.BaseFromEntry(e)
	if err != nil {
		return FileInfo{}, err
	}
	uid, err := b.Uid(&r.Low)
	if err != nil {
		return FileInfo{}, err
	}
	gid, err := b.Gid(&r.Low)
	if err != nil {
		return FileInfo{}, err
	}
	return newFileInfo(e.Name, uid, gid, &b.Inode), nil
}

func newFileInfo(name string, uid, gid uint32, i *inode.Inode) FileInfo {
	var size int64
	var target string
	switch i.Type {
	case inode.Fil:
		size = int64(i.Data.(inode.File).Size)
	case inode.EFil:
		size = int64(i.Data.(inode.EFile).Size)
	case inode.Sym:
		target = string(i.Data.(inode.Symlink).Target)
	case inode.ESym:
		target = string(i.Data.(inode.ESymlink).Target)
	}
	return FileInfo{
		name:     name,
		uid:      uid,
		gid:      gid,
		size:     size,
		target:   target,
		perm:     uint32(i.Perm),
		modTime:  i.ModTime,
		fileType: i.Type,
	}
}

func (f FileInfo) Name() string {
	return f.name
}

func (f FileInfo) Uid() int {
	return int(f.uid)
}

func (f FileInfo) Gid() int {
	return int(f.gid)
}

func (f FileInfo) Size() int64 {
	return f.size
}

func (f FileInfo) SymlinkPath() string {
	return f.target
}

func (f FileInfo) Mode() fs.FileMode {
	switch f.fileType {
	case inode.Dir, inode.EDir:
		return fs.FileMode(f.perm | uint32(fs.ModeDir))
	case inode.Sym, inode.ESym:
		return fs.FileMode(f.perm | uint32(fs.ModeSymlink))
	case inode.Char, inode.EChar, inode.Block, inode.EBlock:
		return fs.FileMode(f.perm | uint32(fs.ModeDevice))
	case inode.Fifo, inode.EFifo:
		return fs.FileMode(f.perm | uint32(fs.ModeNamedPipe))
	case inode.Sock, inode.ESock:
		return fs.FileMode(f.perm | uint32(fs.ModeSocket))
	}
	return fs.FileMode(f.perm)
}

func (f FileInfo) ModTime() time.Time {
	return time.Unix(int64(f.modTime), 0)
}

func (f FileInfo) IsDir() bool {
	return f.fileType == inode.Dir || f.fileType == inode.EDir
}

func (f FileInfo) IsSymlink() bool {
	return f.fileType == inode.Sym || f.fileType == inode.ESym
}

func (f FileInfo) IsDevice() bool {
	return f.fileType == inode.Block || f.fileType == inode.EBlock ||
		f.fileType == inode.Char || f.fileType == inode.EChar
}

func (f FileInfo) IsFifo() bool {
	return f.fileType == inode.Fifo || f.fileType == inode.EFifo
}

func (f FileInfo) IsSocket() bool {
	return f.fileType == inode.Sock || f.fileType == inode.ESock
}

func (f FileInfo) Sys() any {
	return nil
}
