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

 Example source code of an application which runs a BASIC program (test.basic)
 synchronously and adds an external variable "APP.VERSION$". It's a good idea to
 have a common prefix ("APP" in this example) before the actual name of any
 variable you add in order to minimize conflicts with names typically used in
 BASIC programs (and to make the source code more readable).

*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <exec/exec.h>
#include <utility/tagitem.h>
#include <clib/exec_protos.h>
#include <pragmas/exec_pragmas.h>

// includes for BASIC.library

#include "include/basic.h"
#include "clib/basiclib_protos.h"
#include "pragmas/basiclib_pragmas.h"

// prototypes

extern __saveds LONG registry(void *context, UBYTE *symbol, UWORD method, struct basic_value *value);

// globals

struct Library *BASICBase;

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

 Main entry point. Note that this program should be started with the stack set
 to 16 KB or higher (because the interpreter is executed synchronously, ie. it
 uses our stack).

*/

int
main(int argc, UBYTE **argv)
{
    int rc = 20;

    if (BASICBase = OpenLibrary("BASIC.library", BASIC_VERSION))
    {
        // prepare to create interpreter with registry hook (to add variables to BASIC)

        static struct TagItem ctags[] = { BASIC_CONSOLE, (ULONG)NULL, BASIC_REGISTRY, (ULONG)registry, TAG_END };

        // use current console (BASIC_CONSOLE, NULL) if not started via wb, ie. if argc != 0

        void *context;

        if (argc)
            context = basic_context(&ctags[0]);
        else
            context = basic_context(&ctags[1]);

        if (context)
        {
            static struct TagItem ltags[] = { BASIC_FILENAME, (ULONG)"test.basic", TAG_END };

            // load test program into interpreter

            if (basic_load(context, ltags))
            {
                printf("Error loading program test.basic (error code %d)\n", rc);
            }
            else
            {
                // main loop: execute program synchronously

                do
        {
                    rc = basic_iterate(context);

                } while (rc == BASIC_OK);
            }

            // dispose of interpreter context

            rc = basic_dispose(context);
        }
        else
            puts("Failed to create interpreter context");

        CloseLibrary(BASICBase);
    }
    else
        puts("Can not open BASIC.library");

    return(rc);
}

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

 This hook function is called by the BASIC interpreter to query or set external
 symbols (variables). This example implements the read-only variable VERSION$

*/

__saveds LONG
registry(void *context, UBYTE *symbol, UWORD method, struct basic_value *value)
{
    // decide if <symbol> (guaranteed to be uppercase) is maintained by this function

    if (memcmp(symbol, "APP.VERSION$", 13) == 0)
    {
        // are we expected to read or to set the symbol ?

        switch (method)
        {
            case BASIC_QUERY:

                {
                    static struct basic_string bstring;

                    // our internal version string

                    bstring.buffer = "1.0";
                    bstring.length = 3;

                    // return the string as BASIC value

                    value->type   = BASIC_STRING;
                    value->string = &bstring;

                    // signal that we have a result

                    return(BASIC_ACCEPT);
                }

            case BASIC_SET:

                // do not permit VERSION$ to be changed

                basic_message(context, "APP.VERSION$ is a read-only variable !");

                return(BASIC_STOP);
        }
    }

    // defer processing to interpreter

    return(BASIC_DEFER);
}
