#!/usr/bin/env python3
import io

import woman.util as util
import woman.help as help
from woman.constants import *
from woman.main_functions import main_functions_list, EnumString

def escape_char(c):
    return {
            '\n': '\\n',
            '\t': '\\t',
            '"': '\\"',
            }.get(c,c)

def c(x):
    if x is None:
        return "NULL"
    if type(x) == bytes:
        return x.decode()
    if type(x) == str:
        return '"' + "".join(escape_char(c) for c in x) + '"'
    if type(x) == int:
        return str(x)
    if type(x) == tuple or type(x) == list:
        return "{" + ",".join(map(c,x)) +"}"

def char(s):
    return {
            '\n': b"'\\n'",
            '\t': b"'\\t'",
            '\'': b"'\\''",
        }.get(c,f"'{s}'".encode())


print("const char *USAGE =", c(help.client_usage),";")
print("const char *LICENSE =", c(help.LICENSE_ALL),";")
print("const char *X_QUEUE_ATOM =", c(help.X_QUEUE_ATOM),";")

SUBCOMMANDS_HELP = []

print('static void check(int argc, char ** argv)')
print('{')
for f in main_functions_list:
    parser = util.ArgumentParserNoFail(add_help=False)
    for (args, kvargs) in f.args:
        parser.add_argument(*args, **kvargs)
    help_io = io.StringIO()
    parser.print_help(help_io)
    help_io.seek(0)
    help_str = "\n".join("   "+x for x in (f.help.split("\n") if f.help else []) + help_io.read().split("\n")[2:-1])
    module_help = "/".join(f.names) + ":\n"+help_str
    if f.help is not None:
        SUBCOMMANDS_HELP.append(module_help)

    print('if(', " || ".join(f"!strcmp(argv[1], {c(name)})" for name in f.names), ")")
    print('{')
    print(f'const char * module_help = {c(module_help)};')
    switch= []
    short_opts = []
    long_opts = []
    for args, kvargs in f.args:
        need_argument = "action" not in kvargs
        short = args[0][1:]
        long = args[1][2:]
        short_opts.append(args[0][1:] + (":" if need_argument else ""))
        long_opts.append((long, b"required_argument" if need_argument else b"no_argument", None, char(short)))
        def assert_in(l):
            switch.append(f"case {c(char(short))}: {{")
            switch.append(f"char *list[] = {c(l)};")
            switch.append(f"IS_IN(optarg, list, \"-{short}/--{long}\");")
            switch.append("} break;")
        if "type" in kvargs and issubclass(kvargs["type"], EnumString):
            assert_in(kvargs["type"]().options())

    print(f"struct option long_opts[] = {c(long_opts + [(None, 0, None, 0)])};")
    print("int opt;")
    print(f'while ((opt = getopt_long(argc-1, argv+1, {c("".join(short_opts))}, long_opts, NULL)) >= 0)')
    print('{')
    print('if(opt==\'?\') die("%s", module_help);')
    print("switch (opt)")
    print('{')
    for s in switch:
        print(s)
    print('}')
    print('}')
    print('if (optind < argc-1) die("Positional argument is unsupported.\\n%s", module_help);')
    print('return;')
    print('}')
    
print('die_help("Command %s not found.", argv[1]);')
print('}')

SUBCOMMANDS_HELP.append(
f"""
WORKSPACE must be name (mostly number) of workspace
MASTER must be number between {MIN_MASTER} and {MAX_MASTER} or command from next list:
    - next/prev            next/previous master from actual
                           after all masters, shared slaves will be iterated
    - next-skip/prev-skip  next/previous used master from actual
    - alloc                first unused master
SLAVE must be number between {MIN_SLAVE} and {MAX_SLAVE} or command from next list:
    - next-limit/prev-limit              next/previous slave from actual master
    - next-limit-skip/prev-limit-skip    next/previous used slave from actual master
    - next-limit/prev-limit/next-limit-skip/prev-limit-skip
                The same as with limit, but if there is no more slave,
                next master will be used and after all slaves will be shared slaves.
    - alloc                first unused slave on this master
"""[1:-1]
)

print("const char *SUBCOMMANDS_HELP =", c("   " + "\n\n".join(SUBCOMMANDS_HELP).replace('\n', '\n   ')),";")