/*
 * Copyright (c) 2022 Actions Technology Co., Ltd
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#include <soc.h>
#include <board.h>
#include <device.h>
#include <drivers/uart.h>
#include <drivers/uart_dma.h>
#include <logging/log.h>
#include <gps/gps.h>

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

LOG_MODULE_REGISTER(gps_3352, LOG_LEVEL_INF);
#define CONFIG_GPS_DEV_NAME "gps"
#define GPS_UERT_DEV_NAME CONFIG_UART_2_NAME

#ifdef CONFIG_SENSOR_GPS_UART_DMA
#define GPS_DATA_BUFFER_SIZE 100
#else
#define UART_FIFO_MAX 16
#define GPS_DATA_BUFFER_SIZE 100
#endif // CONFIG_SENSOR_GPS_UART_DMA

struct ag3352_data {
	const struct device *uart_gpio_dev;
#ifdef CONFIG_SENSOR_GPS_UART_DMA
	uint8_t temp_buff[GPS_DATA_BUFFER_SIZE];
#else
	uint8_t gps_data[2][GPS_DATA_BUFFER_SIZE];
	uint8_t gpa_data_cur;
	uint8_t gpa_data_prev;
#endif // CONFIG_SENSOR_GPS_UART_DMA
	gps_notify_t notify;
	struct k_work gps_work;
};

static struct ag3352_data ag3352_data;

#ifdef CONFIG_SENSOR_GPS_UART_DMA
void uart_ctrl_rx_dma_start(struct device *dev)
{
	uart_dma_receive(dev, ag3352_data.temp_buff, GPS_DATA_BUFFER_SIZE);

	/* timeout���������Ƿ�����
     */
	// hrtimer_start(&g_tool_data.timer, UART_CTRL_RX_DMA_TIMEOUT_US, 0);
}

static void uart_ctrl_rx_dma_handler(const struct device *dma_dev, void *user_data,
				     uint32_t channel, int status)
{
	LOG_INF("%s", ag3352_data.temp_buff);
	// hrtimer_stop(&g_tool_data.timer);

	// ring_buf_put(&g_tool_data.rx_ringbuf, g_tool_data.temp_buff, UART_CTRL_RX_TEMP_BUF_SIZE);

	uart_ctrl_rx_dma_start((struct device *)ag3352_data.uart_gpio_dev);
}
#else
static void uart_fifo_callback(const struct device *dev, void *user_data)
{
	uint8_t rx_buff[UART_FIFO_MAX];
	int read_size;
	static uint8_t cnt;

	uart_irq_update(dev);
	if (uart_irq_rx_ready(dev)) {
		read_size = uart_fifo_read(dev, rx_buff, UART_FIFO_MAX);
		if (read_size == UART_FIFO_MAX)
			LOG_ERR("uart fifo buffer overflow");

		for (int i = 0; i < read_size; i++) {
			switch (rx_buff[i]) {
			case '$':
				if (ag3352_data.gpa_data_cur)
					ag3352_data.gpa_data_cur = 0;
				else
					ag3352_data.gpa_data_cur = 1;

				memset(ag3352_data.gps_data[ag3352_data.gpa_data_cur], 0,
				       sizeof(ag3352_data.gps_data[ag3352_data.gpa_data_cur]));
				cnt = 0;
				ag3352_data.gps_data[ag3352_data.gpa_data_cur][cnt] = rx_buff[i];
				break;

			case '\n':
				cnt++;
				ag3352_data.gps_data[ag3352_data.gpa_data_cur][cnt] = rx_buff[i];
				ag3352_data.gpa_data_prev = ag3352_data.gpa_data_cur;
				k_work_submit(&ag3352_data.gps_work);
				break;

			default:
				cnt++;
				ag3352_data.gps_data[ag3352_data.gpa_data_cur][cnt] = rx_buff[i];

				/* protection data cannot exceed boundary */
				if (cnt >= GPS_DATA_BUFFER_SIZE)
					cnt--;
				break;
			}
		}
	}
}
#endif // CONFIG_SENSOR_GPS_UART_DMA
static void ag3352_enable(const struct device *dev)
{
	struct ag3352_data *data = (struct ag3352_data *)dev->data;
	sys_write32(0x3, HGLFCLK_CTL);
	sys_write32((sys_read32(CMU_DEVCLKEN1) | (1 << 31)), CMU_DEVCLKEN1);
	sys_write32(0x101e, 0x400680F4); //GPIO61 set 0x1E: LOSCOUT

	gps_reset_pin_ctl();
	gps_wake_up_pin_ctl(true);

#ifdef CONFIG_SENSOR_GPS_UART_DMA
	uart_ctrl_rx_dma_start((struct device *)data->uart_gpio_dev);
#else
	uart_irq_rx_enable(data->uart_gpio_dev);
#endif // CONFIG_SENSOR_GPS_UART_DMA
}

static void ag3352_disable(const struct device *dev)
{
	struct ag3352_data *data = (struct ag3352_data *)dev->data;
	// gps_wake_up_pin_ctl(false);
#ifdef CONFIG_SENSOR_GPS_UART_DMA
	uart_dma_receive_stop((struct device *)data->uart_gpio_dev);
#else
	uart_irq_rx_disable(data->uart_gpio_dev);
#endif // CONFIG_SENSOR_GPS_UART_DMA
}

static void ag3352_register_notify(const struct device *dev, gps_notify_t notify)
{
	LOG_INF("ag3352_init");
	struct ag3352_data *data = (struct ag3352_data *)dev->data;

	data->notify = notify;
}

static void ag3352_unregister_notify(const struct device *dev, gps_notify_t notify)
{
	struct ag3352_data *data = (struct ag3352_data *)dev->data;

	data->notify = NULL;
}

static const struct gps_dev_driver_api ag3352_api = {
	.enable = ag3352_enable,
	.disable = ag3352_disable,
	.inquiry = NULL,
	.register_notify = ag3352_register_notify,
	.unregister_notify = ag3352_unregister_notify,
};

static void _gps_work_handler(struct k_work *work)
{
	struct gps_value val = { 0 };

	//LOG_INF("gps nmea: %s\r\n", ag3352_data.gps_data[ag3352_data.gpa_data_prev]);
	if (ag3352_data.notify) {
#ifdef CONFIG_SENSOR_GPS_UART_DMA
#else
		val.gps_nmea_data = ag3352_data.gps_data[ag3352_data.gpa_data_prev];
#endif // CONFIG_SENSOR_GPS_UART_DMA
		ag3352_data.notify(NULL, &val);
	}
}

static int ag3352_init(const struct device *dev)
{
	struct ag3352_data *data = (struct ag3352_data *)dev->data;
	LOG_INF("ag3352_init");
	gps_power_pin_ctl(true);
	gps_power095_pin_ctl(true);
	gps_reset_pin_ctl();
	data->uart_gpio_dev = device_get_binding(GPS_UERT_DEV_NAME);
	if (data->uart_gpio_dev == NULL) {
		LOG_ERR("Couldn't find gps uart");
		return -ENODEV;
	}
	k_work_init(&data->gps_work, _gps_work_handler);
#ifdef CONFIG_SENSOR_GPS_UART_DMA
	uart_dma_receive_init((struct device *)data->uart_gpio_dev, uart_ctrl_rx_dma_handler, NULL);
	uart_rx_dma_switch((struct device *)data->uart_gpio_dev, TRUE, NULL, NULL);
#else
	uart_irq_callback_set(data->uart_gpio_dev, uart_fifo_callback);
	data->gpa_data_cur = 0;
#endif // CONFIG_SENSOR_GPS_UART_DMA
	return 0;
}

#if IS_ENABLED(CONFIG_GPS)
DEVICE_DEFINE(ag3352, CONFIG_GPS_DEV_NAME, &ag3352_init, NULL, &ag3352_data, NULL, POST_KERNEL, 60,
	      &ag3352_api);
#endif