#include <sys/ioctl.h>
#include <errno.h>
#include <linux/v4l2-subdev.h>
#include <stdio.h>
#include "util.h"
#include "log.h"

int
xioctl(int fd, int request, void *arg)
{
	int r;
	do {
		r = ioctl(fd, request, arg);
	} while (r == -1 && errno == EINTR);
	return r;
}

// Copied from drivers/media/v4l2-core/v4l2-ctrls-defs.c, trimmed down
const char
*
v4l2_ctrl_get_name(unsigned int id)
{
	switch (id) {
		case V4L2_CID_FLASH_LED_MODE:
			return "LED Mode";
		case V4L2_CID_FLASH_STROBE_SOURCE:
			return "Strobe Source";
		case V4L2_CID_FLASH_STROBE:
			return "Strobe";
		default:
			return "Unknown";
	}
}

// Copied from drivers/media/v4l2-core/v4l2-ctrls-defs.c, trimmed down and slightly modified
const char
*
v4l2_ctrl_get_menu_value(unsigned int id, signed int val)
{
	static const char *const flash_led_mode[] = {
		"Off",
		"Flash",
		"Torch",
		NULL,
	};
	static const char *const flash_strobe_source[] = {
		"Software",
		"External",
		NULL,
	};

	switch (id) {
		case V4L2_CID_FLASH_LED_MODE:
			return flash_led_mode[val];
		case V4L2_CID_FLASH_STROBE_SOURCE:
			return flash_strobe_source[val];
		default:
			return "None/Unknown";
	}
}

// Set a control value, but ignore if it fails
void
set_control(int fd, unsigned int ctrl_id, signed int ctrl_val)
{
	struct v4l2_control ctrl;
	ctrl.id = ctrl_id;
	ctrl.value = ctrl_val;
	int res = xioctl(fd, VIDIOC_S_CTRL, &ctrl);
	if (res == -1) {
		log_debug(
			"Failed to set %s to %s for flash.\n",
			v4l2_ctrl_get_name(ctrl_id),
			v4l2_ctrl_get_menu_value(ctrl_id, ctrl_val)
		);
	}
}

char *
pixfmt_to_str(unsigned int pixfmt)
{
	static char result[5] = "    ";
	result[0] = (char) (pixfmt >> 0 & 0xFF);
	result[1] = (char) (pixfmt >> 8 & 0xFF);
	result[2] = (char) (pixfmt >> 16 & 0xFF);
	result[3] = (char) (pixfmt >> 24 & 0xFF);
	return result;
}