// +build windows

package staros

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

type FileLock struct {
	filepath string
	handle   win32api.HANDLE
}

func GetFileCreationTime(fileinfo os.FileInfo) time.Time {
	d := fileinfo.Sys().(*syscall.Win32FileAttributeData)
	return time.Unix(0, d.CreationTime.Nanoseconds())
}

func GetFileAccessTime(fileinfo os.FileInfo) time.Time {
	d := fileinfo.Sys().(*syscall.Win32FileAttributeData)
	return time.Unix(0, d.LastAccessTime.Nanoseconds())
}

func SetFileTimes(file *os.File, info os.FileInfo) {

}

func SetFileTimesbyTime(file *os.File) {

}

func (f *FileLock) openFileForLock() error {
	name, err := syscall.UTF16PtrFromString(f.filepath)
	if err != nil {
		return err
	}
	handle, err := syscall.CreateFile(
		name,
		syscall.GENERIC_READ,
		syscall.FILE_SHARE_READ,
		nil,
		syscall.OPEN_ALWAYS,
		syscall.FILE_FLAG_OVERLAPPED|0x00000080,
		0)
	if err != nil {
		return err
	}
	f.handle = win32api.HANDLE(handle)
	return nil
}

func (f *FileLock) lockForTimeout(timeout time.Duration, lockType win32api.DWORD) error {
	var err error
	if err = f.openFileForLock(); err != nil {
		return err
	}
	event, err := win32api.CreateEventW(nil, true, false, nil)
	if err != nil {
		return err
	}
	myEvent := &syscall.Overlapped{HEvent: syscall.Handle(event)}
	defer syscall.CloseHandle(myEvent.HEvent)
	_, err = win32api.LockFileEx(f.handle, lockType, 0, 1, 0, myEvent)
	if err == nil {
		return nil
	}
	if err != syscall.ERROR_IO_PENDING {
		return err
	}
	millis := uint32(syscall.INFINITE)
	if timeout >= 0 {
		millis = uint32(timeout.Nanoseconds() / 1000000)
	}
	s, err := syscall.WaitForSingleObject(myEvent.HEvent, millis)
	switch s {
	case syscall.WAIT_OBJECT_0:
		// success!
		return nil
	case syscall.WAIT_TIMEOUT:
		f.Unlock()
		return ERR_TIMEOUT
	default:
		f.Unlock()
		return err
	}
}

func (f *FileLock) Lock(Exclusive bool) error {
	var lockType win32api.DWORD
	if Exclusive {
		lockType = win32api.LOCKFILE_EXCLUSIVE_LOCK
	} else {
		lockType = 0
	}
	return f.lockForTimeout(0, lockType)
}

func (f *FileLock) LockWithTimeout(tm time.Duration, Exclusive bool) error {
	var lockType win32api.DWORD
	if Exclusive {
		lockType = win32api.LOCKFILE_EXCLUSIVE_LOCK
	} else {
		lockType = 0
	}
	return f.lockForTimeout(tm, lockType)
}

func (f *FileLock) LockNoBlocking(Exclusive bool) error {
	var lockType win32api.DWORD
	if Exclusive {
		lockType = win32api.LOCKFILE_EXCLUSIVE_LOCK
	} else {
		lockType = 0
	}
	return f.lockForTimeout(0, lockType|win32api.LOCKFILE_FAIL_IMMEDIATELY)
}

func (f *FileLock) Unlock() error {
	return syscall.Close(syscall.Handle(f.handle))
}
