// This is a minimal wrapper for s7, intended to be usable as a
// bootstrapper when assembling the code.
//
// If there are no arguments, then read from stdin.
// If there is at least one argument, it is the name of a .scm file to be loaded.
// That argument can be "-", to indicate reading from stdin.
// No options are recognised here.
//
// Any other arguments (which are allowed to look like options) are put into *command-line*.
//
// This file is part of Beastie <https://purl.org/nxg/dist/beastie>
// SPDX-FileCopyrightText: 2023 Norman Gray <https://nxg.me.uk>
// SPDX-License-Identifier: BSD-2-Clause

#include <stdio.h>
#include <stdlib.h>             // for exit()
#include <string.h>             // for strcmp

#include "s7.h"
#include "util.h"

#include "version.h"
static const char version_string[] = "version "
    BEASTIE_VERSION ", " BEASTIE_RELEASEDATE
    " (s7 " S7_VERSION ")";

#include "readermacros.inc"
void initialise_runtime(s7_scheme*);  // from core.c

static const char* progname;

void beastie0_exit(void)
{
    error_exit("beastie bootstrap program, %s\n"
               "Usage:\n"
               "  %s [prog.scm arg...]  (program can be \"-\")",
               version_string, progname);
}

void scheme_abort_s7(int lineno)
{
    fprintf(stderr, "s7 abort: line %d\n", lineno);
    exit(1);
}

s7_scheme* S7;

int main(int argc, char** argv)
{

    S7 = s7_init();
    s7_load_c_string(S7, readermacros_scm, readermacros_scm_len);
    const char* def_reader_macros =
        "(set! *#readers*"
        "      `((#\\\" . ,dquotes-reader*)"
        "        (#\\; . ,srfi62-comments)"
        "         . ,*#readers*))";
    s7_load_c_string(S7, def_reader_macros, strlen(def_reader_macros));
    initialise_runtime(S7);

    progname = argv[0];
    const char* scm_file = NULL;

    s7_pointer command_line_arguments = s7_nil(S7);
    for (int i=argc-1; i>=1; i--) {
        if (i == 1) {
            // treat argument one specially: this can be
            //   * a file name to be read (saved into scm_file)
            //   * the string "-" to indicate reading from stdin
            //   * nothing else: if any option appears here, print an error message
            if (strcmp(argv[1], "-") == 0) {
                scm_file = NULL; // same as initial value
            } else if (argv[1][0] == '-') {
                beastie0_exit();
            } else {
                scm_file = argv[1];
            }
        }
        command_line_arguments = s7_cons(S7,
                                         s7_make_string(S7, argv[i]),
                                         command_line_arguments);
    }
    s7_define_constant(S7, "*command-line*", command_line_arguments);

    s7_pointer result;

    if (scm_file) {
        // The more obvious thing to do here is
        //    int status = s7_load(S7, scm_file) ? 0 : 1;
        // but when evaluating scm_file, this results in (current-input-port)
        // being that file, which isn't useful when that file wants to use
        // (current-input-port) to refer to the stdin _here_.
        //
        // Return #f on success, or an error message if anything goes wrong.
        s7_define_constant(S7, "*input-file*", s7_make_string(S7, scm_file));
        result = s7_eval_c_string(S7,
                                  "(catch #t"
                                  "  (lambda ()"
                                  "    (call-with-input-file *input-file*"
                                  "      (let ((outenv (curlet)))"
                                  "        (lambda (p)"
                                  "          (let loop ((e (read p)))"
                                  "            (unless (eof-object? e)"
                                  "              (eval e outenv)"
                                  "              (loop (read p)))))))"
                                  "    #f)"
                                  "  (lambda (tag msg)"
                                  "    (apply format #f msg)))");
    } else {
        s7_define_constant(S7, "*input-file*", s7_make_string(S7, "-"));
        result = s7_eval_c_string(S7,
                                  "(catch #t"
                                  "  (lambda ()"
                                  "    (let ((outenv (curlet)))"
                                  "      (let loop ((e (read)))"
                                  "        (cond ((eof-object? e) #f)"
                                  "              (else"
                                  "               (eval e outenv)"
                                  "               (loop (read)))))))"
                                  "  (lambda (tag msg)"
                                  "    (apply format #f msg)))");
    }

    int status;

    if (s7_boolean(S7, result)) {
        s7_display(S7, result, s7_current_error_port(S7));
        fprintf(stderr, "\n");

        status = 1;
    } else {
        status = 0;
    }

    exit(status);
}
