/*
 * Copyright (c) 2018 Oticon A/S
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "cmdline_common.h"
#include <zephyr/types.h>
#include "hw_models_top.h"
#include "timer_model.h"
#include "cmdline.h"
#include <zephyr/toolchain.h>
#include <zephyr/arch/posix/posix_trace.h>
#include "native_tracing.h"

static int s_argc, test_argc;
static char **s_argv, **test_argv;

static struct args_struct_t *args_struct;
static int used_args;
static int args_aval;
#define ARGS_ALLOC_CHUNK_SIZE 20

void native_cleanup_cmd_line(void)
{
	if (args_struct != NULL) { /* LCOV_EXCL_BR_LINE */
		free(args_struct);
		args_struct = NULL;
	}
}

/**
 * Add a set of command line options to the program.
 *
 * Each option to be added is described in one entry of the input <args>
 * This input must be terminated with an entry containing ARG_TABLE_ENDMARKER.
 */
void native_add_command_line_opts(struct args_struct_t *args)
{
	int count = 0;

	while (args[count].option != NULL) {
		count++;
	}
	count++; /*for the end marker*/

	if (used_args + count >= args_aval) {
		int growby = count;
		/* reallocs are expensive let's do them only in big chunks */
		if (growby < ARGS_ALLOC_CHUNK_SIZE) {
			growby = ARGS_ALLOC_CHUNK_SIZE;
		}

		struct args_struct_t *new_args_struct = realloc(args_struct,
				      (args_aval + growby)*
				      sizeof(struct args_struct_t));
		args_aval += growby;
		/* LCOV_EXCL_START */
		if (new_args_struct == NULL) {
			posix_print_error_and_exit("Could not allocate memory");
		} else {
			args_struct = new_args_struct;
		}
		/* LCOV_EXCL_STOP */
	}

	memcpy(&args_struct[used_args], args,
		count*sizeof(struct args_struct_t));

	used_args += count - 1;
	/*
	 * -1 as the end marker should be overwritten next time something
	 * is added
	 */
}

void native_add_testargs_option(void)
{
	static struct args_struct_t testargs_options[] = {
		/*
		 * Fields:
		 * manual, mandatory, switch,
		 * option_name, var_name ,type,
		 * destination, callback,
		 * description
		 */
		{true, false, false,
		"testargs", "arg", 'l',
		(void *)NULL, NULL,
		"Any argument that follows will be ignored by the top level, "
		"and made available for possible tests"},
		ARG_TABLE_ENDMARKER};

	native_add_command_line_opts(testargs_options);
}

static void print_invalid_opt_error(char *argv)
{
	posix_print_error_and_exit("Incorrect option '%s'. Did you misspell it?"
				   " Is that feature supported in this build?"
				   "\n",
				   argv);

}

/**
 * Handle possible command line arguments.
 *
 * We also store them for later use by possible test applications
 */
void native_handle_cmd_line(int argc, char *argv[])
{
	int i;

	native_add_tracing_options();
	native_add_testargs_option();

	s_argv = argv;
	s_argc = argc;

	cmd_args_set_defaults(args_struct);

	for (i = 1; i < argc; i++) {

		if ((cmd_is_option(argv[i], "testargs", 0))) {
			test_argc = argc - i - 1;
			test_argv = &argv[i+1];
			break;
		}

		if (!cmd_parse_one_arg(argv[i], args_struct)) {
			cmd_print_switches_help(args_struct);
			print_invalid_opt_error(argv[i]);
		}
	}
}

/**
 * The application/test can use this function to inspect all the command line
 * arguments
 */
void native_get_cmd_line_args(int *argc, char ***argv)
{
	*argc = s_argc;
	*argv = s_argv;
}

/**
 * The application/test can use this function to inspect the command line
 * arguments received after --testargs
 */
void native_get_test_cmd_line_args(int *argc, char ***argv)
{
	*argc = test_argc;
	*argv = test_argv;
}
