/*
 * Copyright 2023, Alvaro Garcia <maxpowel@gmail.com>
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Emulator for max17048 fuel gauge
 */


#define DT_DRV_COMPAT maxim_max17048


#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(maxim_max17048);

#include <zephyr/device.h>
#include <zephyr/drivers/emul.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/i2c_emul.h>
#include <zephyr/sys/byteorder.h>

#include "max17048.h"

static int crate_value = 0x4000;

void emul_max17048_set_crate_status(int value)
{
	crate_value = value;
}

/** Static configuration for the emulator */
struct max17048_emul_cfg {
	/** I2C address of emulator */
	uint16_t addr;
};

static int emul_max17048_reg_write(const struct emul *target, int reg, int val)
{

	return -EIO;
}

static int emul_max17048_reg_read(const struct emul *target, int reg, int *val)
{

	switch (reg) {
	case REGISTER_VERSION:
	*val = 0x1000;
		break;
	case REGISTER_CRATE:
	*val = crate_value;
	break;
	case REGISTER_SOC:
	*val = 0x3525;
		break;
	case REGISTER_VCELL:
	*val = 0x4387;
		break;
	default:
		LOG_ERR("Unknown register 0x%x read", reg);
		return -EIO;
	}
	LOG_INF("read 0x%x = 0x%x", reg, *val);

	return 0;
}

static int max17048_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs,
				       int num_msgs, int addr)
{
	/* Largely copied from emul_bmi160.c */
	unsigned int val;
	int reg;
	int rc;

	__ASSERT_NO_MSG(msgs && num_msgs);

	i2c_dump_msgs_rw(target->dev, msgs, num_msgs, addr, false);
	switch (num_msgs) {
	case 2:
		if (msgs->flags & I2C_MSG_READ) {
			LOG_ERR("Unexpected read");
			return -EIO;
		}
		if (msgs->len != 1) {
			LOG_ERR("Unexpected msg0 length %d", msgs->len);
			return -EIO;
		}
		reg = msgs->buf[0];

		/* Now process the 'read' part of the message */
		msgs++;
		if (msgs->flags & I2C_MSG_READ) {
			switch (msgs->len - 1) {
			case 1:
				rc = emul_max17048_reg_read(target, reg, &val);
				if (rc) {
					/* Return before writing bad value to message buffer */
					return rc;
				}

				/* SBS uses SMBus, which sends data in little-endian format. */
				sys_put_le16(val, msgs->buf);
				break;
			default:
				LOG_ERR("Unexpected msg1 length %d", msgs->len);
				return -EIO;
			}
		} else {
			/* We write a word (2 bytes by the SBS spec) */
			if (msgs->len != 2) {
				LOG_ERR("Unexpected msg1 length %d", msgs->len);
			}
			uint16_t value = sys_get_le16(msgs->buf);

			rc = emul_max17048_reg_write(target, reg, value);
		}
		break;
	default:
		LOG_ERR("Invalid number of messages: %d", num_msgs);
		return -EIO;
	}

	return rc;
}

static const struct i2c_emul_api max17048_emul_api_i2c = {
	.transfer = max17048_emul_transfer_i2c,
};

/**
 * Set up a new emulator (I2C)
 *
 * @param emul Emulation information
 * @param parent Device to emulate
 * @return 0 indicating success (always)
 */
static int emul_max17048_init(const struct emul *target, const struct device *parent)
{
	ARG_UNUSED(target);
	ARG_UNUSED(parent);

	return 0;
}

/*
 * Main instantiation macro.
 */
#define MAX17048_EMUL(n)                                                                          \
	static const struct max17048_emul_cfg max17048_emul_cfg_##n = {                          \
		.addr = DT_INST_REG_ADDR(n),                                                       \
	};                                                                                         \
	EMUL_DT_INST_DEFINE(n, emul_max17048_init, NULL,                  \
			    &max17048_emul_cfg_##n, &max17048_emul_api_i2c, NULL)

DT_INST_FOREACH_STATUS_OKAY(MAX17048_EMUL)
