/*
 * ________________________________________________________________________________________________________
 * Copyright (c) 2017 InvenSense Inc. All rights reserved.
 *
 * This software, related documentation and any modifications thereto (collectively “Software”) is subject
 * to InvenSense and its licensors' intellectual property rights under U.S. and international copyright
 * and other intellectual property rights laws.
 *
 * InvenSense and its licensors retain all intellectual property and proprietary rights in and to the Software
 * and any use, reproduction, disclosure or distribution of the Software without an express license agreement
 * from InvenSense is strictly prohibited.
 *
 * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, THE SOFTWARE IS
 * PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 * EXCEPT AS OTHERWISE PROVIDED IN A LICENSE AGREEMENT BETWEEN THE PARTIES, IN NO EVENT SHALL
 * INVENSENSE BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY
 * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THE SOFTWARE.
 * ________________________________________________________________________________________________________
 */
#include <asf.h>
#include <stdio.h>

#include "conf_uart_serial.h"
#include "conf_board.h"
#include "system.h"
#include "sensor.h"

#include "ASF/sam/drivers/pio/pio.h"
#include "ASF/sam/drivers/pio/pio_handler.h"
#include "ASF/sam/drivers/twi/twi.h"
#include "ASF/sam/drivers/tc/tc.h"

/* InvenSense Drivers and Utils */
#include "Invn/EmbUtils/InvScheduler.h"
#include "Invn/EmbUtils/RingByteBuffer.h"
#include "Invn/EmbUtils/Message.h"
#include "Invn/EmbUtils/RingBuffer.h"

#include "Invn/Devices/Drivers/Icp101xx/Icp101xx.h"

/* extern and global declarations */
uint8_t			I2C_Address = ICP101XX_I2C_ADDR; /* Default ICP101xx (Slave) Address */
twi_options_t	opt;
twi_packet_t	packet_tx, packet_rx;

RingByteBuffer uart_rx_rb;
static uint8_t uart_rx_rb_buffer[2*BUFFER_SIZE];

timer_callback_t timer_callback = NULL;
uint32_t timeout_count = 0;
uint32_t pressure_timer_count = DEFAULT_PRESSURE_ODR_US/MICROSECONDS_PER_MILLISECOND;
static RINGBUFFER(timestamp_buffer, BUFFER_SIZE, uint32_t);

volatile uint32_t	ul_ticks = 0;
volatile uint32_t	timer_ticks = 0;
extern uint32_t		enabled_sensor_mask;

InvScheduler 		scheduler;
InvSchedulerTask	blinkerLedTask;
InvSchedulerTask 	commandHandlerTask;

#if (USART_TX_DMA_XFER_EN == 1)
 uint8_t			g_uc_pdc_buffer[BUFFER_SIZE];	/* Pdc transfer buffer */
 pdc_packet_t		g_pdc_usart_packet;				/* PDC data packet for transfer */
 Pdc				*g_p_usart_pdc;					/* Pointer to USART PDC register base */
#endif

void configure_console(void) {

	const usart_serial_options_t uart_serial_options = {
		.baudrate = CONF_UART_BAUDRATE,
		.charlength = CONF_UART_CHAR_LENGTH,
		.paritytype = CONF_UART_PARITY,
		.stopbits = CONF_UART_STOP_BITS,
	};

	/* Configure console UART for command handling */
	sysclk_enable_peripheral_clock(CONSOLE_UART_ID);
	usart_serial_init(CONF_UART, (usart_serial_options_t *)&uart_serial_options);

	RingByteBuffer_init(&uart_rx_rb, uart_rx_rb_buffer, sizeof(uart_rx_rb_buffer));
	
	/* Enable UART IRQ */
	usart_enable_interrupt(CONSOLE_UART, US_IER_RXRDY);

	/* Set UART interrupt priority */
	NVIC_SetPriority(CONSOLE_UART_IRQn, 0);

	/* Enable UART interrupt */
	NVIC_EnableIRQ(CONSOLE_UART_IRQn);

	const usart_serial_options_t uart_serial_options_dbg = {
		.baudrate = DEBUG_UART_BAUDRATE,
		.charlength = CONF_UART_CHAR_LENGTH,
		.paritytype = CONF_UART_PARITY,
		.stopbits = CONF_UART_STOP_BITS,
	};

	/* Configure console UART for debugging */
	sysclk_enable_peripheral_clock(CONF_UART_ID_DBG);
	stdio_serial_init(CONF_UART_DBG, &uart_serial_options_dbg);

#if (USART_TX_DMA_XFER_EN == 1)
	/* Get pointer to USART PDC register base */
	g_p_usart_pdc = usart_get_pdc_base(DEBUG_UART);

	/* Initialize PDC data packet for transfer */
	g_pdc_usart_packet.ul_addr = (uint32_t) g_uc_pdc_buffer;
	g_pdc_usart_packet.ul_size = BUFFER_SIZE;

	/* Enable PDC transfers */
	pdc_enable_transfer(g_p_usart_pdc, PERIPH_PTCR_RXTEN | PERIPH_PTCR_TXTEN);
#endif
}

/**
 * \brief Interrupt handler for USART interrupt.
 */
void console_uart_irq_handler(void) {
	
	uint32_t ul_status;

	/* Read USART Status. */
	ul_status = usart_get_status(CONSOLE_UART);
	
	if((ul_status &  US_CSR_RXRDY )) {		
		uint8_t rxbyte;
		usart_serial_getchar(CONSOLE_UART, &rxbyte);
		
		if(!RingByteBuffer_isFull(&uart_rx_rb))
			RingByteBuffer_pushByte(&uart_rx_rb, rxbyte);
	}
}

void i2c_master_initialize(void) {
	
	/* Insert application code here, after the board has been initialized. */
	/* Enable the peripheral and set TWI mode. */
	flexcom_enable(BOARD_FLEXCOM_TWI);
	flexcom_set_opmode(BOARD_FLEXCOM_TWI, FLEXCOM_TWI);
	
	/* Configure the options of TWI driver */
	opt.master_clk = sysclk_get_peripheral_hz();
	opt.speed = TWI_CLK;

	twi_master_init(BOARD_BASE_TWI, &opt);
}

uint32_t i2c_master_read_register(uint8_t chip_address, uint8_t reg, uint32_t rlen, void * rbuffer) {
	
	twi_packet_t packet_read;
	
	(void)reg;
	
	packet_read.chip = chip_address;
	packet_read.addr[0] = 0;
	packet_read.addr_length = 0;
	packet_read.buffer = rbuffer;
	packet_read.length = rlen;

	if(twi_master_read((Twi*)BOARD_BASE_TWI, &packet_read) == TWI_SUCCESS) {
		return TWI_SUCCESS;
	}
	
	return TWI_BUSY;
}

uint32_t i2c_master_write_register(uint8_t chip_address, uint8_t reg, uint32_t wlen, void * wbuffer) {
	
	twi_packet_t packet_write;
	
	packet_write.chip = chip_address;
	packet_write.addr[0] = ((uint8_t *)wbuffer)[0];
	packet_write.addr_length = 1;
	packet_write.buffer = ((uint8_t *)wbuffer) + 1;
	packet_write.length = wlen;
	
	return twi_master_write((Twi*)BOARD_BASE_TWI, &packet_write);
}

void set_pressure_timer(uint32_t new_timer_val)
{
	pressure_timer_count = new_timer_val;
}

void SysTick_Handler(void) {
	
	static uint32_t timer_count = 0;
	
	if (++timer_count >= pressure_timer_count) {
		if((timer_callback != NULL) && enabled_sensor_mask) {
			timer_callback(&ul_ticks);
		}
		timer_count = 0;	
	}
	
	ul_ticks++;

	/* Update Scheduler Time */
	InvScheduler_updateTime(&scheduler);
}

int32_t timer_clear_irq_timestamp() {
	
	/* Clear the buffer used to store the timestamp cached on interrupt */
	RINGBUFFER_CLEAR(&timestamp_buffer);	
	
	timer_ticks = 0;
	
	return 0;
}

void timer_create_ringbuffer_timestamp(uint32_t value) {
	
	if(!RINGBUFFER_FULL(&timestamp_buffer))
		RINGBUFFER_PUSH(&timestamp_buffer, &value);
}

uint64_t timer_get_irq_timestamp_us(void) {
	
	uint32_t timestamp = 0;
	
	if(!RINGBUFFER_EMPTY(&timestamp_buffer))		
		RINGBUFFER_POP(&timestamp_buffer, &timestamp);
		
	return (timestamp * MICROSECONDS_PER_MILLISECOND);
}

void timer_register_callback(timer_callback_t cb_ptr) {

	timer_callback = cb_ptr;
}
