import os
import sys
import re
import subprocess
import json

VCC_PATH  = 'C:/Program Files/Side Effects Software/Houdini 16.0.600/bin/vcc.exe'
SYN_PATH  = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')
COMP_PATH = os.path.join(SYN_PATH, 'VEX.sublime-completions')
FUNC_PATH = os.path.join(os.path.join(SYN_PATH, 'syntax_lists'), 'VexFunctions.txt')


def contexts(vcc_path=VCC_PATH):
    """Return a sorted list of all vex contexts."""
    ctxs = subprocess.check_output([vcc_path, '-X'])
    ctxs = ctxs.decode('ascii').split('\n')
    return sorted([x for x in ctxs if x != '' and x != None])


def context_functions(context, vcc_path=VCC_PATH, as_set=False):
    """Return the sorted list of all function names for a vex context."""
    ctx_info = subprocess.check_output([vcc_path, '-X', context])
    ctx_info = ctx_info.decode('ascii')

    funcs = set()
    for f in re.findall('\w+\(', ctx_info):
        if len(f) > 1:
            funcs.add(f[:-1])

    if as_set:
        return funcs
    else:
        return sorted(list(funcs))


def context_function_signatures(context, vcc_path=VCC_PATH):
    ctx_info = subprocess.check_output([vcc_path, '-X', context])
    ctx_info = ctx_info.decode('ascii')

    sigs = []
    for s in re.findall('(\w+(\[\])?) (\w+)\((.*)\)', ctx_info):
        sig_str  = '%s %s(%s)' % (s[0], s[2], s[3])
        if s[3] == 'void':
            hint_str = ''
        else:
            hint_str = '%s\n(%s)' % (s[0], s[3].rstrip().lstrip().rstrip(';'))
        args = [x.strip() for x in s[3].split(';')]
        sigs.append({'returns':s[0], 'name':s[2], 'ctx':context, 'args':args, 'str':sig_str,
                     'hint':hint_str})

    return sigs


def all_functions(vcc_path=VCC_PATH, write_functions=True, function_path=FUNC_PATH):
    """Returns a sorted list of all vex functions in all contexts."""
    all_funcs = set()
    for ctx in contexts():
        all_funcs.update(context_functions(ctx, as_set=True))

    all_funcs_sorted = sorted(all_funcs)

    if write_functions:
        with open(function_path, 'w') as f:
            for func in all_funcs_sorted:
                f.write('{}\n'.format(func))

    return all_funcs_sorted


def all_function_signatures(vcc_path=VCC_PATH):
    all_sigs = []
    sig_strs = set()

    for ctx in contexts():
        ctx_sigs = context_function_signatures(ctx)

        for ctx_sig in ctx_sigs:
            if ctx_sig['str'] not in sig_strs:
                sig_strs.add(ctx_sig['str'])
                all_sigs.append(ctx_sig)

    return all_sigs


def generate_simple_completions(sublime_completion_path=COMP_PATH):
    """Converts the function signitures generated by vcc into SublimeText compatable completion
    JSON files."""
    completions = []
    for name in all_functions():
        completions.append({'trigger' : ('%s\tfunction' % name),
                            'contents': ('%s(${1})' % name)})

    data = {'scope': 'source.vex', 'completions': completions}

    with open(sublime_completion_path, 'w') as f:
        json.dump(data, f, sort_keys=True, indent=4, separators=(',', ': '))


def generate_completions(sublime_completion_path=COMP_PATH):
    """Converts the function signitures generated by vcc into SublimeText compatable completion
    JSON files."""
    completions = []
    for sig in all_function_signatures():

        if len(sig['args']) == 1 and sig['args'][0] == 'void':
            comp_arg_fmt = ''
        else:
            comp_arg_fmt = ''
            comp_arg_fmt_no_varadic = ''
            c = 1

            for arg_type in sig['args']:
                comp_arg_fmt += ('${%d:%s}, ' % (c, arg_type))
                c += 1
                if arg_type != '...':
                    comp_arg_fmt_no_varadic = comp_arg_fmt

            comp_arg_fmt = comp_arg_fmt[:-2]  # stripping ', ' before closing parenthesis
            comp_arg_fmt_no_varadic = comp_arg_fmt_no_varadic[:-2]

        # in the varadic case, we'll generate two completions - one with and one without the
        # varadic argument elipsis
        if sig['args'][-1] == '...':
            new_hint = sig['hint'][:-4]
            new_hint = new_hint.rstrip().rstrip(';')
            new_hint += ')'
            completions.append({'trigger' : ('%s\t%s' % (sig['name'], new_hint)),
                                'contents': ('%s(%s)' % (sig['name'], comp_arg_fmt_no_varadic))})

        completions.append({'trigger' : ('%s\t%s' % (sig['name'], sig['hint'])),
                            'contents': ('%s(%s)' % (sig['name'], comp_arg_fmt))})

    data = {'scope': 'source.vex', 'completions': completions}

    with open(sublime_completion_path, 'w') as f:
        json.dump(data, f, sort_keys=True, indent=4, separators=(',', ': '))


if __name__ == '__main__':
    if len(sys.argv) < 2:
        generate_simple_completions()
    elif len(sys.argv) == 2:
        generate_simple_completions(sys.argv[1])
    else:
        raise Exception('To many arguments.')