#!python

import argparse
import sys

from myrpcgen.Constants import VERSION
from myrpcgen.GeneratorBase import StructFieldAccess, GeneratorBase, GeneratorException
from myrpcgen.Parser import Parser, ParserException

# Generators registers themshelves at import.

import myrpcgen.JSGenerator
import myrpcgen.PyGenerator

INDENT = 4
OUTDIR = "."
SFA = "underscore" # Default, must be in SFA_CHOICES.
SFA_CHOICES = {"underscore": StructFieldAccess.UNDERSCORE,
               "capital":    StructFieldAccess.CAPITAL,
               "direct":     StructFieldAccess.DIRECT}

def main():
    # Parse command line arguments.

    args = parse_args()

    # Lookup generator.

    gen_name = args.gen_name

    try:
        gen_class = GeneratorBase.lookup_gen(gen_name)
    except KeyError:
        fatal("Generator {} is unknown".format(gen_name))

    # Parse IDL.

    parser = Parser(args.idlfile)

    try:
        parser.parse()
    except ParserException as e:
        fatal("Parsing of IDL file failed: {}".format(e))

    namespaces = parser.get_namespaces()
    tm = parser.get_tm()
    methods = parser.get_methods()

    # Generate sources.

    namespace = namespaces[gen_name] if (gen_name in namespaces) else None
    sfa = SFA_CHOICES[args.sfa_name]

    # args is passed to generator to allow processing of custom
    # command-line arguments (see GeneratorBase.setup_argparse).

    gen = gen_class(namespace, tm, methods, args.indent, sfa, args.outdir, args.overwrite, args)

    try:
        gen.setup_gen()

        # IDL-syntax and command-line arguments must be validated before
        # reaching this point.

        gen.create_outdir()

        gen.gen_types()

        if args.client:
            gen.gen_client()

        if args.processor:
            gen.gen_processor()
    except GeneratorException as e:
        fatal("Generator failed: {}".format(e))

    sys.exit(0)

def parse_args():
    # FIXME: argparser, common exit, exitcode

    gen_names = GeneratorBase.list_gen()
    gen_namesf = ", ".join(gen_names)

    parser = argparse.ArgumentParser(description = "Compile MyRPC IDL to the specified language.")

    # Add generic arguments.

    parser.add_argument("-v", action = "version", version = VERSION)
    parser.add_argument("-t", dest = "indent",
                        metavar = "indent",
                        default = INDENT,
                        help = "identation width (default: {})".format(INDENT))
    parser.add_argument("-f", dest = "sfa_name",
                        choices = SFA_CHOICES,
                        default = SFA,
                        help = "struct field access (default: {})".format(SFA))
    parser.add_argument("-d", dest = "outdir", metavar = "outdir",
                        default = OUTDIR,
                        help = "output directory for generated files (default: {})".format(OUTDIR))
    parser.add_argument("-o", dest = "overwrite", action = "store_true",
                        help = "overwrite already existing files (default: no)")
    parser.add_argument("-g", dest = "gen_name", required = True,
                        metavar = "gen",
                        help = "generator to use (available: {})".format(gen_namesf))
    parser.add_argument("-C", dest = "client", action = "store_true",
                        help = "generate client")
    parser.add_argument("-P", dest = "processor", action = "store_true",
                        help = "generate processor")
    parser.add_argument("idlfile")

    # Add generator specific arguments. Generators must call add_argument_group to
    # create a new arg group, and every arg must be prefixed with generator
    # name.

    for gen_name in gen_names:
        gen_class = GeneratorBase.lookup_gen(gen_name)
        gen_class.setup_argparse(parser)

    # Parse command-line arguments.

    args = parser.parse_args()

    try:
        args.indent = int(args.indent)
        if args.indent < 1:
            raise ValueError()
    except ValueError:
        fatal("-t must be numeric and >= 1")

    if (not args.client and not args.processor):
        fatal("-C and/or -P must be given")

    return args

def fatal(msg):
    print("{}: {}".format(sys.argv[0], msg), file = sys.stderr)

    sys.exit(1)

main()
