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

#include <xml.h>

#define TS	(4*1024*1024)

#define PRINT(a) \
    tag = (a); if (tag) { strncat(s, "      <tag>", TS-strlen(s)); strncat(s, tag, TS-strlen(s)); strncat(s, "</tag>\n", TS-strlen(s)); }

static unsigned int tags_level = 0;
static unsigned int print_tags = 0;

char *
getCommandLineOption(int argc, char **argv, char const *option)
{
    int slen = strlen(option);
    char *rv = 0;
    int i;

    for (i=0; i<argc; i++)
    {
        if (strncmp(argv[i], option, slen) == 0)
        {
            rv = "";
            i++;
            if (i<argc) rv = argv[i];
        }
    }

    return rv;
}

void
show_help()
{
    printf("Usage: metdata [options]\n");
    printf("Updates the metadata of a FlightGear aircraft configuration.\n");
    printf("\nOptions:\n");
    printf("  -i, --input <file>\t\taircraft configuration file to update\n");

    printf("\n");
    exit(-1);
}

char *strlwr(char *str)
{
  unsigned char *p = (unsigned char *)str;

  while (*p) {
     *p = tolower((unsigned char)*p);
      p++;
  }

  return str;
}

void print_xml(FILE *fd, void *id, char *tags)
{
    static int level = 1;
    void *xid = xmlMarkId(id);
    unsigned int num;

    num = xmlNodeGetNum(xid, "*");
    if (num == 0)
    {
        char *s;
        s = xmlGetString(xid);
        if (s)
        {
            fprintf(fd, "%s", s);
            free(s);
        }
    }
    else
    {
        unsigned int i, q;
        for (i=0; i<num; i++)
        {
            if (xmlNodeGetPos(id, xid, "*", i) != 0)
            {
                unsigned int anum, a;
                char name[256];
                xmlNodeCopyName(xid, (char *)&name, 256);

                if (tags_level == 1 && !strcmp(name, "sim")) tags_level++;

                fprintf(fd, "\n");
                for(q=0; q<level; q++) fprintf(fd, "  ");
                fprintf(fd, "<%s", name);

                anum = xmlAttributeGetNum(xid);
                for (a=0; a<anum; a++)
                {
                    char name[256], attrib[256];
                    if (xmlAttributeCopyName(xid, (char *)&name, 256, a) != 0)
                    {
                        xmlAttributeCopyString(xid, name, (char *)&attrib, 256);
                        fprintf(fd, " %s=\"%s\"", name, attrib);
                    }
                }
                fprintf(fd, ">");

                level++;
                print_xml(fd, xid, tags);

                level--;
                fprintf(fd, "</%s>", name);

                if (tags_level == 2 && !strcmp(name, "aero")) {
                    fprintf(fd, "\n%s\n", tags);
                }
            }
            else printf("error\n");
        }
        fprintf(fd, "\n");
        for(q=1; q<level; q++) fprintf(fd, "  ");
    }
}

void
updateFile(char *fname, char *tags)
{
    char bfname[4096];
    FILE *fd = NULL;
    void *rid;

    snprintf(bfname, 4096, "%s.bak", fname);
    rename(fname, bfname);

    rid = xmlOpen(bfname);
    if (rid)
    {
        fd = fopen(fname, "w");
        if (!fd)
        {
            xmlClose(rid);
            rid = NULL;
            rename(bfname, fname);
        }
    }

    if (rid)
    {
        unsigned int i, num;
        void *xid;

        fprintf(fd, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n");

        xid = xmlMarkId(rid);
        num = xmlNodeGetNum(xid, "*");
        for (i=0; i<num; i++)
        {
            if (xmlNodeGetPos(rid, xid, "*", i) != 0)
            {
                unsigned int anum, a;
                char name[256];
                xmlNodeCopyName(xid, (char *)&name, 256);

                if (strcmp(name, "PropertyList")) break;

                tags_level++;
                fprintf(fd, "<%s", name);
                anum = xmlAttributeGetNum(xid);
                for (a=0; a<anum; a++)
                {
                    char name[256], attrib[256];
                    if (xmlAttributeCopyName(xid, (char *)&name, 256, a) != 0)
                    {
                        xmlAttributeCopyString(xid, name, (char *)&attrib, 256);
                        fprintf(fd, " %s=\"%s\"", name, attrib);
                    }
                }
                fprintf(fd, ">");
                print_xml(fd, xid, tags);
                fprintf(fd, "\n</%s>\n", name);
            }
        }
        free(xid);

        xmlClose(rid);
        fclose(fd);
    }
}

const char*
vendor_tag(char *desc)
{
    char *p = strlwr(desc);
    if (strstr(p, "boeing")) return "boeing";
    if (strstr(p, "airbus")) return "airbus";
    if (strstr(p, "antonov")) return "antonov";
    if (strstr(p, "tupolev")) return "tupolev";
    if (strstr(p, "ilyoushin")) return "ilyoushin";
    if (strstr(p, "bombardier")) return "bombardier";
    if (strstr(p, "fokker")) return "fokker";
    if (strstr(p, "lockheed")) return "lockheed";
    if (strstr(p, "general-dynamics")) return "general-dynamics";
    if (strstr(p, "general dynamics")) return "general-dynamics";
    if (strstr(p, "mig")) return "mikoyan-gurevich";
    if (strstr(p, "mikoyan gurevich")) return "mikoyan-gurevich";
    if (strstr(p, "mikoyan-gurevich")) return "mikoyan-gurevich";
    if (strstr(p, "sukoi")) return "sukoi";
    if (strstr(p, "cessna")) return "cessna";
    if (strstr(p, "fairchild")) return "fairchild";
    if (strstr(p, "dassult")) return "dassult";
    if (strstr(p, "dornier")) return "dornier";
    if (strstr(p, "arado")) return "arado";
    if (strstr(p, "schleicher")) return "schleicher";
    if (strstr(p, "avro")) return "avro";
    if (strstr(p, "saab")) return "saab";
    if (strstr(p, "dassault")) return "dassault";
    if (strstr(p, "aermacchi")) return "aermacchi";
    if (strstr(p, "arsenal")) return "arsenal";
    if (strstr(p, "rockwell")) return "rockwell";
    if (strstr(p, "northrop")) return "northrop";
    if (strstr(p, "grumman")) return "grumman";
    if (strstr(p, "mc donnell") && strstr(p, "douglas")) return "mc-donnell-douglas";
    if (strstr(p, "douglas")) return "douglas";
    if (strstr(p, "mc donnell")) return "mc-donnell";
    if (strstr(p, "mc-donnell")) return "mc-donnell";
//  if (strstr(p, "jaguar")) return "jaguar";
    if (strstr(p, "junkers")) return "junkers";
    if (strstr(p, "kawasaki")) return "kawasaki";
    if (strstr(p, "de havilland")) return "de-havilland";
    if (strstr(p, "de-havilland")) return "de-havilland";
    if (strstr(p, "diamond")) return "diamond";
    if (strstr(p, "bell")) return "bell";
    if (strstr(p, "hughes")) return "hughes";
    if (strstr(p, "kamov")) return "kamov";
    if (strstr(p, "mil")) return "mil";
    if (strstr(p, "eurocopter")) return "eurocopter";
    if (strstr(p, "alouette")) return "alouette";
    if (strstr(p, "aerospatiale")) return "aerospatiale";
    if (strstr(p, "sikorsky")) return "sikorsky";
    if (strstr(p, "bernard")) return "bernard";
    if (strstr(p, "bleriot")) return "bleriot";
    if (strstr(p, "bristol")) return "bristol";
    if (strstr(p, "breguet")) return "breguet";
    if (strstr(p, "wright")) return "wright";
    if (strstr(p, "breda")) return "breda";
    if (strstr(p, "rutan")) return "rutan";
    if (strstr(p, "vought")) return "vought";
    if (strstr(p, "fiat")) return "fiat";
    if (strstr(p, "focke wulf")) return "focke-wulf";
    if (strstr(p, "focke-wulf")) return "focke-wulf";
    if (strstr(p, "gloster")) return "gloster";
    if (strstr(p, "hawker")) return "hawker";
    if (strstr(p, "heinkel")) return "heinkel";
    if (strstr(p, "messerschmitt")) return "messerschmitt";
    if (strstr(p, "north american")) return "north-american";
    if (strstr(p, "north-american")) return "north-american";
    if (strstr(p, "piaggio")) return "piaggio";
    if (strstr(p, "pilatus")) return "pilatus";
    if (strstr(p, "supermarine")) return "supermarine";
    if (strstr(p, "beechcraft")) return "beechcraft";
    if (strstr(p, "beech")) return "beechcraft";
    if (strstr(p, "vickers")) return "vickers";
    if (strstr(p, "westland")) return "westland";
    if (strstr(p, "yakovlev")) return "yakovlev";
    if (strstr(p, "schweizer")) return "schweizer";

    /* educated guess */
    if (strstr(p, "b7")) return "boeing";
    if (strstr(p, "airbus")) return "airbus";
    if (strstr(p, "an-")) return "antonov";
    if (strstr(p, "tu-")) return "tupolev";
    if (strstr(p, "il-")) return "ilyoushin";
    if (strstr(p, "su-")) return "sukoi";
    if (strstr(p, "dc-")) return "douglas";
    if (strstr(p, "md-")) return "mc-donnell-douglas";
    if (strstr(p, "ju-")) return "junkers";
    if (strstr(p, "ec-")) return "eurocopter";
    if (strstr(p, "he-")) return "heinkel";
    if (strstr(p, "as-")) return "aerospatiale";
    if (strstr(p, "me-")) return "messerschmitt";
    if (strstr(p, "fw-")) return "focke-wulf";
    if (strstr(p, "yak-")) return "yakovlev";
    if (strstr(p, "sgs-")) return "schweizer";
    if (strstr(p, "ask-")) return "schleicher";
    if (strstr(p, "mirage")) return "dassault";

    return NULL;
}

/* -- JSBSim ---------------------------------------------------------------- */
const char*
jsb_wing_tag(void *xid, void *path)
{
    void *xlid, *xmid, *xeid;
    double aero_z = 0.0;
    double eye_z = 0.0;
    int i, num;

    xeid = xmlNodeGet(xid, "/fdm_config/propulsion/engine/thruster");
    if (xeid)
    {
        char *rv = NULL;
        char *file = xmlAttributeGetString(xeid, "file");
        char *fname = calloc(1, strlen(path)+strlen("Engines/")+
                                strlen(file)+strlen(".xml")+1);
        if (fname)
        {
            void *xfid;

            memcpy(fname, path, strlen(path));
            memcpy(fname+strlen(path), "Engines/", strlen("Engines/"));
            memcpy(fname+strlen(path)+strlen("Engines/"), file, strlen(file));
            memcpy(fname+strlen(path)+strlen("Engines/")+strlen(file), ".xml", strlen(".xml"));

            xfid = xmlOpen(fname);
            if (xfid)
            {
                if (xmlNodeTest(xfid, "rotor")) rv = "helicopter";
                xmlClose(xfid);
            }
            free(fname);
        }
        xmlFree(file);
        xmlFree(xeid);
        if (rv) return rv;
    }

    xmid = xmlNodeGet(xid, "/fdm_config/metrics");
    if (!xmid) return NULL;

    xlid = xmlMarkId(xmid);
    num = xmlNodeGetNum(xmid, "location");
    for (i=0; i<num; ++i)
    {
        if (xmlNodeGetPos(xmid, xlid, "location", i) != 0)
        {
            if (!xmlAttributeCompareString(xlid, "name", "AERORP")) {
                aero_z = xmlNodeGetDouble(xlid, "z");
            } else if (!xmlAttributeCompareString(xlid, "name", "EYEPOINT")) {
                eye_z = xmlNodeGetDouble(xlid, "z");
            }
        }
    }
    xmlFree(xlid);
    xmlFree(xmid);

    if (aero_z > eye_z) return "high-wing";
    else return "low-wing";
}

const char*
jsb_gear_tag(void *xid)
{
    void *xcid, *xgid = xmlNodeGet(xid, "/fdm_config/ground_reactions");
    double nose_x = 0.0;
    double main_x = 0.0;
    int bogeys = 0;
    int i, num;

    if (!xgid) return NULL;

    xcid = xmlMarkId(xgid);
    num = xmlNodeGetNum(xgid, "contact");
    for (i=0; i<num; ++i)
    {
        if (xmlNodeGetPos(xgid, xcid, "contact", i) != 0)
        {
            if (!xmlAttributeCompareString(xcid, "type", "BOGEY"))
            {
                if (xmlNodeGetDouble(xcid, "location/y") == 0.0) {
                    nose_x = xmlNodeGetDouble(xcid, "location/x");
                } else {
                    main_x = xmlNodeGetDouble(xcid, "location/x");
                }
                bogeys++;
            }
        }
    }
    xmlFree(xcid);
    xmlFree(xgid);
 
    if (bogeys == 0) return "skids";
    else if (bogeys < 3 || main_x < nose_x) return "tail-dragger";
    return "tricycle";
}

const char*
jsb_gear_retract_tag(void *xid)
{
    int r = xmlNodeGetInt(xid, "/fdm_config/ground_reactions/contact/retractable");
    if (r) return "retractable-gear";
    else return "fixed-gear";
}

const char*
jsb_gear_steering_tag(void *xid)
{
    void *xcid, *xgid = xmlNodeGet(xid, "/fdm_config/ground_reactions");
    char *rv = NULL;
    int found = 0;
    int i, num;

    if (!xgid) return NULL;

    xcid = xmlMarkId(xgid);
    num = xmlNodeGetNum(xgid, "contact");
    for (i=0; i<num; ++i)
    {
        if (xmlNodeGetPos(xgid, xcid, "contact", i) != 0)
        {
            if (!xmlAttributeCompareString(xcid, "type", "BOGEY"))
            {
                if (xmlNodeGetDouble(xcid, "location/y") == 0.0)
                {
                    double m = xmlNodeGetDouble(xcid, "max_steer");
                    int c = xmlNodeGetInt(xcid, "castered");

                    if (c || (m == 360.0 && !c)) {
                        rv = "castering";
                        break;
                    } else if (m > 0.0) {
//                      rv = "normal-steering";
                        break;
                    }
                    else {
//                      rv = "no-steering";
                    }
                }
            }
        }
    }
    xmlFree(xcid);
    xmlFree(xgid);

    return rv;
}

const char*
jsb_engines_tag(void *xid)
{
    void *xeid, *xpid = xmlNodeGet(xid, "/fdm_config/propulsion");
    const char* rv = NULL;
    int engines = 0;
    int i, num;
    char *main;

    if (!xpid) return rv;

    xeid = xmlMarkId(xpid);

    num = xmlNodeGetNum(xpid, "engine");
    for (i=0; i<num; ++i)
    {
        if (xmlNodeGetPos(xpid, xeid, "engine", i) != 0)
        {
            if (i == 0)
            {
                main = xmlAttributeGetString(xeid, "file");
                engines++;
            }
            else if (!xmlAttributeCompareString(xeid, "file", main)) {
                engines++;
            }
        }
    }
    xmlFree(main);
    xmlFree(xeid);
    xmlFree(xpid);

    switch (engines)
    {
    case 0:
        rv = "glider";
        break;
    case 1:
        rv = "single-engine";
        break;
    case 2:
        rv = "twin-engine";
        break;
    case 3:
        rv = "three-engine";
        break;
    case 4:
        rv = "four-engine";
        break;
    default:
        break;
    }

    return rv;
}

const char*
jsb_engine_tag(void *xid, char *path)
{
    void *xeid = xmlNodeGet(xid, "/fdm_config/propulsion/engine");
    char *engine, *fname;
    const char *rv = NULL;

    if (!xeid) return rv;

    engine = xmlAttributeGetString(xeid, "file");
    fname = calloc(1, strlen(path)+strlen("Engines/")+
                      strlen(engine)+strlen(".xml")+1);
    if (fname)
    {
        void *xfid;

        memcpy(fname, path, strlen(path));
        memcpy(fname+strlen(path), "Engines/", strlen("Engines/"));
        memcpy(fname+strlen(path)+strlen("Engines/"), engine, strlen(engine));
        memcpy(fname+strlen(path)+strlen("Engines/")+strlen(engine), ".xml", strlen(".xml"));

        xfid = xmlOpen(fname);
        if (xfid)
        {
            if (xmlNodeTest(xfid, "turbine_engine")) rv = "jet";
            else if (xmlNodeTest(xfid, "turboprop_engine")) rv = "turboprop";
            else if (xmlNodeTest(xfid, "piston_engine")) rv = "piston";
            else if (xmlNodeTest(xfid, "electric_engine")) rv = "electric";
            else if (xmlNodeTest(xfid, "rocket_engine")) rv = "rocket";

            xmlClose(xfid);
        }
        else {
            printf("JSBSim engine file not found: %s\n", fname);
        }
        free(fname);
    }
    else {
        printf("Unable to allocate memory\n");
    }
    xmlFree(engine);
    xmlFree(xeid);

    return rv;
}

const char*
jsb_propulsion_tag(void *xid, char *path)
{
    void *xeid = xmlNodeGet(xid, "fdm_config/propulsion/engine");
    char *file, *fname;
    const char *rv = NULL;

    if (!xeid) return rv;

    file = xmlAttributeGetString(xeid, "file");
    fname = calloc(1, strlen(path)+strlen("Engines/")+
                      strlen(file)+strlen(".xml")+1);
    if (fname)
    {
        void *xfid;

        memcpy(fname, path, strlen(path));
        memcpy(fname+strlen(path), "Engines/", strlen("Engines/"));
        memcpy(fname+strlen(path)+strlen("Engines/"), file, strlen(file));
        memcpy(fname+strlen(path)+strlen("Engines/")+strlen(file), ".xml", strlen(".xml"));

        xfid = xmlOpen(fname);
        if (xfid)
        {
            if (xmlNodeGetInt(xfid, "turbine_engine/augmented")) {
                rv = "afterburner";
            }
            else if (xmlNodeGetInt(xfid, "piston_engine/numboostspeeds")) {
                rv = "supercharged";
            }
            xmlClose(xfid);
        }
        else {
            printf("JSBSim engine file not found: %s\n", fname);
        }
        free(fname);
    }
    xmlFree(xeid);
    xmlFree(file);

    return rv;
}

const char*
jsb_thruster_tag(void *xid, char *path)
{
    void *xeid = xmlNodeGet(xid, "/fdm_config/propulsion/engine/thruster");
    char *file, *fname;
    const char *rv = NULL;

    if (!xeid) return rv;

    file = xmlAttributeGetString(xeid, "file");
    fname = calloc(1, strlen(path)+strlen("Engines/")+
                      strlen(file)+strlen(".xml")+1);
    if (fname)
    {
        void *xfid;

        memcpy(fname, path, strlen(path));
        memcpy(fname+strlen(path), "Engines/", strlen("Engines/"));
        memcpy(fname+strlen(path)+strlen("Engines/"), file, strlen(file));
        memcpy(fname+strlen(path)+strlen("Engines/")+strlen(file), ".xml", strlen(".xml"));

        xfid = xmlOpen(fname);
        if (xfid)
        {
            if (xmlNodeTest(xfid, "propeller"))
            {
                double min = xmlNodeGetDouble(xfid, "propeller/minpitch");
                double max = xmlNodeGetDouble(xfid, "propeller/maxpitch");
                if (min == max) rv = "fixed-pitch";
                else rv = "variable-pitch";
            }
            xmlClose(xfid);
        }
        else {
            printf("JSBSim thruster file not found: %s\n", fname);
        }
        free(fname);
    }
    else {
        printf("Unable to allocate memory\n");
    }
    xmlFree(file);
    xmlFree(xeid);

    return rv;
}

void
update_metadata_jsb(char *sfname, char *path, char *aero, char *desc)
{
    const char *tag;
    char *s, *fname;
    void *xid;

    fname = calloc(1, strlen(path)+strlen(aero)+strlen(".xml")+1);
    if (!fname)
    {
        printf("Unable to allocate memory\n");
        return;
    }

    memcpy(fname, path, strlen(path));
    memcpy(fname+strlen(path), aero, strlen(aero));
    memcpy(fname+strlen(path)+strlen(aero), ".xml", strlen(".xml"));

    xid = xmlOpen(fname);
    if (!xid)
    {
        printf("JSBSim aero file not found: '%s'\n", fname);
        return;
    }

    s = malloc(65*1024);
    if (s)
    {
        sprintf(s, "    <tags>\n");
        PRINT("auto-generated");
        PRINT(vendor_tag(desc ? desc : aero));
        PRINT(strlwr(desc ? desc : aero));
        PRINT(jsb_wing_tag(xid, path));
        PRINT(jsb_gear_tag(xid));
        PRINT(jsb_gear_retract_tag(xid));
        PRINT(jsb_gear_steering_tag(xid));
        PRINT(jsb_engines_tag(xid));
        PRINT(jsb_engine_tag(xid, path));
        PRINT(jsb_propulsion_tag(xid, path));
        PRINT(jsb_thruster_tag(xid, path));
        s = strncat(s, "    </tags>", TS-strlen(s));

        if (print_tags) {
           printf( "%s\n", s);
        } else {
           updateFile(sfname, s);
        }
        free(s);
    }
    xmlClose(xid);
    free(fname);
}

/* -- Yasim ----------------------------------------------------------------- */

const char*
yasim_wing_tag(void *xid)
{
    void *xaid = xmlNodeGet(xid, "/airplane");
    void *xwid, *xcid;
    int n_wings;
    double wing_z = 0.0;
    double eye_z = 0.0;

    if (xmlNodeTest(xaid, "rotor")) {
        return "helicopter";
    }

    xwid = xmlNodeGet(xaid, "wing");
    if (xwid)
    {
        wing_z = xmlAttributeGetDouble(xwid, "z");
        xmlFree(xwid);
    }

    xcid = xmlNodeGet(xaid, "cockpit");
    if (xcid)
    {
       eye_z = xmlAttributeGetDouble(xcid, "z");
        xmlFree(xcid);
    }

    n_wings = xmlNodeGetNum(xaid, "mstab");
    xmlFree(xaid);

//  if (n_wings == 2) return "triplane";
//  if (n_wings == 1) return "biplane";

    if (wing_z > eye_z) return "high-wing";
    return "low-wing";
}

const char*
yasim_gear_tag(void *xid)
{
    void *xaid = xmlNodeGet(xid, "/airplane");
    void *xgid = xmlMarkId(xaid);
    double nose_x = 0.0;
    double main_x = 0.0;
    int gears = 0;
    int i, num;

    num = xmlNodeGetNum(xaid, "gear");
    for (i=0; i<num; ++i)
    {
        if (xmlNodeGetPos(xaid, xgid, "gear", i) != 0)
        {
             if (xmlAttributeGetDouble(xgid, "y") == 0.0) {
                 nose_x = xmlAttributeGetDouble(xgid, "x");
             } else {
                 main_x = xmlAttributeGetDouble(xgid, "x");
             }
             gears++;
        }
    }
    xmlFree(xgid);
    xmlFree(xaid);

    if (gears == 0) return "skids";
    else if (gears < 3 || main_x > nose_x) return "tail-dragger";
    return "tricycle";
}

const char*
yasim_gear_retract_tag(void *xid)
{
    void *xaid = xmlNodeGet(xid, "/airplane");
    void *xgid = xmlMarkId(xaid);
    char *rv = "fixed-gear";
    int found = 0;
    int i, num;

    num = xmlNodeGetNum(xaid, "gear");
    for (i=0; i<num; ++i)
    {
        if (xmlNodeGetPos(xaid, xgid, "gear", i) != 0)
        {
            void *xgcid = xmlMarkId(xgid);
            int j, cnum = xmlNodeGetNum(xgid, "control-input");
            for (j=0; j<cnum; ++j)
            {
                if (xmlNodeGetPos(xgid, xgcid, "control-input", j))
                {
                    if (!xmlAttributeCompareString(xgcid, "control", "EXTEND")) {
                        rv = "retractable-gear";
                        found = 1;
                        break;
                    }
               } 
            }
            xmlFree(xgcid);
        }
    }
    xmlFree(xgid);
    xmlFree(xaid);
    
    return rv;
}

const char*
yasim_gear_steering_tag(void *xid)
{
    void *xaid = xmlNodeGet(xid, "/airplane");
    void *xgid = xmlMarkId(xaid);
    char *rv = NULL;
    int found = 0;
    int i, num;

    num = xmlNodeGetNum(xaid, "gear");
    for (i=0; i<num; ++i)
    {
        if (xmlNodeGetPos(xaid, xgid, "gear", i) != 0)
        {
            void *xgcid = xmlMarkId(xgid);
            int j, cnum = xmlNodeGetNum(xgid, "control-input");
            for (j=0; j<cnum; ++j)
            {
                if (xmlNodeGetPos(xgid, xgcid, "control-input", j))
                {
                    if (!xmlAttributeCompareString(xgcid, "control", "STEER")) {
//                      rv = "normal-steering";
                        found = 1;
                        break;
                    }
                    else if (!xmlAttributeCompareString(xgcid, "control", "CASTERING")) {
                        rv = "castering";
                        found = 1;
                        break;
                   }
               }
            }
	    xmlFree(xgcid);
        }
    }
    xmlFree(xgid);
    xmlFree(xaid);

    return rv;
}

const char*
yasim_engines_tag(void *xid)
{
    void *xeid = xmlNodeGet(xid,  "/airplane");
    const char* rv = "multi-engine";
    int engines = 0;

    if (xmlNodeTest(xeid, "propeller")) {
        engines = xmlNodeGetNum(xeid, "propeller");
    }
    else if (xmlNodeTest(xeid, "jet")) {
        engines = xmlNodeGetNum(xeid, "jet");
    }
    else if (xmlNodeTest(xeid, "rotor")) {
        engines = 1;
    }

    switch (engines)
    {
    case 0:
        rv = "glider";
        break;
    case 1:
        rv = "single-engine";
        break;
    case 2:
        rv = "twin-engine";
        break;
    case 3:
        rv = "three-engine";
        break;
    case 4:
        rv = "four-engine";
        break;
    default:
        break;
    }
    xmlFree(xeid);

    return rv;
}

const char*
yasim_engine_tag(void *xid, char *path)
{
    const char* rv = NULL;

    if (xmlNodeTest(xid, "/airplane/propeller/piston-engine")) {
        rv = "piston";
    }
    else if (xmlNodeTest(xid, "/airplane/propeller/turbine-engine")) {
        rv = "turboprop";
    }
    else if (xmlNodeTest(xid, "/airplane/jet")) {
        rv = "jet";
    }

    return rv;
}

const char*
yasim_propulsion_tag(void *xid, char *path)
{
    const char* rv = NULL;
    void *xpid;

    if ((xpid = xmlNodeGet(xid, "/airplane/jet")) != NULL)
    {
        if (xmlAttributeGetDouble(xpid, "afterburner") > 0.0) {
            rv = "afterburner";
        }
        xmlFree(xpid);
    }
    else if ((xpid = xmlNodeGet(xid, "/airplane/propeller/piston-engine"))  != NULL)
    {
        if (xmlAttributeGetInt(xpid, "supercharger") > 0) {
            rv = "supercharger";
        }
        xmlFree(xpid);
    }

    return rv;
}

const char*
yasim_thruster_tag(void *xid, char *path)
{
    const char* rv = NULL;
    void *xtid;

    xtid = xmlNodeGet(xid, "/airplane/propeller/control-input");
    if (xtid)
    {
        if (!xmlAttributeCompareString(xtid, "control", "ADVANCE")) {
            rv = "variable-pitch";
        } else rv = "fixed-pitch";
        xmlFree(xtid);
    }
    
    return rv;
}

void
update_metadata_yasim(char *sfname, char *path, char *aero, char *desc)
{
    const char *tag;
    char *s, *fname;
    void *xid;
    
    fname = calloc(1, strlen(path)+strlen(aero)+strlen(".xml")+1);
    if (!fname)
    {   
        printf("Unable to allocate memory\n");
        return;
    }

    memcpy(fname, path, strlen(path));
    memcpy(fname+strlen(path), aero, strlen(aero)); 
    memcpy(fname+strlen(path)+strlen(aero), ".xml", strlen(".xml"));
    
    xid = xmlOpen(fname);
    if (!xid)
    {   
        printf("Yasim aero file not found: '%s'\n", fname);
        return;
    }
    
    s = malloc(65*1024);
    if (s)
    {
        sprintf(s, "    <tags>\n");
        PRINT("auto-generated");
        PRINT(vendor_tag(desc ? desc : aero));
        PRINT(strlwr(desc ? desc : aero));
        PRINT(yasim_wing_tag(xid));
        PRINT(yasim_gear_tag(xid));
        PRINT(yasim_gear_retract_tag(xid));
        PRINT(yasim_gear_steering_tag(xid));
        PRINT(yasim_engines_tag(xid));
        PRINT(yasim_engine_tag(xid, path));
        PRINT(yasim_propulsion_tag(xid, path));
        PRINT(yasim_thruster_tag(xid, path));
        s = strncat(s, "    </tags>", TS-strlen(s));

        if (print_tags) {
           printf( "%s\n", s);
        } else {
           updateFile(sfname, s);
        }
        free(s);
    }
    xmlClose(xid);
    free(fname);
}

/* -------------------------------------------------------------------------- */

void
update_metadata(const char *fname)
{
    void *xsid, *xid = xmlOpen(fname);

    if (!xid)
    {
        printf("File not found: '%s'\n", fname);
        return;
    }

    xsid = xmlNodeGet(xid, "/PropertyList/sim");
    if (!xsid)
    {
        printf("path '/PropertyList/sim' not found in '%s'\n", fname);
        xmlClose(xid);
        return;
    }

    if (xmlNodeTest(xsid, "tags") == 0)
    {
        char *desc = NULL, *str = NULL;
        char *path, *pend;

        pend = strrchr(fname, '/');
        if (!pend) pend = strrchr(fname, '\\');
        if (!pend) path = strdup("./");
        else
        {
            pend++;
            path = calloc(1, pend-fname+1);
            memcpy(path, fname, pend-fname);
        }

        desc = xmlNodeGetString(xsid, "description");
        str = xmlNodeGetString(xsid, "aero");
        if (!xmlNodeCompareString(xsid, "flight-model", "jsb")) {
            update_metadata_jsb(fname, path, str, desc);
        }
        else if (!xmlNodeCompareString(xsid, "flight-model", "yasim")) {
            update_metadata_yasim(fname, path, str, desc);
        }
        else
        {
            str = xmlNodeGetString(xsid, "flight-model");
            printf("The '%s' flightmodel is unsupported at this time\n", str);
        }

        xmlFree(xsid);

        xmlFree(desc);
        xmlFree(str);
        free(path);
    }
    else {
        printf("'%s' already contains metadata tags\n", fname);
    }

    xmlClose(xid);
}

int
main(int argc, char **argv)
{
    const char *setFile;

    if (argc == 1 || getCommandLineOption(argc, argv, "-h") ||
                     getCommandLineOption(argc, argv, "--help")) {
        show_help();
    }

    if (getCommandLineOption(argc, argv, "-p") ||
        getCommandLineOption(argc, argv, "--print")) {
        print_tags = 1;
    }

    setFile = getCommandLineOption(argc, argv, "-i");
    if (!setFile) {
        setFile = getCommandLineOption(argc, argv, "-input");
    }
    if (!setFile) { 
        show_help();
    }

    printf("%s\n", setFile);
    update_metadata(setFile);
    printf("\n");
    return 0;
}