#include <string.h>
#include <stdlib.h>

#include "proto.h"

#include <grass/glocale.h>

static char *xstrdup(const char *arg)
{
    return (G_strcasecmp(arg, "{NULL}") == 0) ? NULL : strdup(arg);
}

int parse_boolean(struct context *ctx, const char *arg)
{
    if (G_strcasecmp(arg, "yes") == 0)
        return YES;

    if (G_strcasecmp(arg, "no") == 0)
        return NO;

    fprintf(stderr, _("Unknown boolean value \"%s\" at line %d\n"), arg,
            ctx->line);

    return NO;
}

void parse_toplevel(struct context *ctx, const char *cmd)
{
    char **tokens;

    if (G_strcasecmp(cmd, "module") == 0) {
        ctx->state = S_MODULE;
        ctx->module = G_define_module();
        return;
    }

    if (G_strcasecmp(cmd, "flag") == 0) {
        ctx->state = S_FLAG;
        ctx->flag = G_define_flag();
        if (!ctx->first_flag)
            ctx->first_flag = ctx->flag;
        return;
    }

    if (G_strncasecmp(cmd, "option", strlen("option")) == 0) {
        ctx->state = S_OPTION;

        tokens = G_tokenize(cmd, " ");
        if (G_number_of_tokens(tokens) > 1) {
            /* standard option */
            ctx->option = define_standard_option(tokens[1]);
        }
        else {
            ctx->option = G_define_option();
        }

        if (!ctx->first_option)
            ctx->first_option = ctx->option;

        G_free_tokens(tokens);
        return;
    }

    if (G_strcasecmp(cmd, "rules") == 0) {
        ctx->state = S_RULES;
        return;
    }

    fprintf(stderr, _("Unknown command \"%s\" at line %d\n"), cmd, ctx->line);
}

void parse_module(struct context *ctx, const char *cmd, const char *arg)
{

    /* Label and description can be internationalized */
    if (G_strcasecmp(cmd, "label") == 0) {
        ctx->module->label = translate(xstrdup(arg));
        return;
    }

    if (G_strcasecmp(cmd, "description") == 0) {
        ctx->module->description = translate(xstrdup(arg));
        return;
    }

    if (G_strcasecmp(cmd, "keyword") == 0 ||
        G_strcasecmp(cmd, "keywords") == 0) {
        /* (still) accept 'keywords' for backward compatibility */
        G_add_keyword(translate(xstrdup(arg)));
        return;
    }

    if (G_strcasecmp(cmd, "overwrite") == 0) {
        ctx->module->overwrite = parse_boolean(ctx, arg);
        return;
    }

    if (G_strcasecmp(cmd, "end") == 0) {
        ctx->state = S_TOPLEVEL;
        return;
    }

    fprintf(stderr, _("Unknown module parameter \"%s\" at line %d\n"), cmd,
            ctx->line);
}

void parse_flag(struct context *ctx, const char *cmd, const char *arg)
{
    if (G_strcasecmp(cmd, "key") == 0) {
        ctx->flag->key = arg[0];
        return;
    }

    if (G_strcasecmp(cmd, "suppress_required") == 0) {
        ctx->flag->suppress_required = parse_boolean(ctx, arg);
        return;
    }

    /* Label, description, and guisection can all be internationalized */
    if (G_strcasecmp(cmd, "label") == 0) {
        ctx->flag->label = translate(xstrdup(arg));
        return;
    }

    if (G_strcasecmp(cmd, "description") == 0) {
        ctx->flag->description = translate(xstrdup(arg));
        return;
    }

    if (G_strcasecmp(cmd, "guisection") == 0) {
        ctx->flag->guisection = translate(xstrdup(arg));
        return;
    }

    if (G_strcasecmp(cmd, "end") == 0) {
        ctx->state = S_TOPLEVEL;
        return;
    }

    fprintf(stderr, _("Unknown flag parameter \"%s\" at line %d\n"), cmd,
            ctx->line);
}

int parse_type(struct context *ctx, const char *arg)
{
    if (G_strcasecmp(arg, "integer") == 0)
        return TYPE_INTEGER;

    if (G_strcasecmp(arg, "double") == 0)
        return TYPE_DOUBLE;

    if (G_strcasecmp(arg, "string") == 0)
        return TYPE_STRING;

    fprintf(stderr, _("Unknown type \"%s\" at line %d\n"), arg, ctx->line);

    return TYPE_STRING;
}

void parse_option(struct context *ctx, const char *cmd, const char *arg)
{
    if (G_strcasecmp(cmd, "key") == 0) {
        ctx->option->key = xstrdup(arg);
        return;
    }

    if (G_strcasecmp(cmd, "type") == 0) {
        ctx->option->type = parse_type(ctx, arg);
        return;
    }

    if (G_strcasecmp(cmd, "required") == 0) {
        ctx->option->required = parse_boolean(ctx, arg);
        return;
    }

    if (G_strcasecmp(cmd, "multiple") == 0) {
        ctx->option->multiple = parse_boolean(ctx, arg);
        return;
    }

    if (G_strcasecmp(cmd, "options") == 0) {
        ctx->option->options = xstrdup(arg);
        return;
    }

    if (G_strcasecmp(cmd, "key_desc") == 0) {
        ctx->option->key_desc = xstrdup(arg);
        return;
    }

    /* Label, description, descriptions, and guisection can all be
     * internationalized */
    if (G_strcasecmp(cmd, "label") == 0) {
        ctx->option->label = translate(xstrdup(arg));
        return;
    }

    if (G_strcasecmp(cmd, "description") == 0) {
        ctx->option->description = translate(xstrdup(arg));
        return;
    }

    if (G_strcasecmp(cmd, "descriptions") == 0) {
        ctx->option->descriptions = translate(xstrdup(arg));
        return;
    }

    if (G_strcasecmp(cmd, "answer") == 0) {
        ctx->option->answer = xstrdup(arg);
        return;
    }

    if (G_strcasecmp(cmd, "gisprompt") == 0) {
        ctx->option->gisprompt = xstrdup(arg);
        return;
    }

    if (G_strcasecmp(cmd, "guisection") == 0) {
        ctx->option->guisection = translate(xstrdup(arg));
        return;
    }

    if (G_strcasecmp(cmd, "guidependency") == 0) {
        ctx->option->guidependency = translate(xstrdup(arg));
        return;
    }

    if (G_strcasecmp(cmd, "end") == 0) {
        ctx->state = S_TOPLEVEL;
        return;
    }

    fprintf(stderr, _("Unknown option parameter \"%s\" at line %d\n"), cmd,
            ctx->line);
}

int print_options(const struct context *ctx, int sep)
{
    struct Option *option;
    struct Flag *flag;
    const char *overwrite = getenv("GRASS_OVERWRITE");
    const char *verbose = getenv("GRASS_VERBOSE");

    printf("@ARGS_PARSED@%c", sep);

    if (overwrite)
        printf("GRASS_OVERWRITE=%s%c", overwrite, sep);

    if (verbose)
        printf("GRASS_VERBOSE=%s%c", verbose, sep);

    for (flag = ctx->first_flag; flag; flag = flag->next_flag)
        printf("flag_%c=%d%c", flag->key, flag->answer ? 1 : 0, sep);

    for (option = ctx->first_option; option; option = option->next_opt)
        printf("opt_%s=%s%c", option->key, option->answer ? option->answer : "",
               sep);

    fflush(stdout);

    return EXIT_SUCCESS;
}
