r'''
    Install a feature

    .. note::
        Some features require reboot after un/installation, if so until the
        server is restarted other features can not be installed!

    .. note::
        Some features take a long time to complete un/installation, set -t with
        a long timeout

    Args:

        feature (str, list):
            The name of the feature(s) to install. This can be a single feature,
            a string of features in a comma delimited list (no spaces), or a
            list of features.

            .. versionadded:: 2018.3.0
                Added the ability to pass a list of features to be installed.

        recurse (Options[bool]):
            Install all sub-features. Default is False

        restart (Optional[bool]):
            Restarts the computer when installation is complete, if required by
            the role/feature installed. Will also trigger a reboot if an item
            in ``exclude`` requires a reboot to be properly removed. Default is
            False

        source (Optional[str]):
            Path to the source files if missing from the target system. None
            means that the system will use windows update services to find the
            required files. Default is None

        exclude (Optional[str]):
            The name of the feature to exclude when installing the named
            feature. This can be a single feature, a string of features in a
            comma-delimited list (no spaces), or a list of features.

            .. warning::
                As there is no exclude option for the ``Add-WindowsFeature``
                or ``Install-WindowsFeature`` PowerShell commands the features
                named in ``exclude`` will be installed with other sub-features
                and will then be removed. **If the feature named in ``exclude``
                is not a sub-feature of one of the installed items it will still
                be removed.**

    Returns:
        dict: A dictionary containing the results of the install

    CLI Example:

    .. code-block:: bash

        # Install the Telnet Client passing a single string
        salt '*' win_servermanager.install Telnet-Client

        # Install the TFTP Client and the SNMP Service passing a comma-delimited
        # string. Install all sub-features
        salt '*' win_servermanager.install TFTP-Client,SNMP-Service recurse=True

        # Install the TFTP Client from d:\side-by-side
        salt '*' win_servermanager.install TFTP-Client source=d:\\side-by-side

        # Install the XPS Viewer, SNMP Service, and Remote Access passing a
        # list. Install all sub-features, but exclude the Web Server
        salt '*' win_servermanager.install "['XPS-Viewer', 'SNMP-Service', 'RemoteAccess']" True recurse=True exclude="Web-Server"

def install(feature, recurse=False, restart=False, source=None, exclude=None):
    r'''
    Install a feature

    .. note::
        Some features require reboot after un/installation, if so until the
        server is restarted other features can not be installed!

    .. note::
        Some features take a long time to complete un/installation, set -t with
        a long timeout

    Args:

        feature (str, list):
            The name of the feature(s) to install. This can be a single feature,
            a string of features in a comma delimited list (no spaces), or a
            list of features.

            .. versionadded:: 2018.3.0
                Added the ability to pass a list of features to be installed.

        recurse (Options[bool]):
            Install all sub-features. Default is False

        restart (Optional[bool]):
            Restarts the computer when installation is complete, if required by
            the role/feature installed. Will also trigger a reboot if an item
            in ``exclude`` requires a reboot to be properly removed. Default is
            False

        source (Optional[str]):
            Path to the source files if missing from the target system. None
            means that the system will use windows update services to find the
            required files. Default is None

        exclude (Optional[str]):
            The name of the feature to exclude when installing the named
            feature. This can be a single feature, a string of features in a
            comma-delimited list (no spaces), or a list of features.

            .. warning::
                As there is no exclude option for the ``Add-WindowsFeature``
                or ``Install-WindowsFeature`` PowerShell commands the features
                named in ``exclude`` will be installed with other sub-features
                and will then be removed. **If the feature named in ``exclude``
                is not a sub-feature of one of the installed items it will still
                be removed.**

    Returns:
        dict: A dictionary containing the results of the install

    CLI Example:

    .. code-block:: bash

        # Install the Telnet Client passing a single string
        salt '*' win_servermanager.install Telnet-Client

        # Install the TFTP Client and the SNMP Service passing a comma-delimited
        # string. Install all sub-features
        salt '*' win_servermanager.install TFTP-Client,SNMP-Service recurse=True

        # Install the TFTP Client from d:\side-by-side
        salt '*' win_servermanager.install TFTP-Client source=d:\\side-by-side

        # Install the XPS Viewer, SNMP Service, and Remote Access passing a
        # list. Install all sub-features, but exclude the Web Server
        salt '*' win_servermanager.install "['XPS-Viewer', 'SNMP-Service', 'RemoteAccess']" True recurse=True exclude="Web-Server"
    '''
    # If it is a list of features, make it a comma delimited string
    if isinstance(feature, list):
        feature = ','.join(feature)

    # Use Install-WindowsFeature on Windows 2012 (osversion 6.2) and later
    # minions. Default to Add-WindowsFeature for earlier releases of Windows.
    # The newer command makes management tools optional so add them for parity
    # with old behavior.
    command = 'Add-WindowsFeature'
    management_tools = ''
    if salt.utils.versions.version_cmp(__grains__['osversion'], '6.2') >= 0:
        command = 'Install-WindowsFeature'
        management_tools = '-IncludeManagementTools'

    cmd = '{0} -Name {1} {2} {3} {4} ' \
          '-WarningAction SilentlyContinue'\
          .format(command, _cmd_quote(feature), management_tools,
                  '-IncludeAllSubFeature' if recurse else '',
                  '' if source is None else '-Source {0}'.format(source))
    out = _pshell_json(cmd)

    # Uninstall items in the exclude list
    # The Install-WindowsFeature command doesn't have the concept of an exclude
    # list. So you install first, then remove
    if exclude is not None:
        removed = remove(exclude)

    # Results are stored in a list of dictionaries in `FeatureResult`
    if out['FeatureResult']:
        ret = {'ExitCode': out['ExitCode'],
               'RestartNeeded': False,
               'Restarted': False,
               'Features': {},
               'Success': out['Success']}

        # FeatureResult is a list of dicts, so each item is a dict
        for item in out['FeatureResult']:
            ret['Features'][item['Name']] = {
                'DisplayName': item['DisplayName'],
                'Message': item['Message'],
                'RestartNeeded': item['RestartNeeded'],
                'SkipReason': item['SkipReason'],
                'Success': item['Success']
            }

            if item['RestartNeeded']:
                ret['RestartNeeded'] = True

        # Only items that installed are in the list of dictionaries
        # Add 'Already installed' for features that aren't in the list of dicts
        for item in feature.split(','):
            if item not in ret['Features']:
                ret['Features'][item] = {'Message': 'Already installed'}

        # Some items in the exclude list were removed after installation
        # Show what was done, update the dict
        if exclude is not None:
            # Features is a dict, so it only iterates over the keys
            for item in removed['Features']:
                if item in ret['Features']:
                    ret['Features'][item] = {
                        'Message': 'Removed after installation (exclude)',
                        'DisplayName': removed['Features'][item]['DisplayName'],
                        'RestartNeeded': removed['Features'][item]['RestartNeeded'],
                        'SkipReason': removed['Features'][item]['SkipReason'],
                        'Success': removed['Features'][item]['Success']
                    }

                    # Exclude items might need a restart
                    if removed['Features'][item]['RestartNeeded']:
                        ret['RestartNeeded'] = True

        # Restart here if needed
        if restart:
            if ret['RestartNeeded']:
                if __salt__['system.restart'](in_seconds=True):
                    ret['Restarted'] = True

        return ret

    else:

        # If we get here then all features were already installed
        ret = {'ExitCode': out['ExitCode'],
               'Features': {},
               'RestartNeeded': False,
               'Restarted': False,
               'Success': out['Success']}

        for item in feature.split(','):
            ret['Features'][item] = {'Message': 'Already installed'}

        return ret