//go:build windows
// +build windows

package wmiext

import (
	"github.com/go-ole/go-ole"
	"github.com/sirupsen/logrus"
	"golang.org/x/sys/windows"
)

var (
	ole32                    = windows.NewLazySystemDLL("ole32.dll")
	procCoSetProxyBlanket    = ole32.NewProc("CoSetProxyBlanket")
	procCoInitializeSecurity = ole32.NewProc("CoInitializeSecurity")

	modoleaut32               = windows.NewLazySystemDLL("oleaut32.dll")
	procSafeArrayCreateVector = modoleaut32.NewProc("SafeArrayCreateVector")
	procSafeArrayPutElement   = modoleaut32.NewProc("SafeArrayPutElement")
	procSafeArrayGetElement   = modoleaut32.NewProc("SafeArrayGetElement")
	procSafeArrayDestroy      = modoleaut32.NewProc("SafeArrayDestroy")

	clsidWbemObjectTextSrc = ole.NewGUID("{8d1c559d-84f0-4bb3-a7d5-56a7435a9ba6}")
	iidIWbemObjectTextSrc  = ole.NewGUID("{bfbf883a-cad7-11d3-a11b-00105a1f515a}")

	wmiWbemTxtLocator *ole.IUnknown
	wmiWbemLocator    *ole.IUnknown

	clsidWbemLocator = ole.NewGUID("4590f811-1d3a-11d0-891f-00aa004b2e24")
	iidIWbemLocator  = ole.NewGUID("dc12a687-737f-11cf-884d-00aa004b2e24")
)

const (
	// WMI HRESULT values
	WBEM_S_NO_ERROR     = 0
	WBEM_S_FALSE        = 1
	WBEM_S_NO_MORE_DATA = 0x40005

	// WMI Generic flags
	WBEM_FLAG_RETURN_WBEM_COMPLETE = 0x0
	WBEM_FLAG_RETURN_IMMEDIATELY   = 0x10
	WBEM_FLAG_FORWARD_ONLY         = 0x20

	// WMI Query flags
	WBEM_FLAG_SHALLOW = 1

	// Timeout flags
	WBEM_NO_WAIT  = 0
	WBEM_INFINITE = 0xFFFFFFFF

	// COM Auth Flags
	EOAC_NONE = 0

	// RPC Authentication
	RPC_C_AUTHN_WINNT = 10

	// RPC Authentication Level
	RPC_C_AUTHN_LEVEL_DEFAULT = 0
	RPC_C_AUTHN_LEVEL_CALL    = 3

	// RPC Authorization
	RPC_C_AUTHZ_NONE = 0

	// RPC Impersonation
	RPC_C_IMP_LEVEL_IMPERSONATE = 3
)

func init() {
	var err error

	err = ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED)
	if err != nil {
		if oleCode, ok := err.(*ole.OleError); ok {
			code := oleCode.Code()
			// 1 = Already init
			if code != 0 && code != 1 {
				logrus.Errorf("Unable to initialize COM: %s", err.Error())
				return
			}
		}
	}

	initSecurity()

	wmiWbemLocator, err = ole.CreateInstance(clsidWbemLocator, iidIWbemLocator)
	if err != nil {
		logrus.Errorf("Could not initialize Wbem components, WMI operations will likely fail %s", err.Error())
	}

	// IID_IWbemObjectTextSrc Obtain the initial locator to WMI
	wmiWbemTxtLocator, err = ole.CreateInstance(clsidWbemObjectTextSrc, iidIWbemObjectTextSrc)
	if err != nil {
		logrus.Errorf("Could not initialize Wbem components, WMI operations will likely fail %s", err.Error())
	}
}

func initSecurity() {
	var svc int32 = -1

	res, _, _ := procCoInitializeSecurity.Call( // CoInitializeSecurity
		uintptr(0),                           // [in, optional] PSECURITY_DESCRIPTOR        pSecDesc,
		uintptr(svc),                         // [in]           LONG                        cAuthSvc,
		uintptr(0),                           // [in, optional] SOLE_AUTHENTICATION_SERVICE *asAuthSvc,
		uintptr(0),                           // [in, optional] void                        *pReserved1,
		uintptr(RPC_C_AUTHN_LEVEL_DEFAULT),   // [in]           DWORD                       dwAuthnLevel,
		uintptr(RPC_C_IMP_LEVEL_IMPERSONATE), // [in]           DWORD                       dwImpLevel,
		uintptr(0),                           // [in, optional] void                        *pAuthList,
		uintptr(EOAC_NONE),                   // [in]           DWORD                       dwCapabilities,
		uintptr(0))                           // [in, optional] void                        *pReserved3
	if res < 0 {
		logrus.Errorf("Unable to initialize COM security: %s", ole.NewError(res).Error())
	}
}
