/*
 * Copyright 2023 Google LLC
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <stdlib.h>
#include <zephyr/device.h>
#include <zephyr/input/input_analog_axis.h>
#include <zephyr/input/input_analog_axis_settings.h>
#include <zephyr/logging/log.h>
#include <zephyr/settings/settings.h>
#include <zephyr/sys/printk.h>

LOG_MODULE_REGISTER(analog_axis_settings, CONFIG_INPUT_LOG_LEVEL);

#define ANALOG_AXIS_SETTINGS_PATH_MAX 32

#define MAX_AXES CONFIG_INPUT_ANALOG_AXIS_SETTINGS_MAX_AXES

static void analog_axis_calibration_log(const struct device *dev)
{
	struct analog_axis_calibration cal;
	int i;

	for (i = 0; i < analog_axis_num_axes(dev); i++) {
		analog_axis_calibration_get(dev, i, &cal);

		LOG_INF("%s: ch: %d min: %d max: %d deadzone: %d",
			dev->name, i, cal.in_min, cal.in_max, cal.in_deadzone);
	}
}

static int analog_axis_calibration_load(const char *key, size_t len_rd,
					settings_read_cb read_cb, void *cb_arg)
{
	const struct device *dev;
	struct analog_axis_calibration cal[MAX_AXES];
	int axes;
	char dev_name[ANALOG_AXIS_SETTINGS_PATH_MAX];
	const char *next;
	int nlen;
	ssize_t len;

	nlen = settings_name_next(key, &next);
	if (nlen + 1 > sizeof(dev_name)) {
		LOG_ERR("Setting name too long: %d", nlen);
		return -EINVAL;
	}

	memcpy(dev_name, key, nlen);
	dev_name[nlen] = '\0';

	dev = device_get_binding(dev_name);
	if (dev == NULL) {
		LOG_ERR("Cannot restore: device %s not available", dev_name);
		return -ENODEV;
	}

	len = read_cb(cb_arg, cal, sizeof(cal));
	if (len < 0) {
		LOG_ERR("Data restore error: %d", len);
	}

	axes = analog_axis_num_axes(dev);
	if (len != sizeof(struct analog_axis_calibration) * axes) {
		LOG_ERR("Invalid settings data length: %d, expected %d",
			len, sizeof(struct analog_axis_calibration) * axes);
		return -EIO;
	}

	for (int i = 0; i < axes; i++) {
		analog_axis_calibration_set(dev, i, &cal[i]);
	}

	analog_axis_calibration_log(dev);

	return 0;
}

SETTINGS_STATIC_HANDLER_DEFINE(analog_axis, "aa-cal", NULL,
			       analog_axis_calibration_load, NULL, NULL);

int analog_axis_calibration_save(const struct device *dev)
{
	struct analog_axis_calibration cal[MAX_AXES];
	int axes;
	char path[ANALOG_AXIS_SETTINGS_PATH_MAX];
	int ret;

	analog_axis_calibration_log(dev);

	ret = snprintk(path, sizeof(path), "aa-cal/%s", dev->name);
	if (ret < 0) {
		return -EINVAL;
	}

	axes = analog_axis_num_axes(dev);
	for (int i = 0; i < axes; i++) {
		analog_axis_calibration_get(dev, i, &cal[i]);
	}

	ret = settings_save_one(path, &cal[0],
				sizeof(struct analog_axis_calibration) * axes);
	if (ret < 0) {
		LOG_ERR("Settings save errord: %d", ret);
		return ret;
	}

	return 0;
}
