package win32api

import (
	"errors"
	"syscall"
	"unsafe"

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

func DuplicateTokenEx(hExistingToken HANDLE, dwDesiredAccess DWORD,
	lpTokenAttributes uintptr, ImpersonationLevel int,
	TokenType TOKEN_TYPE, phNewToken *TOKEN) error {

	advapi32, err := syscall.LoadLibrary("advapi32.dll")
	if err != nil {
		return errors.New("Can't Load Advapi32 API")
	}
	defer syscall.FreeLibrary(advapi32)
	Dup, err := syscall.GetProcAddress(syscall.Handle(advapi32), "DuplicateTokenEx")
	if err != nil {
		return errors.New("Can't Load WTSQueryUserToken API")
	}
	r, _, errno := syscall.Syscall6(uintptr(Dup), 6, uintptr(hExistingToken), uintptr(dwDesiredAccess), lpTokenAttributes, uintptr(ImpersonationLevel),
		uintptr(TokenType), uintptr(unsafe.Pointer(phNewToken)))
	if r == 0 {
		return error(errno)
	}
	return nil
}

func CreateProcessAsUser(hToken TOKEN, lpApplicationName, lpCommandLine string,
	lpProcessAttributes, lpThreadAttributes, bInheritHandles uintptr,
	dwCreationFlags uint16, lpEnvironment HANDLE, lpCurrentDirectory string,
	lpStartupInfo *StartupInfo, lpProcessInformation *ProcessInformation) error {
	var (
		commandLine uintptr = 0
		workingDir  uintptr = 0
	)
	advapi32, err := syscall.LoadLibrary("advapi32.dll")
	if err != nil {
		return errors.New("Can't Load Advapi32 API")
	}
	defer syscall.FreeLibrary(advapi32)
	CPAU, err := syscall.GetProcAddress(syscall.Handle(advapi32), "CreateProcessAsUserW")
	if err != nil {
		return errors.New("Can't Load CreateProcessAsUserW API")
	}
	if len(lpCommandLine) > 0 {
		commandLine = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(lpCommandLine)))
	}
	if len(lpCurrentDirectory) > 0 {
		workingDir = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(lpCurrentDirectory)))
	}
	r, _, errno := syscall.Syscall12(uintptr(CPAU), 11, uintptr(hToken), uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(lpApplicationName))),
		commandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, uintptr(dwCreationFlags), uintptr(lpEnvironment),
		workingDir, uintptr(unsafe.Pointer(lpStartupInfo)), uintptr(unsafe.Pointer(lpProcessInformation)), 0)
	if r == 0 {
		return error(errno)
	}
	return nil
}
func GetTokenInformation(TokenHandle HANDLE, TokenInformationClass, TokenInformation,
	TokenInformationLength uintptr, ReturnLength *uintptr) error {
	advapi32, err := syscall.LoadLibrary("advapi32.dll")
	if err != nil {
		return errors.New("Can't Load Advapi32 API")
	}
	defer syscall.FreeLibrary(advapi32)
	GTI, err := syscall.GetProcAddress(syscall.Handle(advapi32), "GetTokenInformation")
	if err != nil {
		return errors.New("Can't Load GetTokenInformation API")
	}
	if r, _, errno := syscall.Syscall6(uintptr(GTI), 5, uintptr(TokenHandle), TokenInformationClass,
		TokenInformation, TokenInformationLength, uintptr(unsafe.Pointer(ReturnLength)), 0); r == 0 {
		return error(errno)
	}
	return nil
}
