/*
 * Copyright (c) 2023 Nordic Semiconductor ASA
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

#include <stdlib.h>

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/led.h>
#include <zephyr/drivers/mfd/npm13xx.h>
#include <zephyr/drivers/regulator.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/sys/printk.h>

/* we copy this stuct here to get access to mfd device only */
struct npm13xx_charger_config {
	const struct device *mfd;
	// int32_t term_microvolt;
	// int32_t term_warm_microvolt;
	// const struct linear_range term_volt_ranges[2U];
	// int32_t current_microamp;
	// const struct linear_range current_range;
	// const int32_t full_scale_discharge_factors[2U];
	// int32_t dischg_limit_microamp;
	// uint8_t dischg_limit_idx;
	// int32_t vbus_limit_microamp;
	// int32_t temp_thresholds[4U];
	// int32_t dietemp_thresholds[2U];
	// uint32_t thermistor_ohms;
	// uint16_t thermistor_beta;
	// uint8_t thermistor_idx;
	// uint8_t trickle_sel;
	// uint8_t iterm_sel;
	// bool charging_enable;
	// bool vbatlow_charge_enable;
	// bool disable_recharge;
};

#define SLEEP_TIME_MS  100
#define UPDATE_TIME_MS 2000

#define FAST_FLASH_MS	100
#define SLOW_FLASH_MS	500
#define PRESS_SHORT_MS	1000
#define PRESS_MEDIUM_MS 5000

/* nPM1300 CHARGER.BCHGCHARGESTATUS register bitmasks */
#define NPM13XX_CHG_STATUS_COMPLETE_MASK    BIT(1)
#define NPM13XX_CHG_STATUS_TRICKLE_MASK	    BIT(2)
#define NPM13XX_CHG_STATUS_CC_MASK	        BIT(3)
#define NPM13XX_CHG_STATUS_CV_MASK	        BIT(4)

/* nPM13xx base addresses */
#define CHGR_BASE 0x03U
/* nPM13xx charger register offsets */
#define CHGR_OFFSET_EN_SET      0x04U
#define CHGR_OFFSET_EN_CLR      0x05U
#define CHGR_OFFSET_DIS_SET     0x06U

/* VBUS status masks */
#define STATUS_PRESENT_MASK      0x01U
#define STATUS_CUR_LIMIT_MASK    0x02U
#define STATUS_OVERVLT_PROT_MASK 0x04U
#define STATUS_UNDERVLT_MASK     0x08U
#define STATUS_SUSPENDED_MASK    0x10U
#define STATUS_BUSOUT_MASK       0x20U

#define FAST_FLASH_MS	100
#define SLOW_FLASH_MS	500
#define PRESS_SHORT_MS	1000
#define PRESS_MEDIUM_MS 5000

static volatile int flash_time_ms = SLOW_FLASH_MS;
static volatile bool vbus_connected;

static const struct device *pmic = DEVICE_DT_GET(DT_NODELABEL(npm1300_ek_pmic));
static const struct device *leds = DEVICE_DT_GET(DT_NODELABEL(npm1300_ek_leds));
static const struct device *regulators = DEVICE_DT_GET(DT_NODELABEL(npm1300_ek_regulators));
static const struct device *ldsw = DEVICE_DT_GET(DT_NODELABEL(npm1300_ek_ldo1));
static const struct device *charger = DEVICE_DT_GET(DT_NODELABEL(npm1300_ek_charger));

static void event_callback(const struct device *dev, struct gpio_callback *cb, uint32_t event)
{
	static int press_t, delta;

	if (event & BIT(NPM13XX_EVENT_SHIPHOLD_PRESS)) {
		press_t = k_uptime_get();
	}

	if (event & BIT(NPM13XX_EVENT_SHIPHOLD_RELEASE)) {
		delta = k_uptime_get() - press_t;

		if (delta < PRESS_SHORT_MS) {
			printk("Short press\n");
			if (!regulator_is_enabled(ldsw)) {
				regulator_enable(ldsw);
			}
			flash_time_ms = FAST_FLASH_MS;
		} else if (delta < PRESS_MEDIUM_MS) {
			printk("Medium press\n");
			if (regulator_is_enabled(ldsw)) {
				regulator_disable(ldsw);
			}
			flash_time_ms = SLOW_FLASH_MS;
		} else {
			printk("Long press\n");
			if (vbus_connected) {
				printk("Ship mode entry not possible with USB connected\n");
			} else {
				regulator_parent_ship_mode(regulators);
			}
		}
	}

	if (event & BIT(NPM13XX_EVENT_VBUS_DETECTED)) {
		printk("Vbus connected\n");
		vbus_connected = true;
	}

	if (event & BIT(NPM13XX_EVENT_VBUS_REMOVED)) {
		printk("Vbus removed\n");
		vbus_connected = false;
	}
}

bool configure_events(void)
{
	if (!device_is_ready(pmic)) {
		printk("Pmic device not ready.\n");
		return false;
	}

	if (!device_is_ready(regulators)) {
		printk("Regulator device not ready.\n");
		return false;
	}

	if (!device_is_ready(ldsw)) {
		printk("Load switch device not ready.\n");
		return false;
	}

	if (!device_is_ready(charger)) {
		printk("Charger device not ready.\n");
		return false;
	}

	const struct npm13xx_charger_config *const config = charger->config;

	// Patch for disabling NTC monitoring, this allow the charger working fine
	int ret = mfd_npm13xx_reg_write(config->mfd, CHGR_BASE, CHGR_OFFSET_DIS_SET, 2U);
	if (ret != 0) {
		return ret;
	}

	static struct gpio_callback event_cb;

	gpio_init_callback(&event_cb, event_callback,
			       BIT(NPM13XX_EVENT_SHIPHOLD_PRESS) | 
				   BIT(NPM13XX_EVENT_SHIPHOLD_RELEASE) |
				   BIT(NPM13XX_EVENT_VBUS_DETECTED) |
				   BIT(NPM13XX_EVENT_VBUS_REMOVED)
				);

	mfd_npm13xx_add_callback(pmic, &event_cb);

	/* Initialise vbus detection status */
	// struct sensor_value val;
	// ret = sensor_attr_get(charger, SENSOR_CHAN_CURRENT, SENSOR_ATTR_UPPER_THRESH, &val);

	// if (ret < 0) {
	// 	return false;
	// }

	// vbus_connected = (val.val1 != 0) || (val.val2 != 0);

	return true;
}

int main(void)
{
	if (!device_is_ready(leds)) {
		printk("Error: led device is not ready\n");
		return 0;
	}

	if (!configure_events()) {
		printk("Error: could not configure events\n");
		return 0;
	}

	printk("PMIC device ok\n");

	while (1) {
		led_on(leds, 2U);
		k_msleep(flash_time_ms);
		led_off(leds, 2U);
		k_msleep(flash_time_ms);
	}
}
