/*
 * Copyright (c) 2019 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <stdio.h>
#include <zephyr.h>
#include <device.h>
#include <init.h>
#include <pm/pm.h>
#include <pm/device.h>
#include "retained.h"
#include <hal/nrf_gpio.h>

#include <drivers/gpio.h>
#include <soc.h>
#include <dk_buttons_and_leds.h>

#define CONSOLE_LABEL DT_LABEL(DT_CHOSEN(zephyr_console))

#define BUSY_WAIT_S 2U
#define SLEEP_S 2U

#define USER_BUTTON             DK_BTN1_MSK

static void button_changed(uint32_t button_state, uint32_t has_changed)
{
	if (has_changed & USER_BUTTON) {
		printk("dk_buttons_init -> button 1 pressed\r\n");
	}
}

static int init_button(void)
{
	
	int err;

	err = dk_buttons_init(button_changed);
	if (err) {
		printk("Cannot init buttons (err: %d)\n", err);
	}

	return err;
	
}

#define BUTTON_NODE_SIZE 4  
static const struct gpio_dt_spec button[4] = 
{ 
	GPIO_DT_SPEC_GET_OR( DT_ALIAS( sw0 ), gpios, {0} ),
	GPIO_DT_SPEC_GET_OR( DT_ALIAS( sw1 ), gpios, {0} ),
	GPIO_DT_SPEC_GET_OR( DT_ALIAS( sw2 ), gpios, {0} ),
	GPIO_DT_SPEC_GET_OR( DT_ALIAS( sw3 ), gpios, {0} ),
};

static struct gpio_callback button_cb_data;

void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
	if( !( pins ^ BIT(button[0].pin) ) )
	{
		printk("button 1 pressed\r\n");
	}

	if( !( pins ^ BIT(button[1].pin) ) )
	{
		
		printk("button 2 pressed\r\n");
	}

	if( !( pins ^ BIT(button[2].pin) ) )
	{
		printk("button 3 pressed\r\n");
	}

	if( !( pins ^ BIT(button[3].pin) ) )
	{

	}
}
void button_init(void)
{
	int ret;
	gpio_init_callback(&button_cb_data, button_pressed, BIT(button[0].pin) | BIT(button[1].pin) | BIT(button[2].pin) | BIT(button[3].pin));
	for( uint8_t i = 0; i < BUTTON_NODE_SIZE; i++ )
	{
		if( !button[i].port )
		{
			return;
		}
		
		ret = device_is_ready( button[i].port );
		if ( !ret ) 
		{
			printk("Error: button device %s is not ready\n",button[i].port->name);
			return;
		}
		
		ret = gpio_pin_configure_dt( &button[i], GPIO_INPUT );
		if ( ret ) 
		{
			printk("Error %d: failed to configure %s pin %d\n",ret, button[i].port->name, button[i].pin);
			return;
		}
		
		ret = gpio_pin_interrupt_configure_dt( &button[i], GPIO_INT_EDGE_TO_ACTIVE );
		if ( ret ) 
		{
			printk("Error %d: failed to configure interrupt on %s pin %d\n",ret, button[i].port->name, button[i].pin);
			return;
		}
		gpio_add_callback( button[i].port, &button_cb_data );

		/* Configure to generate PORT event (wakeup) on button 1 press. */
		nrf_gpio_cfg_input(DT_GPIO_PIN(DT_NODELABEL(button0), gpios),
			   NRF_GPIO_PIN_PULLUP);
		nrf_gpio_cfg_sense_set(DT_GPIO_PIN(DT_NODELABEL(button0), gpios),
			   NRF_GPIO_PIN_SENSE_LOW);
	}
}

/* Prevent deep sleep (system off) from being entered on long timeouts
 * or `K_FOREVER` due to the default residency policy.
 *
 * This has to be done before anything tries to sleep, which means
 * before the threading system starts up between PRE_KERNEL_2 and
 * POST_KERNEL.  Do it at the start of PRE_KERNEL_2.
 */
static int disable_ds_1(const struct device *dev)
{
	ARG_UNUSED(dev);

	pm_constraint_set(PM_STATE_SOFT_OFF);
	return 0;
}

SYS_INIT(disable_ds_1, PRE_KERNEL_2, 0);

void main(void)
{
	int rc;
	const struct device *cons = device_get_binding(CONSOLE_LABEL);

	printk("\n%s system off demo\n", CONFIG_BOARD);

	if (IS_ENABLED(CONFIG_APP_RETENTION)) {
		bool retained_ok = retained_validate();

		/* Increment for this boot attempt and update. */
		retained.boots += 1;
		retained_update();

		printk("Retained data: %s\n", retained_ok ? "valid" : "INVALID");
		printk("Boot count: %u\n", retained.boots);
		printk("Off count: %u\n", retained.off_count);
		printk("Active Ticks: %" PRIu64 "\n", retained.uptime_sum);
	} else {
		printk("Retained data not supported\n");
	}
#if 0
	button_init();
#else
	rc = init_button();
	if (rc) {
		printk("Button init failed (err %d)\n", rc);
		return;
	}
#endif
#if 0	
	/* Configure to generate PORT event (wakeup) on button 1 press. */
	nrf_gpio_cfg_input(DT_GPIO_PIN(DT_NODELABEL(button0), gpios),
			   NRF_GPIO_PIN_PULLUP);
	nrf_gpio_cfg_sense_set(DT_GPIO_PIN(DT_NODELABEL(button0), gpios),
			       NRF_GPIO_PIN_SENSE_LOW);

	printk("Busy-wait %u s\n", BUSY_WAIT_S);
	k_busy_wait(BUSY_WAIT_S * USEC_PER_SEC);

	printk("Busy-wait %u s with UART off\n", BUSY_WAIT_S);
	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND);
	k_busy_wait(BUSY_WAIT_S * USEC_PER_SEC);
	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_RESUME);

	printk("Sleep %u s\n", SLEEP_S);
	k_sleep(K_SECONDS(SLEEP_S));

	printk("Sleep %u s with UART off\n", SLEEP_S);
	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND);
	k_sleep(K_SECONDS(SLEEP_S));
	rc = pm_device_action_run(cons, PM_DEVICE_ACTION_RESUME);
#endif

	printk("Sleep %u s\n", SLEEP_S);
	k_sleep(K_SECONDS(SLEEP_S));

	printk("Entering system off; press BUTTON1 to restart\n");

	if (IS_ENABLED(CONFIG_APP_RETENTION)) {
		/* Update the retained state */
		retained.off_count += 1;
		retained_update();
	}

	/* Above we disabled entry to deep sleep based on duration of
	 * controlled delay.  Here we need to override that, then
	 * force entry to deep sleep on any delay.
	 */
	pm_power_state_force(0u, (struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
	
	/* Now we need to go sleep. This will let the idle thread runs and
	 * the pm subsystem will use the forced state. To confirm that the
	 * forced state is used, lets set the same timeout used previously.
	 */
	k_sleep(K_SECONDS(SLEEP_S));

	printk("ERROR: System off failed\n");
	while (true) {
		/* spin to avoid fall-off behavior */
	}
}
