# -*- coding: utf-8 -*-
'''
Manage the Windows System PATH

Note that not all Windows applications will rehash the PATH environment variable,
Only the ones that listen to the WM_SETTINGCHANGE message
http://support.microsoft.com/kb/104011
'''
from __future__ import absolute_import

# Python Libs
import logging
import re
import os
from salt.ext.six.moves import map

# Third party libs
try:
    from win32con import HWND_BROADCAST, WM_SETTINGCHANGE
    from win32api import SendMessage
    HAS_WIN32 = True
except ImportError:
    HAS_WIN32 = False

# Import salt libs
import salt.utils

# Settings
log = logging.getLogger(__name__)


def __virtual__():
    '''
    Load only on Windows
    '''
    if salt.utils.is_windows() and HAS_WIN32:
        return 'win_path'
    return (False, "Module win_path: module only works on Windows systems")


def _normalize_dir(string):
    '''
    Normalize the directory to make comparison possible
    '''
    return re.sub(r'\\$', '', string.lower())


def rehash():
    '''
    Send a WM_SETTINGCHANGE Broadcast to Windows to refresh the Environment variables

    CLI Example:

    ... code-block:: bash

        salt '*' win_path.rehash
    '''
    return bool(SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 'Environment'))


def get_path():
    '''
    Returns a list of items in the SYSTEM path

    CLI Example:

    .. code-block:: bash

        salt '*' win_path.get_path
    '''
    ret = __salt__['reg.read_value']('HKEY_LOCAL_MACHINE',
                                   'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment',
                                   'PATH')['vdata'].split(';')

    # Trim ending backslash
    return list(map(_normalize_dir, ret))


def exists(path):
    '''
    Check if the directory is configured in the SYSTEM path
    Case-insensitive and ignores trailing backslash

    Returns:
        boolean True if path exists, False if not

    CLI Example:

    .. code-block:: bash

        salt '*' win_path.exists 'c:\\python27'
        salt '*' win_path.exists 'c:\\python27\\'
        salt '*' win_path.exists 'C:\\pyThon27'
    '''
    path = _normalize_dir(path)
    sysPath = get_path()

    return path in sysPath


def add(path, index=0):
    '''
    Add the directory to the SYSTEM path in the index location

    Returns:
        boolean True if successful, False if unsuccessful

    CLI Example:

    .. code-block:: bash

        # Will add to the beginning of the path
        salt '*' win_path.add 'c:\\python27' 0

        # Will add to the end of the path
        salt '*' win_path.add 'c:\\python27' index='-1'
    '''
    currIndex = -1
    sysPath = get_path()
    path = _normalize_dir(path)
    index = int(index)

    # validate index boundaries
    if index < 0:
        index = len(sysPath) + index + 1
    if index > len(sysPath):
        index = len(sysPath)

    localPath = os.environ["PATH"].split(os.pathsep)
    if path not in localPath:
        localPath.append(path)
        os.environ["PATH"] = os.pathsep.join(localPath)

    # Check if we are in the system path at the right location
    try:
        currIndex = sysPath.index(path)
        if currIndex != index:
            sysPath.pop(currIndex)
        else:
            return True
    except ValueError:
        pass

    # Add it to the Path
    sysPath.insert(index, path)
    regedit = __salt__['reg.set_value'](
        'HKEY_LOCAL_MACHINE',
        'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment',
        'PATH',
        ';'.join(sysPath),
        'REG_EXPAND_SZ'
    )

    # Broadcast WM_SETTINGCHANGE to Windows
    if regedit:
        return rehash()
    else:
        return False


def remove(path):
    r'''
    Remove the directory from the SYSTEM path

    Returns:
        boolean True if successful, False if unsuccessful

    CLI Example:

    .. code-block:: bash

        # Will remove C:\Python27 from the path
        salt '*' win_path.remove 'c:\\python27'
    '''
    path = _normalize_dir(path)
    sysPath = get_path()

    localPath = os.environ["PATH"].split(os.pathsep)
    if path in localPath:
        localPath.remove(path)
        os.environ["PATH"] = os.pathsep.join(localPath)

    try:
        sysPath.remove(path)
    except ValueError:
        return True

    regedit = __salt__['reg.set_value'](
        'HKEY_LOCAL_MACHINE',
        'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment',
        'PATH',
        ';'.join(sysPath),
        'REG_EXPAND_SZ'
    )
    if regedit:
        return rehash()
    else:
        return False

@@ -325,16 +325,30 @@
 port os%0A
+import ctypes%0A
 from sal
@@ -460,45 +460,26 @@
 ANGE
-%0A    from win32api import SendMessage
+, SMTO_ABORTIFHUNG
 %0A   
@@ -1071,32 +1071,36 @@
  the Environment
+%0A   
  variables%0A%0A    
@@ -1097,112 +1097,718 @@
 bles
-%0A%0A    CLI Example:%0A%0A    ... code-block:: bash%0A%0A        salt '*' win_path.rehash%0A    '''%0A    return bool(
+ for new processes.%0A%0A    .. note::%0A        This will only affect new processes that aren't launched by services. To%0A        apply changes to the path to services, the host must be restarted. The%0A        %60%60salt-minion%60%60, if running as a service, will not see changes to the%0A        environment until the system is restarted. See%0A        %60MSDN Documentation %3Chttps://support.microsoft.com/en-us/help/821761/changes-that-you-make-to-environment-variables-do-not-affect-services%3E%60%0A%0A    CLI Example:%0A%0A    ... code-block:: bash%0A%0A        salt '*' win_path.rehash%0A    '''%0A    broadcast_message = ctypes.create_unicode_buffer('Environment')%0A    user32 = ctypes.windll('user32', use_last_error=True)%0A    result = user32.
 Send
@@ -1814,16 +1814,24 @@
 dMessage
+TimeoutW
 (HWND_BR
@@ -1863,24 +1863,157 @@
 , 0,
- 'Environment'))
+%0A                                        broadcast_message, SMTO_ABORTIFHUNG,%0A                                        5000, 0)%0A    return result == 1
 %0A%0A%0Ad

Fix rehash function