/*
 * Copyright (c) 2006, Adam Dunkels
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * Modified to support simple string variables and functions by David Mitchell
 * November 2008.
 * Changes and additions are marked 'string additions' throughout
 *
 * Modified to support fixed point arithmetic, and number of math and io and
 * hardware functions by Marijan Kostrun, January-February 2018.
 * uBasic-Plus Copyright (c) 2017-2018, M. Kostrun
 *
 * Modified to support multiple programs and use a structure to hold
 * each program state. 2025 Steve Karg <skarg@users.sourceforge.net>
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#ifndef __UBASIC_H__
#define __UBASIC_H__
#include <stdint.h>
#include <stdarg.h>
#include <stdio.h>
#include "config.h"
#include "tokenizer.h"

/* define a status structure with bit fields */
typedef union {
    uint8_t byte;
    struct {
        uint8_t notInitialized : 1;
        uint8_t stringstackModified : 1;
        uint8_t bit2 : 1;
        uint8_t bit3 : 1;
        uint8_t bit4 : 1;
        uint8_t WaitForSerialInput : 1;
        uint8_t Error : 1;
        uint8_t isRunning : 1;
    } bit;
} UBASIC_STATUS;

#ifndef UBASIC_FOR_LOOP_STACK_DEPTH
#define UBASIC_FOR_LOOP_STACK_DEPTH 4
#endif
struct ubasic_for_state {
    uint16_t line_after_for;
    uint8_t for_variable;
    UBASIC_VARIABLE_TYPE to;
    UBASIC_VARIABLE_TYPE step;
};

#ifndef UBASIC_WHILE_LOOP_STACK_DEPTH
#define UBASIC_WHILE_LOOP_STACK_DEPTH 4
#endif
struct ubasic_while_state {
    uint16_t line_while;
    int16_t line_after_endwhile;
};

#ifndef UBASIC_GOSUB_STACK_DEPTH
#define UBASIC_GOSUB_STACK_DEPTH 10
#endif

#ifndef UBASIC_IF_THEN_STACK_DEPTH
#define UBASIC_IF_THEN_STACK_DEPTH 4
#endif

enum {
    UBASIC_RECALL_STORE_TYPE_VARIABLE = 0,
    UBASIC_RECALL_STORE_TYPE_STRING = 1,
    UBASIC_RECALL_STORE_TYPE_ARRAY = 2,
    UBASIC_RECALL_STORE_TYPE_MAX = 3
};

/**
 * A timer.
 *
 * This structure is used for declaring a timer. The timer must be set
 * with mstimer_set() before it can be used.
 */
struct ubasic_mstimer {
    uint32_t start;
    uint32_t interval;
};

struct ubasic_data {
    UBASIC_STATUS status;
    uint8_t input_how;
    struct ubasic_tokenizer tree;

#if defined(UBASIC_VARIABLE_TYPE_ARRAY)
    UBASIC_VARIABLE_TYPE arrays_data[UBASIC_VARIABLE_TYPE_ARRAY];
    int16_t free_arrayptr;
    int16_t arrayvariable[UBASIC_VARNUM_MAX];
#endif
    /* entire program */
    const char *program;
    /* points to current statement */
    const char *program_ptr;
    /* copy of the current statement until end-of-line */
    char location[UBASIC_STATEMENT_SIZE];

    uint16_t gosub_stack[UBASIC_GOSUB_STACK_DEPTH];
    uint8_t gosub_stack_ptr;

    struct ubasic_for_state for_stack[UBASIC_FOR_LOOP_STACK_DEPTH];
    uint8_t for_stack_ptr;

    int16_t if_stack[UBASIC_IF_THEN_STACK_DEPTH];
    uint8_t if_stack_ptr;

    struct ubasic_while_state while_stack[UBASIC_WHILE_LOOP_STACK_DEPTH];
    uint8_t while_stack_ptr;

    UBASIC_VARIABLE_TYPE variables[UBASIC_VARNUM_MAX];

#if defined(UBASIC_SCRIPT_HAVE_STORE_VARS_IN_FLASH)
    uint8_t varnum;
#endif

#if defined(UBASIC_VARIABLE_TYPE_STRING)
    char stringstack[UBASIC_STRING_BUFFER_LEN_MAX];
    int16_t freebufptr;
    int16_t stringvariables[UBASIC_STRING_VAR_LEN_MAX];
#endif

#if defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL)
    uint8_t input_varnum;
    uint8_t input_type;
    char statement[UBASIC_STATEMENT_SIZE];
#endif
#if defined(UBASIC_VARIABLE_TYPE_ARRAY)
    UBASIC_VARIABLE_TYPE input_array_index;
#endif
#if defined(UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS)
    uint32_t tic_toc_timer[UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS];
#endif
#if defined(UBASIC_SCRIPT_HAVE_SLEEP)
    struct ubasic_mstimer input_wait_timer;
    struct ubasic_mstimer sleep_timer;
#endif

/* API for hardware drivers */
#if defined(UBASIC_SCRIPT_HAVE_PWM_CHANNELS)
    void (*pwm_config)(uint16_t psc, uint16_t per);
    void (*pwm_write)(uint8_t ch, int32_t dutycycle);
    int32_t (*pwm_read)(uint8_t ch);
#endif
#if defined(UBASIC_SCRIPT_HAVE_GPIO_CHANNELS)
    void (*gpio_config)(uint8_t ch, int8_t mode, uint8_t freq);
    void (*gpio_write)(uint8_t ch, uint8_t pin_state);
    int32_t (*gpio_read)(uint8_t ch);
#endif
#if (                                              \
    defined(UBASIC_SCRIPT_HAVE_TICTOC_CHANNELS) || \
    defined(UBASIC_SCRIPT_HAVE_SLEEP) ||           \
    defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL))
    uint32_t (*mstimer_now)(void);
#endif
#if defined(UBASIC_SCRIPT_HAVE_ANALOG_READ)
    void (*adc_config)(uint8_t sampletime, uint8_t nreads);
    int32_t (*adc_read)(uint8_t channel);
#endif
#if defined(UBASIC_SCRIPT_HAVE_HARDWARE_EVENTS)
    int8_t (*hw_event)(uint8_t bit);
    void (*hw_event_clear)(uint8_t bit);
#endif
#if defined(UBASIC_SCRIPT_HAVE_RANDOM_NUMBER_GENERATOR)
    uint32_t (*random_uint32)(uint8_t size);
#endif
#if defined(UBASIC_SCRIPT_HAVE_STORE_VARS_IN_FLASH)
    void (*variable_write)(
        uint8_t Name, uint8_t Vartype, uint8_t datalen_bytes, uint8_t *dataptr);
    void (*variable_read)(
        uint8_t Name, uint8_t Vartype, uint8_t *dataptr, uint8_t *datalen);
#endif
#if defined(UBASIC_SCRIPT_HAVE_INPUT_FROM_SERIAL)
    int (*ubasic_getc)(void);
#endif
#if defined(UBASIC_SCRIPT_HAVE_PRINT_TO_SERIAL)
    void (*serial_write)(const char *buffer, uint16_t n);
#endif
#if defined(UBASIC_SCRIPT_HAVE_BACNET)
    void (*bacnet_create_object)(
        uint16_t object_type, uint32_t instance, char *object_name);
    void (*bacnet_write_property)(
        uint16_t object_type,
        uint32_t instance,
        uint32_t property_id,
        UBASIC_VARIABLE_TYPE value);
    UBASIC_VARIABLE_TYPE(*bacnet_read_property)
    (uint16_t object_type, uint32_t instance, uint32_t property_id);
#endif
};

BACNET_STACK_EXPORT
void ubasic_load_program(struct ubasic_data *data, const char *program);
BACNET_STACK_EXPORT
void ubasic_clear_variables(struct ubasic_data *data);
BACNET_STACK_EXPORT
int32_t ubasic_run_program(struct ubasic_data *data);
BACNET_STACK_EXPORT
uint8_t ubasic_execute_statement(struct ubasic_data *data, char *statement);
BACNET_STACK_EXPORT
void ubasic_halt_program(struct ubasic_data *data);
BACNET_STACK_EXPORT
bool ubasic_program_finished(struct ubasic_data *data);
BACNET_STACK_EXPORT
uint8_t ubasic_finished(struct ubasic_data *data);
BACNET_STACK_EXPORT
const char *ubasic_program_location(struct ubasic_data *data);

BACNET_STACK_EXPORT
uint8_t ubasic_waiting_for_input(struct ubasic_data *data);
BACNET_STACK_EXPORT
uint8_t ubasic_getline(struct ubasic_data *data, int ch);
BACNET_STACK_EXPORT
int ubasic_printf(struct ubasic_data *data, const char *format, ...);
BACNET_STACK_EXPORT
int ubasic_getc(struct ubasic_data *data);

BACNET_STACK_EXPORT
UBASIC_VARIABLE_TYPE
ubasic_get_variable(struct ubasic_data *data, char variable);
BACNET_STACK_EXPORT
void ubasic_set_variable(
    struct ubasic_data *data, char variable, UBASIC_VARIABLE_TYPE value);

#if defined(UBASIC_VARIABLE_TYPE_ARRAY)
BACNET_STACK_EXPORT
void ubasic_dim_arrayvariable(
    struct ubasic_data *data, char variable, int16_t size);
BACNET_STACK_EXPORT
void ubasic_set_arrayvariable(
    struct ubasic_data *data,
    char variable,
    uint16_t idx,
    UBASIC_VARIABLE_TYPE value);
BACNET_STACK_EXPORT
UBASIC_VARIABLE_TYPE
ubasic_get_arrayvariable(struct ubasic_data *data, char variable, uint16_t idx);
#endif

#if defined(UBASIC_VARIABLE_TYPE_STRING)
BACNET_STACK_EXPORT
int16_t ubasic_get_stringvariable(struct ubasic_data *data, uint8_t varnum);
BACNET_STACK_EXPORT
void ubasic_set_stringvariable(
    struct ubasic_data *data, uint8_t varnum, int16_t size);
#endif

/* API to interface and initialize the ported hardware drivers */
BACNET_STACK_EXPORT
void ubasic_port_init(struct ubasic_data *data);

#endif /* __UBASIC_H__ */
