/*
 * initcmdline.c - Initial command line options.
 *
 * Written by
 *  Andreas Boose <viceteam@t-online.de>
 *  Ettore Perazzoli <ettore@comm2000.it>
 *
 * This file is part of VICE, the Versatile Commodore Emulator.
 * See README for copyright notice.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 *  02111-1307  USA.
 *
 */

#include "vice.h"

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

#include "archdep.h"
#include "attach.h"
#include "autostart.h"
#include "charset.h"
#include "cmdline.h"
#include "initcmdline.h"
#include "lib.h"
#include "log.h"
#include "machine.h"
#include "resources.h"
#include "tape.h"
#include "translate.h"
#include "util.h"


static char *autostart_string = NULL;
static char *startup_disk_images[4];
static char *startup_tape_image;
static unsigned int autostart_mode;


static void cmdline_free_autostart_string(void)
{
    lib_free(autostart_string);
    autostart_string = NULL;
}

static int cmdline_help(const char *param, void *extra_param)
{
    cmdline_show_help(NULL);
    exit(0);

    return 0;   /* OSF1 cc complains */
}

static int cmdline_config(const char *param, void *extra_param)
{
    /* "-config" needs to be handled before this gets called
       but it also needs to be registered as a cmdline option,
       hence this kludge. */
    return 0;
}

static int cmdline_default(const char *param, void *extra_param)
{
    return resources_set_defaults();
}

static int cmdline_autostart(const char *param, void *extra_param)
{
    cmdline_free_autostart_string();
    autostart_string = lib_stralloc(param);
    autostart_mode = AUTOSTART_MODE_RUN;
    return 0;
}

static int cmdline_autoload(const char *param, void *extra_param)
{
    cmdline_free_autostart_string();
    autostart_string = lib_stralloc(param);
    autostart_mode = AUTOSTART_MODE_LOAD;
    return 0;
}

#if !defined(__OS2__) && !defined(__BEOS__)
static int cmdline_console(const char *param, void *extra_param)
{
    console_mode = 1;
    return 0;
}
#endif


static int cmdline_attach(const char *param, void *extra_param)
{
    int unit = vice_ptr_to_int(extra_param);

    switch (unit) {
      case 1:
        lib_free(startup_tape_image);
        startup_tape_image = lib_stralloc(param);
        break;
      case 8:
      case 9:
      case 10:
      case 11:
        lib_free(startup_disk_images[unit - 8]);
        startup_disk_images[unit - 8] = lib_stralloc(param);
        break;
      default:
        archdep_startup_log_error("cmdline_attach(): unexpected unit number %d?!\n",
                                  unit);
    }

    return 0;
}

static const cmdline_option_t common_cmdline_options[] = {
    { "-help", CALL_FUNCTION, 0,
      cmdline_help, NULL, NULL, NULL,
      USE_PARAM_STRING, USE_DESCRIPTION_ID,
      IDCLS_UNUSED, IDCLS_SHOW_COMMAND_LINE_OPTIONS,
      NULL, NULL },
    { "-?", CALL_FUNCTION, 0,
      cmdline_help, NULL, NULL, NULL,
      USE_PARAM_STRING, USE_DESCRIPTION_ID,
      IDCLS_UNUSED, IDCLS_SHOW_COMMAND_LINE_OPTIONS,
      NULL, NULL },
    { "-h", CALL_FUNCTION, 0,
      cmdline_help, NULL, NULL, NULL,
      USE_PARAM_STRING, USE_DESCRIPTION_ID,
      IDCLS_UNUSED, IDCLS_SHOW_COMMAND_LINE_OPTIONS,
      NULL, NULL },
#if (!defined  __OS2__ && !defined __BEOS__)
    { "-console", CALL_FUNCTION, 0,
      cmdline_console, NULL, NULL, NULL,
      USE_PARAM_STRING, USE_DESCRIPTION_ID,
      IDCLS_UNUSED, IDCLS_CONSOLE_MODE,
      NULL, NULL },
    { "-core", SET_RESOURCE, 0,
      NULL, NULL, "DoCoreDump", (resource_value_t)1,
      USE_PARAM_STRING, USE_DESCRIPTION_ID,
      IDCLS_UNUSED, IDCLS_ALLOW_CORE_DUMPS,
      NULL, NULL },
    { "+core", SET_RESOURCE, 0,
      NULL, NULL, "DoCoreDump", (resource_value_t)0,
      USE_PARAM_STRING, USE_DESCRIPTION_ID,
      IDCLS_UNUSED, IDCLS_DONT_ALLOW_CORE_DUMPS,
      NULL, NULL },
#else
    { "-debug", SET_RESOURCE, 0,
      NULL, NULL, "DoCoreDump", (resource_value_t)1,
      USE_PARAM_STRING, USE_DESCRIPTION_ID,
      IDCLS_UNUSED, IDCLS_DONT_CALL_EXCEPTION_HANDLER,
      NULL, NULL },
    { "+debug", SET_RESOURCE, 0,
      NULL, NULL, "DoCoreDump", (resource_value_t)0,
      USE_PARAM_STRING, USE_DESCRIPTION_ID,
      IDCLS_UNUSED, IDCLS_CALL_EXCEPTION_HANDLER,
      NULL, NULL },
#endif
    { NULL }
};

static const cmdline_option_t vsid_cmdline_options[] = {
    { NULL }
};

/* These are the command-line options for the initialization sequence.  */

static const cmdline_option_t cmdline_options[] = {
    { "-default", CALL_FUNCTION, 0,
      cmdline_default, NULL, NULL, NULL,
      USE_PARAM_STRING, USE_DESCRIPTION_ID,
      IDCLS_UNUSED, IDCLS_RESTORE_DEFAULT_SETTINGS,
      NULL, NULL },
    { "-config", CALL_FUNCTION, 1,
      cmdline_config, NULL, NULL, NULL,
      USE_PARAM_ID, USE_DESCRIPTION_ID,
      IDCLS_P_FILE, IDCLS_SPECIFY_CONFIG_FILE,
      NULL, NULL },
    { "-autostart", CALL_FUNCTION, 1,
      cmdline_autostart, NULL, NULL, NULL,
      USE_PARAM_ID, USE_DESCRIPTION_ID,
      IDCLS_P_NAME, IDCLS_ATTACH_AND_AUTOSTART,
      NULL, NULL },
    { "-autoload", CALL_FUNCTION, 1,
      cmdline_autoload, NULL, NULL, NULL,
      USE_PARAM_ID, USE_DESCRIPTION_ID,
      IDCLS_P_NAME, IDCLS_ATTACH_AND_AUTOLOAD,
      NULL, NULL },
    { "-1", CALL_FUNCTION, 1,
      cmdline_attach, (void *)1, NULL, NULL,
      USE_PARAM_ID, USE_DESCRIPTION_ID,
      IDCLS_P_NAME, IDCLS_ATTACH_AS_TAPE,
      NULL, NULL },
    { "-8", CALL_FUNCTION, 1,
      cmdline_attach, (void *)8, NULL, NULL,
      USE_PARAM_ID, USE_DESCRIPTION_ID,
      IDCLS_P_NAME, IDCLS_ATTACH_AS_DISK_8,
      NULL, NULL },
    { "-9", CALL_FUNCTION, 1,
      cmdline_attach, (void *)9, NULL, NULL,
      USE_PARAM_ID, USE_DESCRIPTION_ID,
      IDCLS_P_NAME, IDCLS_ATTACH_AS_DISK_9,
      NULL, NULL },
    { "-10", CALL_FUNCTION, 1,
      cmdline_attach, (void *)10, NULL, NULL,
      USE_PARAM_ID, USE_DESCRIPTION_ID,
      IDCLS_P_NAME, IDCLS_ATTACH_AS_DISK_10,
      NULL, NULL },
    { "-11", CALL_FUNCTION, 1,
      cmdline_attach, (void *)11, NULL, NULL,
      USE_PARAM_ID, USE_DESCRIPTION_ID,
      IDCLS_P_NAME, IDCLS_ATTACH_AS_DISK_11,
      NULL, NULL },
    { NULL }
};

int initcmdline_init(void)
{
    const cmdline_option_t *main_cmdline_options =
        vsid_mode ? vsid_cmdline_options : cmdline_options;

    if (cmdline_register_options(common_cmdline_options) < 0)
        return -1;

    if (cmdline_register_options(main_cmdline_options) < 0)
        return -1;

    return 0;
}

int initcmdline_check_psid(void)
{
    /* Check for PSID here since we don't want to allow autodetection
       in autostart.c. */
    if (vsid_mode) {
        if (autostart_string != NULL
            && machine_autodetect_psid(autostart_string) == -1) {
            log_error(LOG_DEFAULT, "`%s' is not a valid PSID file.",
                      autostart_string);
            return -1;
        }
    }

    return 0;
}

int initcmdline_check_args(int argc, char **argv)
{
    if (cmdline_parse(&argc, argv) < 0) {
        archdep_startup_log_error("Error parsing command-line options, bailing out. For help use '-help'\n");
        return -1;
    }

    /* The last orphan option is the same as `-autostart'.  */
    if (argc >= 1 && autostart_string == NULL) {
        autostart_string = lib_stralloc(argv[1]);
        argc--, argv++;
    }

    if (argc > 1) {
        int len = 0, j;

        for (j = 1; j < argc; j++)
            len += (int)strlen(argv[j]);

        {
            char *txt = lib_calloc(1, len + argc + 1);
            for (j = 1; j < argc; j++)
                strcat(strcat(txt, " "), argv[j]);
            archdep_startup_log_error("Extra arguments on command-line: %s\n",
                                      txt);
            lib_free(txt);
        }
        return -1;
    }

    return 0;
}

/* These are a helper function for the `-autostart' command-line option.  It
   replaces all the $[0-9A-Z][0-9A-Z] patterns in `string' and returns it.  */
static char * hexstring_to_byte( char * source, char * destination )
{
    char * next = source + 1;
    char c;
    BYTE value = 0;
    int digit = 0;
    
    while ( *next && digit++ < 2) {

        value <<= 4;

        c = toupper( *next++ );

        if (c >= 'A' && c <= 'F' ) {
            value += c - 'A';
        }
        else if ( isdigit(c) ) {
            value += c - '0';
        }
        else {
            break;
        }
    }

    if (digit < 2) {
        value = *source;
        next = source + 1;
    }

    *destination = value;

    return next;
}

static char *replace_hexcodes(char * source)
{
    char * destination = lib_stralloc(source ? source : "");

    if ( destination ) {
        char * pread = destination;
        char * pwrite = destination;

        while ( *pread != 0 ) {
            if ( *pread == '$' ) {
                pread = hexstring_to_byte( pread, pwrite++ );
            }
            else {
                *pwrite ++ = *pread ++;
            }
        }
        *pwrite = 0;
    }

    return destination;
}

void initcmdline_check_attach(void)
{
    if (!vsid_mode) {
        /* Handle general-purpose command-line options.  */

        /* `-autostart' */
        if (autostart_string != NULL) {
            char *tmp;

            /* Check for image:prg -format.  */
            tmp = strrchr(autostart_string, ':');
            if (tmp) {
                char *autostart_prg_name;
                char *autostart_file;

                autostart_file = lib_stralloc(autostart_string);
                autostart_prg_name = strrchr(autostart_file, ':');
                *autostart_prg_name++ = '\0';
                /* Does the image exist?  */
                if (util_file_exists(autostart_file)) {
                    char *name;

                    charset_petconvstring((BYTE *)autostart_prg_name, 0);
                    name = replace_hexcodes(autostart_prg_name);
                    autostart_autodetect(autostart_file, name, 0,
                                         autostart_mode);
                    lib_free(name);
                } else
                    autostart_autodetect(autostart_string, NULL, 0,
                                         autostart_mode);
                lib_free(autostart_file);
            } else {
                autostart_autodetect(autostart_string, NULL, 0,
                                     autostart_mode);
            }
        }
        /* `-8', `-9', `-10' and `-11': Attach specified disk image.  */
        {
            int i;

            for (i = 0; i < 4; i++) {
                if (startup_disk_images[i] != NULL
                    && file_system_attach_disk(i + 8, startup_disk_images[i])
                    < 0)
                    log_error(LOG_DEFAULT,
                              "Cannot attach disk image `%s' to unit %d.",
                              startup_disk_images[i], i + 8);
            }
        }

        /* `-1': Attach specified tape image.  */
        if (startup_tape_image && tape_image_attach(1, startup_tape_image) < 0)
            log_error(LOG_DEFAULT, "Cannot attach tape image `%s'.",
                      startup_tape_image);

    }

    cmdline_free_autostart_string();
}

