#!/usr/bin/python3 -B
#  OpenVPN 3 Linux client -- Next generation OpenVPN client
#
#  SPDX-License-Identifier: AGPL-3.0-only
#
#  Copyright (C) 2017 - 2023  OpenVPN Inc <sales@openvpn.net>
#  Copyright (C) 2017 - 2023  David Sommerseth <davids@openvpn.net>
#

##
# @file  gen-openvpn2-completion.py
#
# @brief  Generates a static openvpn2 bash-completion helper script
#         based on the arguments the openvpn3 python module supports
#

import importlib
import sys
import argparse
from datetime import date
from jinja2 import Template

completion_template = """#  OpenVPN 3 Linux client -- Next generation OpenVPN client
#
#  SPDX-License-Identifier: AGPL-3.0-only
#
#  Copyright (C) 2017 - {{ year }}  OpenVPN Inc <sales@openvpn.net>
#
#
#  This script is automatically generated by gen-openvpn2-completion.py
#  Any modifications here will be overwritten.
#

_openvpn2_completion()
{
    local cur prev OPTS
    COMPREPLY=()
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    case $prev in
        '--config')
            # Prepare a simple filter, providing partial matching
            filter="cat" # by default everything passes
            if [ -n "$cur" ]; then
                filter="grep -E ^$cur"
            fi

            selopts="-W"
            # Only list files which most likely are OpenVPN configuration files
            selopts="$selopts $(grep -sm1 -E '(client|remote |port |lport |rport |key |cert |ca )' ${cur}*.conf ${cur}*.ovpn | cut -d: -f1 | $filter | tr '\\n' ' ')"

            # Add directories too, but ignore those starting with '.{some-alpha-num-letters}'
            selopts="$selopts $(compgen -d -- $cur | sed 's#$#/#' | grep -vE '^\\.\\w+' | tr '\\n' ' ')"

            # all information gathered, generate the completion reply
            COMPREPLY=( $( compgen "${selopts}" -- $cur ) )
            ;;
    {% for option, values in valid_args | dictsort %}
        '{{ option }}')
            COMPREPLY=( $(compgen -W  "{{ values }}" -- $cur) )
            return 0
            ;;
    {% endfor %}
    esac
    case $cur in
        -*)
            OPTS="{{ option_list }}"
            COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
            return 0;
            ;;
    esac
    return 0
}

complete -F _openvpn2_completion openvpn2
"""


if __name__ == '__main__':
    argp = argparse.ArgumentParser(description='Generate openvpn2 bash-completion helper')
    argp.add_argument('--python-source-dir', action='store', required=True)
    args = argp.parse_args()

    if None == args.python_source_dir:
        print("The --python-source-dir option is required")
        sys.exit(2)

    # Configure a dummy OpenVPN 3 ConfigParser, so the
    # supported options and arguments can be extracted.
    #
    # Since the openvpn3.constants are generated in a
    # build directory, some tricker with importlib is
    # required

    sys.path.insert(0, 'src/python')
    constants = importlib.import_module('openvpn3.constants',
                                        package='openvpn3.constants')

    # Load the configuration parser from the source directory
    # It is not needed to have the openvpn3.ConfigParser inside
    # the openvpn3 module in this program, so it's just loaded
    # into the ovpn3parser variable
    sys.path.insert(0, args.python_source_dir)
    ovpn3parser = importlib.import_module('ConfigParser')
    cfgparser = ovpn3parser.ConfigParser([sys.argv[0],], argp.description)

    # Retrieve the shell completion data from the openvpn3.ConfigParser
    completion_data = cfgparser.RetrieveShellCompletionData()

    # Prepare a list containing all valid options
    option_list = '{' + ','.join(['"%s"' % o for o in completion_data['options']]) + '}'

    #
    # Generate the bash-completion script
    #
    # We remove --config, as that is explicitly handled in the template
    completion_data['options'].remove('--config')
    valid_args = {}
    for opt, values in completion_data['argvalues'].items():
        if len(values) > 1:
            valid_args[opt] = '{' + ','.join(['"%s"' % v for v in values]) + '}'
        else:
            valid_args[opt] = '%s' % values[0]

    ctpl = Template(completion_template)
    script = ctpl.render(valid_args=valid_args, option_list=option_list,
                         year=date.today().year)

    print(script)
