/**
 * \file
 *
 * \brief Journal service based on dataflash
 *
 * This is the interface of a journal service which can be used to enter
 * journal entries in a dataflash and reading back the latest entry.
 *
 * - 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 JOURNAL_H_INCLUDED
#define JOURNAL_H_INCLUDED

#include <dataflash/dataflash.h>

enum journal_status {
	JOURNAL_IDLE,
	JOURNAL_BUSY,
	JOURNAL_END_NOT_LOCALIZED,
	JOURNAL_ENTRY_OPEN,
	JOURNAL_ERROR,
};

/**
 * Journal entry description struct; this is the information that is stored
 * in the journal and can be recovered after a power loss
 */
struct journal_entry {
	uint16_t	magic0;
	uint16_t	length;
	uint32_t	card_id;
	uint32_t	block;
	uint16_t	committed_card0_magic;
	uint16_t	committed_card1_magic;
};

struct journal;
typedef void (*journal_callback_t)(
		struct journal *,
		struct journal_entry *,
		void *);

/** \internal Journal description struct */
struct journal {
	/** Dataflash used for storing the journal entries */
	struct dataflash_device *storage;
	/** First dataflash byte available to the journal */
	unsigned int start;
	/** Length of dataflash area available to the journal */
	unsigned int length;
	/**
	 * Last location used. This variable will be reconstructed by
	 * journal_init() when recovering from power loss
	 */
	unsigned int last_location;
	/** Next location to write to */
	unsigned int current_location;
	/** Flags controlling journal operation */
	unsigned long flags;
	/** Current status of the journal */
	unsigned int status;
	/** Callback function called when the current operation is done */
	journal_callback_t callback;
	/** Data pointer passed to the callback */
	void *context;

	/** Buffer used for dataflash access */
	struct buffer entry_buffer;
	/** Dataflash request used for dataflash access */
	struct dataflash_request request;
	struct dataflash_request erase_request;
	/** Entry currently in the process of being written */
	struct journal_entry *current_entry;
	/** Temporary storage used when searching for journal entries */
	uint16_t magic_number;
	/** State variable used when searching for journal entries */
	unsigned int search_mode;

	struct workqueue_item workqueue_item;
};

#define JOURNAL_EMPTY			(1 << 0)
#define JOURNAL_CARD0_COMMITTED		(1 << 1)
#define JOURNAL_CARD1_COMMITTED		(1 << 2)

void journal_init(
		struct journal *media,
		struct dataflash_device *storage,
		unsigned int start_addr,
		unsigned int length);

void journal_open_entry(
		struct journal *media,
		struct journal_entry *entry,
		journal_callback_t entry_stored,
		void *context);

void journal_commit_card(
		struct journal *media,
		struct journal_entry *entry,
		unsigned int card_nr,
		journal_callback_t card_committed,
		void *context);

void journal_close_entry(struct journal *media);

void journal_get_last_entry(
		struct journal *media,
		struct journal_entry *entry,
		journal_callback_t entry_retrieved,
		void *context);

void journal_clean_last_entry(
		struct journal *media,
		journal_callback_t entry_cleaned,
		void *context);

void journal_prepare_entry(
		struct journal_entry *entry,
		uint32_t card_id,
		uint32_t block,
		uint16_t length);

bool journal_entry_card_is_intact(
		struct journal_entry *entry,
		unsigned int card_nr);

bool journal_entry_is_consistent(struct journal_entry *entry);

/**
 * \brief Check if journal is empty
 *
 * This function will return true if the journal is empty, false if there is
 * at least one entry in the journal. journal_get_last_entry() must be run before
 * this funtion.
 *
 * \param journal    Journal description struct
 *
 * \return true      Journal is empty
 * \return false     Journal is not empty
 */
static inline bool journal_is_empty(struct journal *journal)
{
	assert(journal);
	return !!(journal->flags & JOURNAL_EMPTY);
}

#endif /* JOURNAL_H_INCLUDED */

