#include <stdarg.h>
#include <libmawk/mawk.h>
#include <libmawk/init.h>
#include <libmawk/code.h>
#include <libmawk/files.h>
#include <libmawk/array_generic.h>
#include <libmawk/vio.h>
#include <libmawk/vio_orig.h>
#include <libmawk/vio_fifo.h>
#include <libmawk/execute.h>

/* initialize a new maw_state_t, load script, run BEGIN blocks */
mawk_state_t *libmawk_initialize(int argc, char *argv[]);

/* Or the same in 3 stages:
	- stage 1 creates mawk state
	- stage 2 processes CLI args and loads and parses scripts
	- stage 3 runs BEGIN blocks

	Creating "builtins" should happen between stage 1 and 2.
	Injecting script (from elsewhere than argv[]) should happen between
	stage 1 and 2. It is okay to not specify any script (or any argument)
	in stage2.
*/
mawk_state_t *libmawk_initialize_stage1(void);
int libmawk_initialize_stdio(mawk_state_t *m, int stdin_apps, int stdout_apps, int stderr_apps); /* set up stdio; where *_apps is zero use a pipe, where non-zero bind to app's */
mawk_state_t *libmawk_initialize_stage2(mawk_state_t *m, int argc, char *argv[]);
mawk_state_t *libmawk_initialize_stage3(mawk_state_t *m);

/* execute exit(0) in the script and free up all memory used by m */
void libmawk_uninitialize(mawk_state_t *m);

/* Or the same in 3 stages:
	- stage 1 executes exit(0) (executing END { } if not already within END)
	- stage 2 free m
	Between stage 1 and stage 2 the application may read states changed by
	the script in END
*/
void libmawk_uninitialize_stage1(mawk_state_t *m);
void libmawk_uninitialize_stage2(mawk_state_t *m);



/* run all main blocks until input runs out */
void libmawk_run_main(mawk_state_t *m);

/* append \0 terminated string to the input buffer; returns -1 on error */
int libmawk_append_input(mawk_state_t *m, const char *input_str);

/* append data to the input buffer; returns -1 on error */
int libmawk_append_ninput(mawk_state_t *m, const char *input, int len);

/* close the input buffer (eof to the script) in case it was a pipe
   between the app and awk - else don't do anything */
int libmawk_close_input(mawk_state_t *m);


/* set value of a cell */
mawk_cell_t *libmawk_set_cell(mawk_state_t *MAWK, mawk_cell_t *cell, const char argtype, ...);

/* set value of a cell from a pointer */
mawk_cell_t *libmawk_set_cellp(mawk_state_t *MAWK, mawk_cell_t *cell, const char argtype, void *argp);

/* call an awk function; argtype is a string consists of a character for
   each argument depending on the argument type:
   - d for integer
   - f for float
   - s for string
   If res is not NULL, it is destroyed (regardless of the return value)
   and the result cell is copied in res and res needs to be cell_destroyed
   by the caller.
   Returns 0 on success or -1 on error.
*/
mawk_exec_result_t libmawk_call_function(mawk_state_t *MAWK, const char *fname, mawk_cell_t *retc, const char *argtypes, ...);

/* Same as libmawk_call_function, except this one takes an array of pointer to the arguments */
mawk_exec_result_t libmawk_call_functionp(mawk_state_t *MAWK, const char *fname, mawk_cell_t *retc, const char *argtypes, void **args);

/* Same as libmawk_call_function, except this one takes an array of pointer to read-only cells */
mawk_exec_result_t libmawk_call_functionc(mawk_state_t *MAWK, const char *fname, mawk_cell_t *retc, int argc, const mawk_cell_t *argv);

/* free an allocated cell (use after libmawk_call_function */
void libmawk_cell_destroy(mawk_state_t *MAWK, mawk_cell_t *c);


/* convert a cell to string in buff, return pointer to buff */
char *libmawk_print_cell(mawk_state_t *MAWK, const mawk_cell_t *c, char *buff, int buffsize);

/* resolve a variable by name; the caller shouldn't change anything on the cell because of
   the possible side effects required on write. The variable is not necessarily
   scalar. */
const mawk_cell_t *libmawk_get_var(mawk_state_t *MAWK, const char *vname);

/* resolve an element of an array by array name and index and returns 1
   if the element is found. If result is non-NULL, the member mawk_cell_t is copied
   there. Changing result will not affect the value in the actual array and
   the caller is responsible for destroying the cell.
   If arr_name doesn't name an array or upon other error, return value is -1
   On succes returns 1
   NOTE: if result is non-NULL, it is destroyed.
   If create is non-zero, create non-existing element with empty value
   */
int libmawk_get_array_at(mawk_state_t *MAWK, const char *arr_name, const char *idx, mawk_cell_t *result, int create);

/* set array at a specific index; valtype/val semantics are the same as for
   the libmawk_cell_set*() */
int libmawk_set_array_atv(mawk_state_t *MAWK, const char *arr_name, const char *idx, const char valtype, va_list *ap);
int libmawk_set_array_atp(mawk_state_t *MAWK, const char *arr_name, const char *idx, const char valtype, void *val);
int libmawk_set_array_at(mawk_state_t *MAWK, const char *arr_name, const char *idx, const char valtype, ...);

/* same for scalars */
int libmawk_set_scalarv(mawk_state_t *MAWK, const char *var_name, const char valtype, va_list *ap);
int libmawk_set_scalarp(mawk_state_t *MAWK, const char *var_name, const char valtype, void *val);
int libmawk_set_scalar(mawk_state_t *MAWK, const char *var_name, const char valtype, ...);

/* register a new function implemented in callback. Returns 0 on success. */
int libmawk_register_function(mawk_state_t *MAWK, const char *fname, libmawk_c_function *callback);

/* calculate the cell pointer of arugmnet number n on the stack from within
   a C function called back from awk */
mawk_cell_t *libmawk_cfunc_arg(mawk_cell_t *sp, int num_args, int n);

/* calculate the cell pointer of the return value on the stack from within
   a C function called back from awk */
mawk_cell_t *libmawk_cfunc_ret(mawk_cell_t *sp, int num_args);

/* *** register new variables _before stage2_ *** */
/* register a new array; if arr_imp is NULL, the generic (_orig) array
   implementation is used, else the virtualized array hooks provided by imp */
SYMTAB *libmawk_register_array(mawk_state_t *MAWK, const char *name, array_imp_t *arr_imp);
/* register a scalar; type must be C_STRING (val is a char *) or C_NUM (val
   is a mawk_num_t *) */
SYMTAB *libmawk_register_scalar(mawk_state_t *MAWK, const char *name, mawk_celltype_t type, void *val);


/* return the numeric value of a cell in mawk's internal number type or
   int or double */
mawk_num_t libmawk_cell2num(mawk_state_t *MAWK, const mawk_cell_t *cp);
int libmawk_cell2int(mawk_state_t *MAWK, const mawk_cell_t *cp);
double libmawk_cell2double(mawk_state_t *MAWK, const mawk_cell_t *cp);

/* empty mawk_cell_t initializer - cells should start out with this value */
#define libmawk_empty_cell {0, NULL, {0}}
