# ===============================================
# Disclaimer:
# This code is a pure copy of elevate-0.1.3
# as found on https://pypi.org/project/elevate
# and maintained by barney.gale@gmail.com
#
# copied here because elevate refuses to install on my PyCharm setup, with strange windows error
# ===============================================

import ctypes
import os
from ctypes import POINTER, c_ulong, c_char_p, c_int, c_void_p
from ctypes.wintypes import HANDLE, BOOL, DWORD, HWND, HINSTANCE, HKEY
from ctypes import windll
import subprocess
import sys

# Constant definitions

SEE_MASK_NOCLOSEPROCESS = 0x00000040
SEE_MASK_NO_CONSOLE = 0x00008000

# Type definitions

PHANDLE = ctypes.POINTER(HANDLE)
PDWORD = ctypes.POINTER(DWORD)


class ShellExecuteInfo(ctypes.Structure):
    _fields_ = [
        ('cbSize',       DWORD),
        ('fMask',        c_ulong),
        ('hwnd',         HWND),
        ('lpVerb',       c_char_p),
        ('lpFile',       c_char_p),
        ('lpParameters', c_char_p),
        ('lpDirectory',  c_char_p),
        ('nShow',        c_int),
        ('hInstApp',     HINSTANCE),
        ('lpIDList',     c_void_p),
        ('lpClass',      c_char_p),
        ('hKeyClass',    HKEY),
        ('dwHotKey',     DWORD),
        ('hIcon',        HANDLE),
        ('hProcess',     HANDLE)]

    def __init__(self, **kw):
        super(ShellExecuteInfo, self).__init__()
        self.cbSize = ctypes.sizeof(self)
        for field_name, field_value in kw.items():
            setattr(self, field_name, field_value)


PShellExecuteInfo = POINTER(ShellExecuteInfo)


# Function definitions

ShellExecuteEx = windll.shell32.ShellExecuteExA
ShellExecuteEx.argtypes = (PShellExecuteInfo, )
ShellExecuteEx.restype = BOOL

WaitForSingleObject = windll.kernel32.WaitForSingleObject
WaitForSingleObject.argtypes = (HANDLE, DWORD)
WaitForSingleObject.restype = DWORD

CloseHandle = windll.kernel32.CloseHandle
CloseHandle.argtypes = (HANDLE, )
CloseHandle.restype = BOOL


# At last, the actual implementation!

# patch: need to pass CWD
#        in case there was a chdir AFTER the start of the initial program
#        the new elevated instance would NOT start on the same dir.
# patch: execute the SAME python interpreter with the same cmdline arguments
#        so rely on orig_argv rather than argv
# patch: need to restaure the path as it was for the original program

def find_in_path(exe):
    for item in os.environ['PATH']:
        tried = item + '/' + exe
        if os.path.isfile(tried):
            return tried
    return None


def elevate(cwd, show_console=True, graphical=True):
    if windll.shell32.IsUserAnAdmin():
        return
    os.chdir(cwd)
    params = ShellExecuteInfo(
        fMask=SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE,
        hwnd=None,
        lpVerb=b'runas',
        # lpDirectory=cwd,  # attention: / et \
        # lpFile=sys.executable.encode('cp1252'),
        lpFile=sys.orig_argv[0].encode('cp1252'),
        # lpParameters=subprocess.list2cmdline(sys.argv).encode('cp1252'),
        lpParameters=subprocess.list2cmdline(sys.orig_argv[1:]).encode('cp1252'),
        nShow=int(show_console))
    # print(f"sys.executable = {sys.executable}")
    # print(f"sys.argv       = {sys.argv}")
    # print(f"sys.orig_argv  = {sys.orig_argv}")
    # input("msg:\n")
    try:
        if not ShellExecuteEx(ctypes.byref(params)):
            data = input("Please enter the message 20:\n")
            raise ctypes.WinError()
    except OSError:
        data = input("Please enter the message 2:\n")

    handle = params.hProcess

    ret = DWORD()
    WaitForSingleObject(handle, -1)
    if windll.kernel32.GetExitCodeProcess(handle, ctypes.byref(ret)) == 0:
        raise ctypes.WinError()
    CloseHandle(handle)
    sys.exit(ret.value)
