/*
 * Copyright (c) 2021-2025 Symas Corporation
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * 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.
 * * Neither the name of the Symas Corporation 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 COPYRIGHT HOLDERS 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 COPYRIGHT
 * OWNER 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.
 */
#ifndef _GENAPI_H_
#define _GENAPI_H_

#define DISPLAY_ADVANCE true
#define DISPLAY_NO_ADVANCE false

typedef enum
  {
  refer_dest,
  refer_source,
  } refer_type_t;

void parser_display_internal( tree file_descriptor,
                              cbl_refer_t refer,
                              bool advance=DISPLAY_NO_ADVANCE);

void parser_first_statement( int lineno );

void parser_enter_file(const char *filename);
void parser_leave_file();
void parser_division( cbl_division_t division,
                      cbl_field_t *ret, size_t narg, cbl_ffi_arg_t args[] );
void parser_enter_program(const char *funcname, bool is_function, int *retval);
void parser_leave_program();

void parser_accept( cbl_refer_t refer, special_name_t special_e);
void parser_accept_exception( cbl_label_t *name );
void parser_accept_exception_end( cbl_label_t *name );

void parser_accept_envar( cbl_refer_t refer, cbl_refer_t envar,
                          cbl_label_t *error, cbl_label_t *not_error );
void parser_set_envar( cbl_refer_t envar, cbl_refer_t refer );

void parser_accept_command_line( cbl_refer_t tgt, cbl_refer_t src,
                          cbl_label_t *error, cbl_label_t *not_error );
void parser_accept_command_line_count( cbl_refer_t tgt );

void parser_accept_date_yymmdd( cbl_field_t *tgt );
void parser_accept_date_yyyymmdd( cbl_field_t *tgt );
void parser_accept_date_yyddd( cbl_field_t *tgt );
void parser_accept_date_yyyyddd( cbl_field_t *tgt );
void parser_accept_date_dow( cbl_field_t *tgt );
void parser_accept_date_hhmmssff( cbl_field_t *tgt );

void
parser_alphabet( cbl_alphabet_t& alphabet );
void
parser_alphabet_use( cbl_alphabet_t& alphabet );

void
parser_allocate( cbl_refer_t size_or_based, cbl_refer_t returning, bool initialized );
void
parser_free( size_t n, cbl_refer_t refers[] );

void
parser_add( size_t nC, cbl_num_result_t *C,
            size_t nA, cbl_refer_t *A,
            cbl_arith_format_t format,
            cbl_label_t *error,
            cbl_label_t *not_error,
            void *compute_error = NULL);  // This has to be cast to a tree pointer to int

void parser_arith_error( cbl_label_t *name );
void parser_arith_error_end( cbl_label_t *name );

void
parser_subtract(size_t nC, cbl_num_result_t *C,
                size_t nA, cbl_refer_t *A,
                size_t nB, cbl_refer_t *B,
                cbl_arith_format_t format,
                cbl_label_t *error,
                cbl_label_t *not_error,
                void *compute_error = NULL);  // This has to be cast to a tree pointer to int

void
parser_multiply(size_t nC, cbl_num_result_t *C,
                size_t nA, cbl_refer_t *A,
                size_t nB, cbl_refer_t *B,
                cbl_label_t *error,
                cbl_label_t *not_error,
                void *compute_error = NULL);  // This has to be cast to a tree pointer to int

void
parser_divide(size_t nC, cbl_num_result_t *C,
              size_t nA, cbl_refer_t *A,
              size_t nB, cbl_refer_t *B,
              cbl_refer_t remainder,
              cbl_label_t *error,
              cbl_label_t *not_error,
              void *compute_error = NULL);  // This has to be cast to a tree pointer to int

void
parser_add( struct cbl_refer_t tgt,
            struct cbl_refer_t a, struct cbl_refer_t b,
            enum cbl_round_t = truncation_e );

void
parser_subtract( struct cbl_refer_t tgt,
                 struct cbl_refer_t a, struct cbl_refer_t b,
                 enum cbl_round_t = truncation_e );

void
parser_multiply( struct cbl_refer_t tgt,
                 struct cbl_refer_t a, struct cbl_refer_t b,
                 enum cbl_round_t = truncation_e );

void
parser_divide( struct cbl_refer_t quotient,
               struct cbl_refer_t divisor,
               struct cbl_refer_t dividend,
               enum cbl_round_t = truncation_e,
               struct cbl_refer_t remainder = cbl_refer_t());

// void
// parser_exponentiation(  cbl_refer_t cref,
//                         cbl_refer_t aref,
//                         cbl_refer_t bref,
//                         cbl_round_t rounded = truncation_e );

void
parser_relop( struct cbl_field_t *tgt,
              struct cbl_refer_t a, enum relop_t, struct cbl_refer_t b );

void
parser_relop_long(struct cbl_field_t *tgt,
                  long a, enum relop_t, struct cbl_refer_t b );

void
parser_logop( struct cbl_field_t *tgt,
              struct cbl_field_t *a,  enum logop_t, struct cbl_field_t *b );

void
parser_setop( struct cbl_field_t *tgt,
              struct cbl_field_t *a,  enum setop_t, struct cbl_field_t *b );

void
parser_bitop( struct cbl_field_t *tgt,
              struct cbl_field_t *a,  enum bitop_t, size_t B );

void
parser_bitwise_op(struct cbl_field_t *tgt,
                  struct cbl_field_t *a,
                  enum bitop_t op,
                  size_t bitmask );

void
parser_classify( struct cbl_field_t *tgt,
                 struct cbl_refer_t  srca,  enum classify_t type );

void
parser_op( struct cbl_refer_t cref,
           struct cbl_refer_t aref, int op, struct cbl_refer_t bref,
           struct cbl_label_t *op_error);

cbl_field_t
determine_intermediate_type( const cbl_refer_t& aref,
                             int op,
                             const cbl_refer_t& bref );

void
parser_if( struct cbl_field_t *yn ); // value is 1 or 0
void
parser_else(void);
void
parser_fi(void);


void
parser_enter_paragraph( struct cbl_label_t *label );
void
parser_leave_paragraph( cbl_label_t *label );

void
parser_enter_section( struct cbl_label_t *label );
void
parser_leave_section( struct cbl_label_t *label );

void
parser_perform( struct cbl_label_t *label, bool suppress_nexting=false );

void
parser_perform_times( struct cbl_label_t *label, cbl_refer_t count );

void
parser_perform_start( struct cbl_perform_tgt_t *tgt );

void
parser_perform_conditional( struct cbl_perform_tgt_t *tgt );

void
parser_perform_conditional_end( struct cbl_perform_tgt_t *tgt );

/*
 * To perform once (not a loop) N is NULL because the user didn't provide a count.
 * tgt->to is NULL if the PERFORM statement has no THRU phrase.
 * For an in-line loop body, tgt->from.type == LblLoop, and tgt->to is NULL.
 */
void
parser_perform( struct cbl_perform_tgt_t *tgt, struct cbl_refer_t N );

/*
 * A simple UNTIL loop uses 1 varys element.  For VARY loops, the
 * VARY/AFTER phrases appear in varys in the same order as in the
 * COBOL text.
 */

// Either parser_perform_until() or parser_perform_inline_times() must appear
// after a parser_perform_start()
void
parser_perform_until(   struct cbl_perform_tgt_t *tgt,
                        bool test_before,
                        size_t nvary,
                        struct cbl_perform_vary_t *varys );

void
parser_perform_inline_times(struct cbl_perform_tgt_t *tgt,
                            struct cbl_refer_t count );

void
parser_see_stop_run( struct cbl_refer_t exit_status, const char name[] );

void
parser_program_hierarchy( const struct cbl_prog_hier_t& hier );
void
parser_end_program(const char *name=NULL);

void parser_sleep(cbl_refer_t seconds);

void parser_exit( cbl_refer_t refer, ec_type_t = ec_none_e );
void parser_exit_section(void);
void parser_exit_paragraph(void);
void parser_exit_perform( struct cbl_perform_tgt_t *tgt, bool cycle );
void parser_exit_program(void); // exits back to COBOL only, else continue

void
parser_display( const struct cbl_special_name_t *upon,
                struct cbl_refer_t args[], size_t n,
                bool advance = DISPLAY_ADVANCE );

void parser_display_field(cbl_field_t *fld);

void parser_display_literal(const char *literal,
                            bool advance = DISPLAY_ADVANCE);

void
parser_assign( size_t nC, cbl_num_result_t *C,
               struct cbl_refer_t from,
               cbl_label_t *on_error,
               cbl_label_t *not_error,
               cbl_label_t *compute_error );

void parser_move(struct cbl_refer_t to,
                 struct cbl_refer_t from,
                 cbl_round_t rounded=truncation_e,
     bool skip_fill_from = false);

void parser_move( size_t ntgt, cbl_refer_t *tgts,
                  cbl_refer_t src, cbl_round_t rounded=truncation_e );

void parser_initialize_table( size_t ntgt, cbl_refer_t src,
                              size_t nspan, const cbl_bytespan_t spans[],
                              size_t table, // symbol table index
                              size_t ntbl, const cbl_subtable_t tbls[] );

void parser_set_pointers( size_t ntgt, cbl_refer_t *tgts, cbl_refer_t src );

void
parser_symbol_add(struct cbl_field_t *field);

void
parser_initialize(struct cbl_refer_t refer, bool like_parser_symbol_add=false);

void
parser_initialize_programs(size_t nprog, struct cbl_refer_t *progs);

void
parser_label_label( struct cbl_label_t *label );

void
parser_label_goto( struct cbl_label_t *label );

void
parser_goto( cbl_refer_t value, size_t narg, cbl_label_t * const labels[] );

void
parser_alter( cbl_perform_tgt_t *tgt );

void
parser_set_conditional88( struct cbl_refer_t tgt, bool which_way );
void
parser_set_numeric(struct cbl_field_t *tgt, ssize_t value);

void
parser_field_attr_set( cbl_field_t *tgt, cbl_field_attr_t attr, bool on_off = true );

void
parser_file_add(struct cbl_file_t *file);

void
parser_file_open( struct cbl_file_t *file, int mode_char );
void
parser_file_open( size_t n, struct cbl_file_t *files[], int mode_char );

void
parser_file_close( struct cbl_file_t *file, file_close_how_t how = file_close_no_how_e);

void
parser_file_read( struct cbl_file_t *file,
                  struct cbl_refer_t buffer,
                  int where );

void
parser_file_start( struct cbl_file_t *file, relop_t op, int flk,
                   cbl_refer_t = cbl_refer_t() );

/*
 * Write *field* to *file*.  *after* is a bool where false
 * means BEFORE. *nlines* is the number of lines, frequently
 * FldLiteralN.  To indicate PAGE, nlines is the literal "PAGE", with
 * quoted_e off.
 *
 * According to the 2014 standard, the lack of an ADVANCING clause implies
 * AFTER ADVANCING 1 LINE.  *nlines* is be zero to write a line without
 * prepending or appending newlines.  See section 14.9.47.1 paragraph 22)
 *
 * At present, we don't have enough information to implement PAGE
 * correctly, because we don't know the page size (in lines) of the
 * output device. Rather than doing nothing, we issue a 0x0C form feed
 * character.
 */
void
parser_file_write(  cbl_file_t *file,
                    cbl_field_t *source,
                    bool after,
                    cbl_refer_t& nlines,
                    bool sequentially);

void
parser_file_rewrite( cbl_file_t *file, cbl_field_t *field,
                     bool sequentially );

void
parser_file_delete( cbl_file_t *file, bool sequentially );

#if condition_lists
struct cbl_conditional_t {
  cbl_field_t *tgt;
  cbl_refer_t& lhs;
  unsigned int op;
  cbl_refer_t& rhs;
};
#endif

void
parser_lsearch_start(   cbl_label_t *name,
                        cbl_field_t *table,
                        cbl_field_t *index,
                        cbl_field_t *varying );

void parser_lsearch_conditional(cbl_label_t * name);
void parser_bsearch_conditional(cbl_label_t * name);

void parser_lsearch_when( cbl_label_t *name, cbl_field_t *conditional );
void
parser_bsearch_when(cbl_label_t *name,
                    cbl_refer_t key,
                    cbl_refer_t sarg,
                    bool ascending);

void parser_lsearch_end( cbl_label_t *name );
void parser_bsearch_end( cbl_label_t *name );

void
parser_bsearch_start( cbl_label_t *name, cbl_field_t *tgt );

void
parser_sort(cbl_refer_t table,
            bool duplicates,
            cbl_alphabet_t *alphabet,
            size_t nkey,
            cbl_key_t *keys );
void
parser_file_sort(   cbl_file_t *file,
                    bool duplicates,
                    cbl_alphabet_t *alphabet,
                    size_t nkey,
                    cbl_key_t *keys,
                    size_t ninput,
                    cbl_file_t **inputs,
                    size_t noutput,
                    cbl_file_t **outputs,
                    cbl_perform_tgt_t *in_proc,
                    cbl_perform_tgt_t *out_proc );
void
parser_file_merge(  cbl_file_t *file,
                    cbl_alphabet_t *alphabet,
                    size_t nkey,
                    cbl_key_t *keys,
                    size_t ninput,
                    cbl_file_t **inputs,
                    size_t noutput,
                    cbl_file_t **outputs,
                    cbl_perform_tgt_t *out_proc );

void
parser_release( cbl_field_t *record_area );

void
parser_exception_file( cbl_field_t *tgt, cbl_file_t* file = NULL );

void
parser_module_name( cbl_field_t *tgt, module_type_t type );

void
parser_intrinsic_numval_c( cbl_field_t *f,
                           cbl_refer_t& input,
                           bool locale,
                           cbl_refer_t& currency,
                           bool anycases,
                           bool test_numval_c = false);

void
parser_intrinsic_subst( cbl_field_t *f,
                        cbl_refer_t& ref1,
                        size_t argc,
                        cbl_substitute_t * argv );

void
parser_intrinsic_callv( cbl_field_t *f,
                        const char name[],
                        size_t argc,
                        cbl_refer_t * argv );

void
parser_intrinsic_call_0( cbl_field_t *tgt,
                       const char name[] );
void
parser_intrinsic_call_1( cbl_field_t *tgt,
                       const char name[],
                       cbl_refer_t& ref1 );
void
parser_intrinsic_call_2( cbl_field_t *tgt,
                       const char name[],
                       cbl_refer_t& ref1,
                       cbl_refer_t& ref2 );
void
parser_intrinsic_call_3( cbl_field_t *tgt,
                       const char name[],
                       cbl_refer_t& ref1,
                       cbl_refer_t& ref2,
                       cbl_refer_t& ref3 );
void
parser_intrinsic_call_4( cbl_field_t *tgt,
                       const char name[],
                       cbl_refer_t& ref1,
                       cbl_refer_t& ref2,
                       cbl_refer_t& ref3,
                       cbl_refer_t& ref4 );

void
parser_string_overflow( cbl_label_t *name );
void
parser_string_overflow_end( cbl_label_t *name );

void
parser_string(  cbl_refer_t tgt,
                cbl_refer_t pointer,
                size_t nsource,
                cbl_string_src_t *sources,
                cbl_label_t *overflow,
                cbl_label_t *not_overflow );

void
parser_unstring( cbl_refer_t src,
                 size_t ndelimited,
                 cbl_refer_t *delimiteds,
                 // into
                 size_t noutput,
                 cbl_refer_t *outputs,
                 cbl_refer_t *delimiters,
                 cbl_refer_t *counts,
                 cbl_refer_t pointer,
                 cbl_refer_t tally,
                 cbl_label_t *overflow,
                 cbl_label_t *not_overflow );

void parser_return_start( cbl_file_t *file, cbl_refer_t into );
void parser_return_atend( cbl_file_t *file );
void parser_return_notatend( cbl_file_t *file );
void parser_return_finish( cbl_file_t *file );

void parser_exception_prepare( const cbl_name_t statement_name,
                               const cbl_enabled_exceptions_array_t *enabled );

//void parser_exception_condition( cbl_field_t *ec );

struct cbl_exception_file;
struct cbl_exception_files_t;

void parser_exception_raise(ec_type_t ec);

void parser_call_exception( cbl_label_t *name );
void parser_call_exception_end( cbl_label_t *name );

//void parser_stash_exceptions(const cbl_enabled_exceptions_array_t *enabled);

void parser_match_exception(cbl_field_t *index,
                            cbl_field_t *blob);
void parser_check_fatal_exception();
void parser_clear_exception();

void parser_call_targets_dump();
size_t parser_call_target_update( size_t caller,
                                  const char extant[],
                                  const char mangled_tgt[] );

void parser_file_stash( struct cbl_file_t *file );

void parser_call( cbl_refer_t name,
                  cbl_refer_t returning,
                  size_t narg, cbl_ffi_arg_t args[],
                  cbl_label_t *except,
                  cbl_label_t *not_except,
                  bool is_function);

void parser_entry_activate( size_t iprog, const cbl_label_t *declarative );

void parser_entry( cbl_field_t *name,
                   size_t narg = 0, cbl_ffi_arg_t args[] = NULL);

bool is_ascending_key(cbl_refer_t key);

void register_main_switch(const char *main_string);

tree parser_cast_long(tree N);
void parser_print_long(tree N);
void parser_print_long(const char *fmt, tree N);
void parser_print_long(long N);
void parser_print_long(const char *fmt, long N); // fmt needs to have a %ls in it
void parser_print_string(const char *ach);
void parser_print_string(const char *fmt, const char *ach); // fmt needs to have a %s in it
void parser_set_statement(const char *statement);

char *initial_from_float128(cbl_field_t *field, _Float128 value);

void parser_set_handled(ec_type_t ec_handled);
void parser_set_file_number(int file_number);
void parser_exception_clear();

void parser_init_list_size(int count_of_variables);
void parser_init_list_element(cbl_field_t *field);
void parser_init_list();

tree file_static_variable(tree type, const char *name);

void parser_statement_begin();

#endif