//+build darwin

package staros

import (
	"b612.me/stario"
	"os"
	"syscall"
	"time"
)

type FileLock struct {
	fd       int
	filepath string
}

func (f *FileLock) openFileForLock() error {
	fd, err := syscall.Open(f.filepath, syscall.O_CREAT|syscall.O_RDONLY, 0600)
	if err != nil {
		return err
	}
	f.filepath = f.filepath
	f.fd = fd
	return nil
}

func (f *FileLock) Lock(Exclusive bool) error {
	var lockType int
	if Exclusive {
		lockType = syscall.LOCK_EX
	} else {
		lockType = syscall.LOCK_SH
	}
	if err := f.openFileForLock(); err != nil {
		return err
	}
	return syscall.Flock(f.fd, lockType)
}

func (f *FileLock) LockNoBlocking(Exclusive bool) error {
	var lockType int
	if Exclusive {
		lockType = syscall.LOCK_EX
	} else {
		lockType = syscall.LOCK_SH
	}
	if err := f.openFileForLock(); err != nil {
		return err
	}
	err := syscall.Flock(f.fd, lockType|syscall.LOCK_NB)
	if err != nil {
		syscall.Close(f.fd)
		if err == syscall.EWOULDBLOCK {
			return ERR_ALREADY_LOCKED
		}
	}
	return err
}

func (f *FileLock) Unlock() error {
	err := syscall.Flock(f.fd, syscall.LOCK_UN)
	if err != nil {
		return err
	}
	return syscall.Close(f.fd)
}

func (f *FileLock) LockWithTimeout(tm time.Duration, Exclusive bool) error {
	return stario.WaitUntilTimeout(tm, func(tmout chan struct{}) error {
		err := f.Lock(Exclusive)
		select {
		case <-tmout:
			f.Unlock()
			return nil
		default:
		}
		return err
	})
}

func timespecToTime(ts syscall.Timespec) time.Time {
	return time.Unix(int64(ts.Sec), int64(ts.Nsec))
}

func GetFileCreationTime(fileinfo os.FileInfo) time.Time {
	return timespecToTime(fileinfo.Sys().(*syscall.Stat_t).Ctimespec)
}

func GetFileAccessTime(fileinfo os.FileInfo) time.Time {
	return timespecToTime(fileinfo.Sys().(*syscall.Stat_t).Atimespec)
}
