/**
 * \file
 *
 * \brief Simple timer able to perform tasks after a given delay
 *
 * This is the interface of a timer designed to perform given tasks
 * after a given delay.
 *
 * - Compiler:           IAR EWAVR32 and GNU GCC for AVR32
 * - Supported devices:  All devices
 * - AppNote:
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support and FAQ: http://support.atmel.no/
 *
 * \page License
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 * Atmel AVR product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

#ifndef TIMER_H_INCLUDED
#define TIMER_H_INCLUDED

#include <types.h>
#include <assert.h>
#include <interrupt.h>
#include <irq_handler.h>

typedef uint32_t counter_t;

/** Higher part of the counter, handled in software */
typedef uint16_t counter_high_t;
/** Half the range of the counter, maximum delay possible */
#define TIMER_MAX_DELAY		(1UL << ((8*sizeof(counter_t))-1))

typedef void (*timer_callback_t)(void *data);

struct timer_task {
	/** The timer instance the task is assigned to */
	struct timer *timer;
	/** Earliest execution time */
	counter_t time_stamp;
	/** Function to call when the alarm goes off */
	timer_callback_t callback;
	/** Pass this data to the callback function */
	void *data;
	/** Pointer to the next task in the queue */
	struct timer_task *next;
};

struct timer {
	/** Register base address */
	void *port;
	/** Counter frequency/resolution */
	uint32_t frequency;
	/** High part of the counter value */
	counter_high_t counter_high;
	/** A timeout has occurred, tasks are pending */
	bool action_pending;
	/** Timer is counting */
	bool is_running;
	/** Pointer to the task with the earliest deadline */
	struct timer_task *first_task;
};


#ifdef __cplusplus
extern "C" {
#endif

static inline uint32_t timer_get_frequency(struct timer *timer)
{
	assert(timer);
	return timer->frequency;
}

static inline struct timer *timer_task_get_timer(struct timer_task *task)
{
	assert(task);
	return task->timer;
}

static inline void *timer_task_get_data(struct timer_task *task)
{
	assert(task);
	return task->data;
}

/**
 * \brief Reset a used task
 *
 * This function will reset a used task, making it ready for re-use.
 * The callback and data will be kept intact.
 *
 * \param  task    	Pointer to the task to be reset
 */
static inline void timer_reset_task(struct timer_task *task)
{
	assert(task);

	/* Clear next pointer to indicate unused item */
	task->next = NULL;
}

/**
 * \brief Initialize a new task
 *
 * Set up callback and data for a task. If the same task should be added
 * multiple times to a timer, different task structs MUST be used, wait for
 * the first task to complete before reusing the struct.
 *
 * \param  task     Pointer to task structure.
 * \param  callback Function to be executed when the timer expires
 * \param  data     Arbitrary data to be passed to \a callback
 */
static inline void timer_init_task(
		struct timer_task *task,
		timer_callback_t callback,
		void *data)
{
	timer_reset_task(task);

	task->callback = callback;
	task->data = data;
}

void timer_init(struct timer *timer, void *port, uint32_t resolution);

extern counter_t timer_convert_us(struct timer *timer, uint32_t us);

void timer_add_task_ticks(
		struct timer *timer,
		struct timer_task *task,
		counter_t delay);

/**
 * \brief Add new task with delay given in microsecond
 *
 * Add a new task to the be executed after a given delay (given in micro
 * seconds). This is a wrapper around timer_add_task_ticks.
 *
 * A task must always be completed before resubmitting it to the timer, but
 * as tasks are considered completed when the callback is called, they can
 * be resubmitted from their own callback.
 *
 * \param  timer    Pointer to timer control structure
 * \param  task     Pointer to task structure.
 * \param  delay_us Delay given in microseconds
 */
static inline void timer_add_task_us(
		struct timer *timer,
		struct timer_task *task,
		uint32_t delay_us)
{
	timer_add_task_ticks(timer, task, timer_convert_us(timer, delay_us));
}


#ifdef __cplusplus
} /* extern "C" */
#endif

#endif // TIMER_H_INCLUDED
