#ifndef I2C_H_
#define I2C_H_

#if defined(__FREERTOS__)
#include "asf.h"
#endif

#define TWI_OFFSET_FROM_FLEXCOM		(0x600UL)

#define I2C_INTERRUPT_PRIO		2

#define DEFAULT_I2C_WAIT_TIME	50	// Clock to the RTT is about 10kHz. This is roughly 5ms

typedef void(*I2C_Callback)(void);

typedef enum {
	I2C_SIMPLE_WRITE,
	I2C_SIMPLE_READ,
	I2C_INT_ADDR_WRITE,
	I2C_INT_ADDR_READ
} I2C_Transaction_Type;

typedef enum {
	I2C_NOT_INIT,
	I2C_IDLE,
	I2C_SUCCESS,
	I2C_NO_REPLY,
	I2C_ARB_LOST,
	I2C_OVERRUN,
	I2C_UNDERRUN,
	I2C_TIMED_OUT
} I2C_Status_Type;

typedef enum {
	I2C_Shifted, // I2C_Shifted: address is lower 7 bits (valid 0x00 to 0x7F)
	I2C_Unshifted // I2C_Unshifted: address includes R/W bit (valid 0x00 to 0xFE, no odd numbered addressed)
} I2C_Address_Shift_Type;

// Notes on "Unshifted" vs "Shifted"
// I2C addresses are 7 bits, but when addressing a part the bus controller clocks out 8 bits. The final 8th bit
// selects between read (1) or write (0).
// We can pretend that the address has 7 bits, but they occupy the most significant bits of a byte, like:
//
// (Unshifted type) Addr.7_Addr.6_Addr.5_Addr.4_Addr.3_Addr.2_Addr.1_Read/Write
// This can count from b0000_000n (where n is ignored for the address) to b1111_111n, or 0 to 254 (0xFE) (only even numbers)
// 
// Or they can be considered the lower 7 bits of a byte:
//
// (Shifted type) Addr.6_Addr.5_Addr.4_Addr.3_Addr.2_Addr.1_Addr.0_Read/Write
// This can count from b000_0000 to b111_1111, or 0 to 127 (0x7F)


typedef struct {
	uint8_t dev_addr; // address of I2C device on bus
	I2C_Address_Shift_Type shift; // 7-bit or 8-bit address definition, see above		
	I2C_Transaction_Type ttype;
	uint8_t internal_addr; // Only used in I2C_INT_ADDR_WRITE/READ transaction types. Sets the register address in the target device
	uint8_t len;
	uint8_t* data; // for write commands, preload with data. for read commands, it will be loaded by the i2c transaction
	I2C_Callback cb; // set to non-null for getting a callback after transaction is complete or fails
} I2C_Transaction_Request;

typedef struct {
	Flexcom* flexcom;
	uint8_t SCL_Pin;
	uint8_t SDA_Pin;
	volatile I2C_Status_Type status;
	volatile uint8_t sent_bytes;
	volatile uint8_t busy;
	I2C_Transaction_Request* current_txn;
#if defined(__FREERTOS__)
	SemaphoreHandle_t i2c_mutex;
#endif
} I2C_Handle;

void init_i2c(I2C_Handle* hi2c, uint32_t desired_clock_speed);
void disable_i2c(I2C_Handle* hi2c);

uint8_t i2c_transact(I2C_Handle* hi2c, I2C_Transaction_Request* hi2c_txn);
uint8_t i2c_transact_blocking(I2C_Handle* hi2c, I2C_Transaction_Request* hi2c_txn, uint32_t timeout);
uint8_t i2c_wait_to_complete(I2C_Handle* hi2c, uint32_t timeout);
uint8_t i2c_busy(I2C_Handle* hi2c);
I2C_Status_Type i2c_status(I2C_Handle* hi2c);
void i2c_irq_handler(I2C_Handle* hi2c); // Call this from your FLEXCOM#_Handler
void i2c_lockup_clear_sequence(I2C_Handle* hi2c);

#if defined(__FREERTOS__)
uint8_t i2c_lock(I2C_Handle* hi2c);
uint8_t i2c_unlock(I2C_Handle* hi2c);
#endif

#endif /* I2C_H_ */
