#ifdef RCSID
static char RCSid[] =
"$Header$";
#endif

/* 
 *   Copyright (c) 2001 by Michael J. Roberts.  All Rights Reserved.
 *   
 *   Please see the accompanying license file, LICENSE.TXT, for information
 *   on using and copying this software.  
 */
/*
Name
  t23main.cpp - combined TADS 2 and 3 interpreter startup
Function
  Senses whether to invoke the TADS 2 or 3 engine based on the game file
  being opened, and passes control to the appropriate engine's entrypoint.
Notes
  
Modified
  03/06/01 MJRoberts  - Creation
*/

#include <Windows.h>

#include "t23main.h"
#include "t3main.h"
#include "htmlw32.h"
#include "w32ver.h"

/* tads 2 headers */
#include "trd.h"

/* tads 3 headers */
#include "t3std.h"
#include "vmmain.h"

/*
 *   Determine which VM to use, and invoke its entrypoint 
 */
int t23main(int argc, char **argv, struct appctxdef *appctx, char *save_ext)
{
    int stat;
    char game_arg[OSFNMAX];
    CHtmlSys_mainwin *mainwin;
    char fname[OSFNMAX + 1];
    char *game_file;
    int engine_type;
    int engine_valid;
    int using_pending;
    char actual_game_file[OSFNMAX];
    char *new_argv[2];
    static const char *defexts[] =
    {
        "gam",
        "t3"
    };

    /* presume failure */
    stat = OSEXFAIL;

    /* presume we won't use a pending game name */
    using_pending = FALSE;

    /* if there are no arugments, check for a pending game */
    if (argc <= 1)
    {
        /* ask the main window for a pending game */
        if ((mainwin = CHtmlSys_mainwin::get_main_win()) != 0
            && mainwin->get_pending_new_game() != 0
            && strlen(mainwin->get_pending_new_game()) + 1 <= sizeof(fname))
        {
            /* copy the pending image file name */
            strcpy(fname, mainwin->get_pending_new_game());
            
            /* use this as the filename for sensing the engine version */
            game_file = fname;
            
            /* 
             *   note that we are using a pending game (so that we can
             *   cancel the pending game if don't recognize its type) 
             */
            using_pending = TRUE;
        }
        else if (appctx != 0 && appctx->get_game_name != 0)
        {
            /* 
             *   No arguments and no pending game, but we have a host
             *   callback that can provide a game name on demand.  Ask for a
             *   name.  
             */
            if ((*appctx->get_game_name)(appctx->get_game_name_ctx,
                                         fname, sizeof(fname)))
            {
                /* 
                 *   we got the name - use this for sensing the engine
                 *   version 
                 */
                game_file = fname;

                /* set up a new argument vector with the game name */
                new_argv[0] = argv[0];
                new_argv[1] = fname;
                argv = new_argv;
                argc = 2;
            }
            else
            {
                /* 
                 *   no game selected - simply return success so that the
                 *   caller can go into standby mode 
                 */
                os_printz("\n(No game has been selected.)\n");
                return OSEXSUCC;
            }
        }
        else
        {
            /* 
             *   we have no game name, and no way of asking the host system
             *   for a game - we must just be starting in standby mode, so
             *   indicate success so that the caller go stand by for the
             *   user to select a game explicitly 
             */
            return OSEXSUCC;
        }
    }
    else
    {
        /* parse arguments to figure out which engine type we're using */
        if (!vm_get_game_arg(argc, argv, game_arg, sizeof(game_arg),
                             &engine_type))
        {
            /* show an error */
            MessageBox(0, "Invalid command line arguments.  No valid "
                       "game file was specified, or the syntax of one "
                       "or more command line options is incorrect.",
                       "TADS", MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);

            /* indicate failure */
            return OSEXFAIL;
        }

        /* use this as the game filename */
        game_file = game_arg;
    }
    
    /* if we got a file, check the game file to determine the game's type */
    if (game_file[0] != '\0')
        engine_type = vm_get_game_type(game_file, actual_game_file,
                                       sizeof(actual_game_file),
                                       defexts,
                                       sizeof(defexts)/sizeof(defexts[0]));

    /* presume the engine type is not valid or not one we recognize */
    engine_valid = FALSE;

    /* invoke the appropriate engine */
    switch(engine_type)
    {
    case VM_GGT_TADS2:
        /* we have a valid engine type that we can deal with */
        engine_valid = TRUE;

        /* invoke tads 2, and use the resulting status code */
        stat = trdmain(argc, argv, appctx, save_ext);
        break;
        
    case VM_GGT_TADS3:
        /* we have a valid engine type that we can deal with */
        engine_valid = TRUE;

        /* invoke tads 3, and use the resulting status code */
        stat = t3main(argc, argv, appctx, save_ext);
        break;
        
    case VM_GGT_AMBIG:
        /* ambiguous game name - show an error */
        MessageBox(0, "More than one game file with the given name "
                   "exists.  Please specify the exact name of the file "
                   "you wish to use, including the suffix (.gam or .t3).",
                   "TADS", MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
        break;
        
    case VM_GGT_INVALID:
        /* the file is not valid for any known engine */
        MessageBox(0, "The game file specified is not a valid compiled "
                   "TADS game.",
                   "TADS", MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
        break;

    case VM_GGT_NOT_FOUND:
    default:
        /* 
         *   we couldn't figure out which engine type to invoke - show an
         *   error 
         */
        MessageBox(0, "The game file specified does not exist.",
                   "TADS", MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
        break;
    }

    /* check for -Xinterfaces mode */
    if (argc == 3 && strcmp(argv[1], "-Xinterfaces") == 0)
    {
        /* 
         *   We're doing -Xinterfaces, so the only reason we're running is to
         *   generate the interfaces file.  We've done that; we just need to
         *   add the system version ID to the file.  
         */
        FILE *fp = fopen(argv[2], "r");
        if (fp != 0)
        {
            /* read it into memory */
            struct fline
            {
                void *operator new(size_t siz, const char *txt)
                    { return th_malloc(siz + strlen(txt)); }
                void operator delete(void *ptr) { th_free(ptr); }

                fline(const char *txt)
                {
                    strcpy(this->txt, txt);
                    this->nxt = 0;
                }

                fline *nxt;
                char txt[1];
            };
            fline *head = 0, *tail = 0;
            for (;;)
            {
                char buf[1024];
                if (fgets(buf, sizeof(buf), fp) == 0)
                    break;
                fline *f = new (buf) fline(buf);
                if (tail != 0)
                    tail->nxt = f;
                else
                    head = f;
                tail = f;
            }

            /* re-open for writing */
            fclose(fp);
            fp = fopen(argv[2], "w");

            /* write it back out, inserting the version information */
            while (head != 0)
            {
                /* write this item */
                fprintf(fp, "%s", head->txt);

                /* if this is the <tads3> item, insert the version data */
                if (strlen(head->txt) > 6
                    && memcmp(head->txt, "<tads3", 6) == 0
                    && strchr(" >", head->txt[6]) != 0)
                {
                    fprintf(fp,
                            " <interpreter>\n"
                            "  <version>%s</version>\n"
                            " </interpreter>\n",
                            HTMLTADS_WIN32_BUILD);
                }

                /* unlink and delete this item */
                fline *cur = head;
                head = head->nxt;
                delete cur;
            }

            /* done with the file */
            fclose(fp);
        }

        /* that's all we wanted to do - terminate the program */
        exit(0);
    }

    /* 
     *   if we didn't get a valid engine type, and we are using a pending
     *   game name, cancel the pending game, since we've now effectively
     *   tried running the game and failed 
     */
    if (using_pending && !engine_valid)
        mainwin->clear_pending_new_game();

    /* return the status */
    return stat;
}
