/*
 * Copyright 2018-2024, Digi International Inc.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#include <errno.h>
#include <libgen.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <libdigiapix/can.h>

#define MAX_RECEPTION_BUFFER	512*1024

static can_if_t *can_if;
static struct can_filter *cfilter;
static struct can_filter *canfilter;
static bool running = true;
static bool prn_msg_info = false;
static bool prn_msg_count = false;

/* Default CAN filter if not specified in command line */
static struct can_filter deffilter = {
	.can_id = 0,
	.can_mask = 0
};

/*
 * usage_and_exit() - Show usage information and exit with 'exitval' return
 *		      value
 *
 * @name:	Application name.
 * @exitval:	The exit code.
 */
static void usage_and_exit(char *name, int exitval)
{
	printf(
		"Example application using libdigiapix CAN support\n"
		"\n"
		"Usage: %s -i <can-iface> -b <bitrate> [options]\n\n"
		"-i <can-iface>      Name of the CAN interface\n"
		"-b <bitrate>        Bitrate to use (Hz)\n"
		"-s <sample-point>   Bitrate Sample Point\n"
		"-f <filters>        Comma-separated filter list in the format\n"
		"                    id:mask (id and mask values in hex)\n"
		"-o                  Enable CAN FD support\n"
		"--- CAN FD options ---\n"
		"  -d <dbitrate>      Maximum data bitrate for CAN FD (Hz)\n"
		"  -a <dsample-point> CAN FD data bitate sample point\n"
		"---\n"
		"-p                  Print message info\n"
		"-c                  Print message counter\n"
		"-h                  Help\n"
		"\n"
		"Examples:\n"
		"%s -i can0 -b 500000 -f 023:fff,006:00f\n"
		"%s -i can1 -b 100000\n"
		"%s -i can1 -b 100000 -d 100000 -o -p\n"
		"\n", name, name, name, name);

	exit(exitval);
}

/*
 * cleanup() - Frees all the allocated memory before exiting
 */
static void cleanup(void)
{
	if (canfilter)
		free(canfilter);

	if (can_if) {
		ldx_can_free(can_if);
		running = false;
	}
}

/*
 * sigaction_handler() - Handler to execute after receiving a signal
 *
 * @signum:	Received signal.
 */
static void sigaction_handler(int signum)
{
	/* 'atexit' executes the cleanup function */
	exit(EXIT_FAILURE);
}

/*
 * register_signals() - Registers program signals
 */
static void register_signals(void)
{
	struct sigaction action;

	action.sa_handler = sigaction_handler;
	action.sa_flags = 0;
	sigemptyset(&action.sa_mask);

	sigaction(SIGHUP, &action, NULL);
	sigaction(SIGINT, &action, NULL);
	sigaction(SIGTERM, &action, NULL);
}

static void can_rx_callback(struct canfd_frame *frame, struct timeval *tv)
{
	static uint32_t nframe = 1;
	int i;

	if (prn_msg_count) {
		printf("CAN frame       %u\n", nframe);
	} else {
		printf("CAN frame\n");
	}

	if (prn_msg_info) {
		printf(
			" - Time:        %llu.%06llu\n"
			" - Type:        %s\n"
			" - ID:          %x\n"
			" - Data length: %u\n"
			" - Data:        "
			, (unsigned long long)tv->tv_sec, (unsigned long long)tv->tv_usec, ldx_can_is_extid_frame(frame) ?
			"Extended ID" : "Standard ID", ldx_can_get_id(frame), frame->len);

		for (i=0;i<frame->len;i++) {
			printf("%02x", frame->data[i]);
			if (i < (frame->len -1))
				printf(":");
		}
		printf("\n\n");
	}

	nframe++;
}

static int strchartimes(const char *str, int c)
{
	int count = 0;
	char *character;

	for (character = (char *)str;
		 character && *character != '\0';
		 character++) {
		if (*character == c)
			count++;
	}

	return count;
}

/*
 * parse_filters() -
 *
 * @str:		String that contains all the filters
 * @cfilter:	Struct that contains the filters
 * @nfilters:	Number of valid filters found on str.
 *
 * Return: 0 on success, error code otherwise.
 */
static int parse_filters(char *str, struct can_filter **cfilter, int *nfilters)
{
	int ret;
	int posible_filters;
	int good_filters = 0;
	char *p = str;

	/* First compute the number of filters */
	posible_filters = strchartimes(str, ',');
	posible_filters++;

	/* Allocate the memory for all the possible filters */
	canfilter = malloc(sizeof(struct can_filter) * posible_filters);
	if (!canfilter) {
		printf("Unable to allocate memory for filters\n");
		return -ENOMEM;
	}

	/* Parse the information for each filter */
	while (posible_filters-- && p) {
		ret = sscanf(p, "%x:%x",
					 &canfilter[good_filters].can_id,
					 &canfilter[good_filters].can_mask);
		if (ret == 2)
			good_filters++;

		p = strchr(p, ',');
		if (p)
			p++;
	}

	*nfilters = good_filters;
	*cfilter = canfilter;

	return EXIT_SUCCESS;
}

int main(int argc, char **argv)
{
	char *name = basename(argv[0]);
	char *iface = NULL;
	can_if_cfg_t ifcfg;
	int nfilters = 0;
	int opt;
	int ret;
	float sp = 0.0;

	if (argc <= 3) {
		usage_and_exit(name, EXIT_FAILURE);
	}

	ldx_can_set_defconfig(&ifcfg);

	while ((opt = getopt(argc, argv, "i:b:f:d:s:a:opc")) > 0) {
		switch (opt) {
		case 'i':
			iface = optarg;
			break;

		case 'b':
			ifcfg.bitrate = strtoul(optarg, NULL, 10);
			break;

		case 'd':
			ifcfg.dbitrate = strtoul(optarg, NULL, 10);
			break;

		case 's':
			sp = strtof(optarg, NULL);
			ifcfg.bit_timing.sample_point = (__u32)(sp * 1000);
			break;

		case 'a':
			sp = strtof(optarg, NULL);
			ifcfg.dbit_timing.sample_point = (__u32)(sp * 1000);
			break;

		case 'f':
			ret = parse_filters(optarg, &cfilter, &nfilters);
			if (ret) {
				printf("Unable to parse filter information\n\n");
				usage_and_exit(name, EXIT_FAILURE);
			}
			break;

		case 'o':
			ifcfg.canfd_enabled = true;
			break;

		case 'p':
			prn_msg_info = true;
			break;

		case 'c':
			prn_msg_count = true;
			break;

		case 'h':
			usage_and_exit(name, EXIT_SUCCESS);
			break;

		default:
			usage_and_exit(name, EXIT_FAILURE);
		}
	}

	if (!iface) {
		fprintf(stderr, "Error: CAN interface not specified\n");
		return EXIT_FAILURE;
	}
	printf("Requesting CAN interface %s... ", iface);

	can_if = ldx_can_request_by_name(iface);
	if (!can_if) {
		printf("ERROR\n");
		return EXIT_FAILURE;
	}
	printf("OK\n");

	/* Register signals and exit cleanup function */
	atexit(cleanup);
	register_signals();

	/* Increase the buffer reception size */
	ifcfg.rx_buf_len = MAX_RECEPTION_BUFFER;

	printf("Initializing CAN interface... ");
	ret = ldx_can_init(can_if, &ifcfg);
	if (ret) {
		printf("ERROR\n");
		goto error;
	}
	printf("OK\n");

	/*
	 * Configure a callback to process the defined filters, otherwise,
	 * use the default filter.
	 */
	if (nfilters == 0) {
		nfilters = 1;
		cfilter = &deffilter;
	}
	ret = ldx_can_register_rx_handler(can_if, can_rx_callback,
									  cfilter, nfilters);

	if (ret < 0) {
		printf("Failed to register rx msg handler\n");
		goto error;
	}

	while (running) {
		sleep(5);
		printf("Waiting for CAN frames...\n");
	}

error:

	printf("\n\nCan receive frame application has finished\n");

	return ret;
}
