//go:build windows

// Package win_eventlog Input plugin to collect Windows Event Log messages
//
//revive:disable-next-line:var-naming
package win_eventlog

import (
	"syscall"
	"unsafe"

	"golang.org/x/sys/windows"
)

var _ unsafe.Pointer

// EvtHandle uintptr
type EvtHandle uintptr

// Do the interface allocations only once for common
// Errno values.
const (
	//revive:disable-next-line:var-naming
	errnoERROR_IO_PENDING = 997
)

var (
	//revive:disable-next-line:var-naming
	errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
)

// EvtFormatMessageFlag defines the values that specify the message string from
// the event to format.
type EvtFormatMessageFlag uint32

// EVT_FORMAT_MESSAGE_FLAGS enumeration
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa385525(v=vs.85).aspx
const (
	//revive:disable:var-naming
	// Format the event's message string.
	EvtFormatMessageEvent EvtFormatMessageFlag = iota + 1
	// Format the message string of the level specified in the event.
	EvtFormatMessageLevel
	// Format the message string of the task specified in the event.
	EvtFormatMessageTask
	// Format the message string of the task specified in the event.
	EvtFormatMessageOpcode
	// Format the message string of the keywords specified in the event. If the
	// event specifies multiple keywords, the formatted string is a list of
	// null-terminated strings. Increment through the strings until your pointer
	// points past the end of the used buffer.
	EvtFormatMessageKeyword
	//revive:enable:var-naming
)

// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
	switch e {
	case 0:
		return nil
	case errnoERROR_IO_PENDING:
		return errERROR_IO_PENDING
	}

	return e
}

var (
	modwevtapi = windows.NewLazySystemDLL("wevtapi.dll")

	procEvtSubscribe             = modwevtapi.NewProc("EvtSubscribe")
	procEvtRender                = modwevtapi.NewProc("EvtRender")
	procEvtClose                 = modwevtapi.NewProc("EvtClose")
	procEvtNext                  = modwevtapi.NewProc("EvtNext")
	procEvtFormatMessage         = modwevtapi.NewProc("EvtFormatMessage")
	procEvtOpenPublisherMetadata = modwevtapi.NewProc("EvtOpenPublisherMetadata")
	procEvtCreateBookmark        = modwevtapi.NewProc("EvtCreateBookmark")
	procEvtUpdateBookmark        = modwevtapi.NewProc("EvtUpdateBookmark")
)

func _EvtSubscribe(
	session EvtHandle,
	signalEvent uintptr,
	channelPath *uint16,
	query *uint16,
	bookmark EvtHandle,
	context uintptr,
	callback syscall.Handle,
	flags EvtSubscribeFlag,
) (handle EvtHandle, err error) {
	r0, _, e1 := syscall.Syscall9(
		procEvtSubscribe.Addr(),
		8,
		uintptr(session),
		uintptr(signalEvent),
		uintptr(unsafe.Pointer(channelPath)),
		uintptr(unsafe.Pointer(query)),
		uintptr(bookmark),
		uintptr(context),
		uintptr(callback),
		uintptr(flags),
		0,
	)
	handle = EvtHandle(r0)
	if handle == 0 {
		if e1 != 0 {
			err = errnoErr(e1)
		} else {
			err = syscall.EINVAL
		}
	}
	return
}

func _EvtRender(
	context EvtHandle,
	fragment EvtHandle,
	flags EvtRenderFlag,
	bufferSize uint32,
	buffer *byte,
	bufferUsed *uint32,
	propertyCount *uint32,
) (err error) {
	r1, _, e1 := syscall.Syscall9(
		procEvtRender.Addr(),
		7,
		uintptr(context),
		uintptr(fragment),
		uintptr(flags),
		uintptr(bufferSize),
		uintptr(unsafe.Pointer(buffer)),
		uintptr(unsafe.Pointer(bufferUsed)),
		uintptr(unsafe.Pointer(propertyCount)),
		0,
		0,
	)
	if r1 == 0 {
		if e1 != 0 {
			err = errnoErr(e1)
		} else {
			err = syscall.EINVAL
		}
	}
	return
}

func _EvtClose(object EvtHandle) (err error) {
	r1, _, e1 := syscall.Syscall(procEvtClose.Addr(), 1, uintptr(object), 0, 0)
	if r1 == 0 {
		if e1 != 0 {
			err = errnoErr(e1)
		} else {
			err = syscall.EINVAL
		}
	}
	return
}

func _EvtNext(resultSet EvtHandle, eventArraySize uint32, eventArray *EvtHandle, timeout uint32, flags uint32, numReturned *uint32) (err error) {
	r1, _, e1 := syscall.Syscall6(
		procEvtNext.Addr(),
		6,
		uintptr(resultSet),
		uintptr(eventArraySize),
		uintptr(unsafe.Pointer(eventArray)),
		uintptr(timeout),
		uintptr(flags),
		uintptr(unsafe.Pointer(numReturned)),
	)
	if r1 == 0 {
		if e1 != 0 {
			err = errnoErr(e1)
		} else {
			err = syscall.EINVAL
		}
	}
	return
}

func _EvtFormatMessage(
	publisherMetadata EvtHandle,
	event EvtHandle,
	messageID uint32,
	valueCount uint32,
	values uintptr,
	flags EvtFormatMessageFlag,
	bufferSize uint32,
	buffer *byte,
	bufferUsed *uint32,
) (err error) {
	r1, _, e1 := syscall.Syscall9(
		procEvtFormatMessage.Addr(),
		9,
		uintptr(publisherMetadata),
		uintptr(event),
		uintptr(messageID),
		uintptr(valueCount),
		uintptr(values),
		uintptr(flags),
		uintptr(bufferSize),
		uintptr(unsafe.Pointer(buffer)),
		uintptr(unsafe.Pointer(bufferUsed)),
	)
	if r1 == 0 {
		if e1 != 0 {
			err = errnoErr(e1)
		} else {
			err = syscall.EINVAL
		}
	}
	return
}

func _EvtOpenPublisherMetadata(session EvtHandle, publisherIdentity *uint16, logFilePath *uint16, locale uint32, flags uint32) (handle EvtHandle, err error) {
	r0, _, e1 := syscall.Syscall6(
		procEvtOpenPublisherMetadata.Addr(),
		5,
		uintptr(session),
		uintptr(unsafe.Pointer(publisherIdentity)),
		uintptr(unsafe.Pointer(logFilePath)),
		uintptr(locale),
		uintptr(flags),
		0,
	)
	handle = EvtHandle(r0)
	if handle == 0 {
		if e1 != 0 {
			err = errnoErr(e1)
		} else {
			err = syscall.EINVAL
		}
	}
	return
}

func _EvtCreateBookmark(bookmarkXML *uint16) (EvtHandle, error) {
	r0, _, e1 := syscall.Syscall(procEvtCreateBookmark.Addr(), 1, uintptr(unsafe.Pointer(bookmarkXML)), 0, 0)
	handle := EvtHandle(r0)
	if handle != 0 {
		return handle, nil
	}
	if e1 != 0 {
		return handle, errnoErr(e1)
	}
	return handle, syscall.EINVAL
}

func _EvtUpdateBookmark(bookmark, event EvtHandle) error {
	r0, _, e1 := syscall.Syscall(procEvtUpdateBookmark.Addr(), 2, uintptr(bookmark), uintptr(event), 0)
	if r0 != 0 {
		return nil
	}
	if e1 != 0 {
		return errnoErr(e1)
	}
	return syscall.EINVAL
}
