/*!
 * \file lib/gis/parser_interface.c
 *
 * \brief GIS Library - Argument parsing functions (interface)
 *
 * (C) 2001-2009 by the GRASS Development Team
 *
 * This program is free software under the GNU General Public License
 * (>=v2). Read the file COPYING that comes with GRASS for details.
 *
 * \author Original author CERL
 * \author Soeren Gebbert added Dec. 2009 WPS process_description document
 */

#include <grass/config.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/types.h>

#if defined(HAVE_LANGINFO_H)
#include <langinfo.h>
#endif
#if defined(__MINGW32__) && defined(USE_NLS)
#include <localcharset.h>
#endif
#ifdef HAVE_ICONV_H
#include <iconv.h>
#endif

#include <grass/gis.h>
#include <grass/glocale.h>
#include <grass/spawn.h>

#include "parser_local_proto.h"

#ifdef HAVE_ICONV_H
static const char *src_enc;
#endif

/*!
 * \brief Formats text for XML.
 *
 * \param[in,out] fp file to write to
 * \param str string to write
 */
static void print_escaped_for_xml(FILE *fp, const char *str)
{
#ifdef HAVE_ICONV_H
    iconv_t conv = iconv_open("UTF-8", src_enc);
    char *enc = NULL;

    if (conv != (iconv_t)-1) {
        char *src = (char *)str;
        size_t srclen = strlen(src);
        size_t dstlen = srclen * 4 + 1;
        char *dst = G_alloca(dstlen);
        size_t ret;

        enc = dst;

        ret = iconv(conv, (char **)&src, &srclen, &dst, &dstlen);
        if (ret != (size_t)-1 && srclen == 0) {
            str = enc;
            *dst = '\0';
        }
    }
#endif

    for (; *str; str++) {
        switch (*str) {
        case '&':
            fputs("&amp;", fp);
            break;
        case '<':
            fputs("&lt;", fp);
            break;
        case '>':
            fputs("&gt;", fp);
            break;
        default:
            fputc(*str, fp);
        }
    }

#ifdef HAVE_ICONV_H
    if (enc) {
        G_freea(enc);
    }

    if (conv != (iconv_t)-1)
        iconv_close(conv);
#endif
}

/*!
   \brief Print module usage description in XML format.
 */
void G__usage_xml(void)
{
    struct Option *opt;
    struct Flag *flag;
    char *type;
    char *s, *top;
    int i;
    const char *encoding;
    int new_prompt = 0;

    new_prompt = G__uses_new_gisprompt();

    /* gettext converts strings to encoding returned by nl_langinfo(CODESET) */

#if defined(HAVE_LANGINFO_H)
    encoding = nl_langinfo(CODESET);
#elif defined(__MINGW32__) && defined(USE_NLS)
    encoding = locale_charset();
#endif

    if (!encoding || strlen(encoding) == 0)
        encoding = "UTF-8";

#ifdef HAVE_ICONV_H
    src_enc = encoding;
    encoding = "UTF-8";
#endif

    if (!st->pgm_name) /* v.dave && r.michael */
        st->pgm_name = G_program_name();
    if (!st->pgm_name)
        st->pgm_name = "??";

    fprintf(stdout, "<?xml version=\"1.0\" encoding=\"%s\"?>\n", encoding);
    fprintf(stdout, "<!DOCTYPE task SYSTEM \"grass-interface.dtd\">\n");

    fprintf(stdout, "<task name=\"%s\">\n", st->pgm_name);

    if (st->module_info.label) {
        fprintf(stdout, "\t<label>\n\t\t");
        print_escaped_for_xml(stdout, st->module_info.label);
        fprintf(stdout, "\n\t</label>\n");
    }

    if (st->module_info.description) {
        fprintf(stdout, "\t<description>\n\t\t");
        print_escaped_for_xml(stdout, st->module_info.description);
        fprintf(stdout, "\n\t</description>\n");
    }

    if (st->module_info.keywords) {
        fprintf(stdout, "\t<keywords>\n\t\t");
        G__print_keywords(stdout, print_escaped_for_xml);
        fprintf(stdout, "\n\t</keywords>\n");
    }

    /***** Don't use parameter-groups for now.  We'll reimplement this later
     ***** when we have a concept of several mutually exclusive option
     ***** groups
    if (st->n_opts || st->n_flags)
            fprintf(stdout, "\t<parameter-group>\n");
     *****
     *****
     *****/

    if (st->n_opts) {
        opt = &st->first_option;
        while (opt != NULL) {
            /* TODO: make this a enumeration type? */
            switch (opt->type) {
            case TYPE_INTEGER:
                type = "integer";
                break;
            case TYPE_DOUBLE:
                type = "float";
                break;
            case TYPE_STRING:
                type = "string";
                break;
            default:
                type = "string";
                break;
            }
            fprintf(stdout,
                    "\t<parameter "
                    "name=\"%s\" "
                    "type=\"%s\" "
                    "required=\"%s\" "
                    "multiple=\"%s\">\n",
                    opt->key, type, opt->required == YES ? "yes" : "no",
                    opt->multiple == YES ? "yes" : "no");

            if (opt->label) {
                fprintf(stdout, "\t\t<label>\n\t\t\t");
                print_escaped_for_xml(stdout, opt->label);
                fprintf(stdout, "\n\t\t</label>\n");
            }

            if (opt->description) {
                fprintf(stdout, "\t\t<description>\n\t\t\t");
                print_escaped_for_xml(stdout, opt->description);
                fprintf(stdout, "\n\t\t</description>\n");
            }

            if (opt->key_desc) {
                fprintf(stdout, "\t\t<keydesc>\n");
                top = G_calloc(strlen(opt->key_desc) + 1, 1);
                strcpy(top, opt->key_desc);
                s = strtok(top, ",");
                for (i = 1; s != NULL; i++) {
                    fprintf(stdout, "\t\t\t<item order=\"%d\">", i);
                    print_escaped_for_xml(stdout, s);
                    fprintf(stdout, "</item>\n");
                    s = strtok(NULL, ",");
                }
                fprintf(stdout, "\t\t</keydesc>\n");
                G_free(top);
            }

            if (opt->gisprompt) {
                const char *atts[] = {"age", "element", "prompt", NULL};
                top = G_calloc(strlen(opt->gisprompt) + 1, 1);
                strcpy(top, opt->gisprompt);
                s = strtok(top, ",");
                fprintf(stdout, "\t\t<gisprompt ");
                for (i = 0; s != NULL && atts[i] != NULL; i++) {
                    fprintf(stdout, "%s=\"%s\" ", atts[i], s);
                    s = strtok(NULL, ",");
                }
                fprintf(stdout, "/>\n");
                G_free(top);
            }

            if (opt->def) {
                fprintf(stdout, "\t\t<default>\n\t\t\t");
                print_escaped_for_xml(stdout, opt->def);
                fprintf(stdout, "\n\t\t</default>\n");
            }

            if (opt->options) {
                /* TODO:
                 * add something like
                 *       <range min="xxx" max="xxx"/>
                 * to <values> */
                i = 0;
                fprintf(stdout, "\t\t<values>\n");
                while (opt->opts[i]) {
                    fprintf(stdout, "\t\t\t<value>\n");
                    fprintf(stdout, "\t\t\t\t<name>");
                    print_escaped_for_xml(stdout, opt->opts[i]);
                    fprintf(stdout, "</name>\n");
                    if (opt->descs && opt->opts[i] && opt->descs[i]) {
                        fprintf(stdout, "\t\t\t\t<description>");
                        print_escaped_for_xml(stdout, opt->descs[i]);
                        fprintf(stdout, "</description>\n");
                    }
                    fprintf(stdout, "\t\t\t</value>\n");
                    i++;
                }
                fprintf(stdout, "\t\t</values>\n");
            }
            if (opt->guisection) {
                fprintf(stdout, "\t\t<guisection>\n\t\t\t");
                print_escaped_for_xml(stdout, opt->guisection);
                fprintf(stdout, "\n\t\t</guisection>\n");
            }
            if (opt->guidependency) {
                fprintf(stdout, "\t\t<guidependency>\n\t\t\t");
                print_escaped_for_xml(stdout, opt->guidependency);
                fprintf(stdout, "\n\t\t</guidependency>\n");
            }
            /* TODO:
             * - key_desc?
             * - there surely are some more. which ones?
             */

            opt = opt->next_opt;
            fprintf(stdout, "\t</parameter>\n");
        }
    }

    if (st->n_flags) {
        flag = &st->first_flag;
        while (flag != NULL) {
            fprintf(stdout, "\t<flag name=\"%c\">\n", flag->key);

            if (flag->label) {
                fprintf(stdout, "\t\t<label>\n\t\t\t");
                print_escaped_for_xml(stdout, flag->label);
                fprintf(stdout, "\n\t\t</label>\n");
            }

            if (flag->suppress_required)
                fprintf(stdout, "\t\t<suppress_required/>\n");

            if (flag->description) {
                fprintf(stdout, "\t\t<description>\n\t\t\t");
                print_escaped_for_xml(stdout, flag->description);
                fprintf(stdout, "\n\t\t</description>\n");
            }
            if (flag->guisection) {
                fprintf(stdout, " \t\t<guisection>\n\t\t\t");
                print_escaped_for_xml(stdout, flag->guisection);
                fprintf(stdout, "\n\t\t</guisection>\n");
            }
            flag = flag->next_flag;
            fprintf(stdout, "\t</flag>\n");
        }
    }

    /***** Don't use parameter-groups for now.  We'll reimplement this later
     ***** when we have a concept of several mutually exclusive option
     ***** groups
    if (st->n_opts || st->n_flags)
            fprintf(stdout, "\t</parameter-group>\n");
     *****
     *****
     *****/

    if (new_prompt) {
        /* overwrite */
        fprintf(stdout, "\t<flag name=\"%s\">\n", "overwrite");
        fprintf(stdout, "\t\t<description>\n\t\t\t");
        print_escaped_for_xml(
            stdout, _("Allow output files to overwrite existing files"));
        fprintf(stdout, "\n\t\t</description>\n");
        fprintf(stdout, "\t</flag>\n");
    }

    /* help */
    fprintf(stdout, "\t<flag name=\"%s\">\n", "help");
    fprintf(stdout, "\t\t<description>\n\t\t\t");
    print_escaped_for_xml(stdout, _("Print usage summary"));
    fprintf(stdout, "\n\t\t</description>\n");
    fprintf(stdout, "\t</flag>\n");

    /* verbose */
    fprintf(stdout, "\t<flag name=\"%s\">\n", "verbose");
    fprintf(stdout, "\t\t<description>\n\t\t\t");
    print_escaped_for_xml(stdout, _("Verbose module output"));
    fprintf(stdout, "\n\t\t</description>\n");
    fprintf(stdout, "\t</flag>\n");

    /* quiet */
    fprintf(stdout, "\t<flag name=\"%s\">\n", "quiet");
    fprintf(stdout, "\t\t<description>\n\t\t\t");
    print_escaped_for_xml(stdout, _("Quiet module output"));
    fprintf(stdout, "\n\t\t</description>\n");
    fprintf(stdout, "\t</flag>\n");

    G__describe_option_rules_xml(stdout);

    fprintf(stdout, "</task>\n");
}
