/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * LR11xx Zephyr HAL — SPI and GPIO low-level operations.
 *
 * The LR11xx SPI protocol:
 *   WRITE: Assert NSS → send 2-byte opcode + param bytes → deassert NSS
 *   READ:  Assert NSS → send 2-byte opcode → deassert NSS
 *          Wait for BUSY low
 *          Assert NSS → read STAT1 + STAT2 + N response bytes → deassert NSS
 *
 *  All transactions must wait for BUSY=low before asserting NSS.
 */

#include "lr11xx.h"

#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>

LOG_MODULE_DECLARE(lr11xx, LOG_LEVEL_DBG);

#define BUSY_POLL_INTERVAL_US   100
#define BUSY_TIMEOUT_MS         1000

int lr11xx_hal_reset(const struct device *dev)
{
	const struct lr11xx_config *cfg = dev->config;
	int ret;

	ret = gpio_pin_set_dt(&cfg->reset, 1);
	if (ret < 0) {
		return ret;
	}
	k_sleep(K_USEC(200));
	ret = gpio_pin_set_dt(&cfg->reset, 0);
	if (ret < 0) {
		return ret;
	}
	k_sleep(K_MSEC(10));

	return lr11xx_hal_wait_busy(dev, K_MSEC(BUSY_TIMEOUT_MS));
}

int lr11xx_hal_wait_busy(const struct device *dev, k_timeout_t timeout)
{
	const struct lr11xx_config *cfg = dev->config;
	uint32_t start = k_uptime_get_32();
	uint32_t timeout_ms = k_ticks_to_ms_floor32(timeout.ticks);

	while (gpio_pin_get_dt(&cfg->busy)) {
		if (timeout_ms > 0 &&
		    (k_uptime_get_32() - start) > timeout_ms) {
			return -ETIMEDOUT;
		}
		k_busy_wait(BUSY_POLL_INTERVAL_US);
	}
	return 0;
}

int lr11xx_hal_write_cmd(const struct device *dev,
			 uint16_t opcode,
			 const uint8_t *params, size_t params_len)
{
	const struct lr11xx_config *cfg = dev->config;
	int ret;

	ret = lr11xx_hal_wait_busy(dev, K_MSEC(BUSY_TIMEOUT_MS));
	if (ret < 0) {
		return ret;
	}

	uint8_t opcode_buf[2] = {
		(uint8_t)(opcode >> 8),
		(uint8_t)(opcode & 0xFF),
	};

	const struct spi_buf tx_bufs[] = {
		{ .buf = opcode_buf, .len = sizeof(opcode_buf) },
		{ .buf = (void *)params, .len = params_len },
	};
	const struct spi_buf_set tx = {
		.buffers = tx_bufs,
		.count   = (params_len > 0) ? 2 : 1,
	};

	return spi_write_dt(&cfg->spi, &tx);
}

int lr11xx_hal_read_resp(const struct device *dev,
			 uint8_t *buf, size_t len)
{
	const struct lr11xx_config *cfg = dev->config;
	int ret;

	ret = lr11xx_hal_wait_busy(dev, K_MSEC(BUSY_TIMEOUT_MS));
	if (ret < 0) {
		return ret;
	}

	/* First two bytes are STAT1 and STAT2 — discard for now */
	uint8_t status[2];
	uint8_t dummy_opcode[2] = {0x00, 0x00};

	const struct spi_buf tx_bufs[] = {
		{ .buf = dummy_opcode, .len = sizeof(dummy_opcode) },
	};
	struct spi_buf rx_bufs[] = {
		{ .buf = status, .len = sizeof(status) },
		{ .buf = buf,    .len = len             },
	};
	const struct spi_buf_set tx = { .buffers = tx_bufs, .count = 1 };
	const struct spi_buf_set rx = { .buffers = rx_bufs, .count = (len > 0) ? 2 : 1 };

	return spi_transceive_dt(&cfg->spi, &tx, &rx);
}

void lr11xx_hal_event_handler(const struct device *port,
			      struct gpio_callback *cb, uint32_t pins)
{
	ARG_UNUSED(port);
	ARG_UNUSED(pins);

	struct lr11xx_data *data =
		CONTAINER_OF(cb, struct lr11xx_data, event_cb);

	k_sem_give(&data->event_sem);
}
