#ifndef I2C_LIB
#define I2C_LIB
#define ADDRESS0 0x50
#define PAGESIZE 256

//uint8_t address;
volatile uint8_t busNotReady;
volatile uint8_t acked = 0;
volatile uint8_t nacked = 0;
//uint8_t number = 123;

volatile uint8_t errorCount = 0;
volatile uint8_t receivedByte = 0;
volatile uint8_t I2C_received = 0;
volatile uint8_t I2C_bytesToSend = 0;
volatile uint8_t I2C_bytesToReceive = 0;
volatile uint8_t I2C_bytesReceived = 0;
volatile uint8_t I2C_bytesSent = 0;
volatile uint8_t I2C_bytesCount = 0;
volatile uint8_t I2C_ackNext = 0;
volatile uint8_t I2C_stopNext = 0;

void I2C_sync(void){
	while(SERCOM3->I2CM.SYNCBUSY.bit.SYSOP);
}

uint8_t I2C_MasterStart(uint8_t address, uint8_t mode){	// 1 - read			0 - write
	if(SERCOM3->I2CM.STATUS.bit.BUSSTATE == 0) SERCOM3->I2CM.STATUS.bit.BUSSTATE = 1;
	I2C_sync();
	
	I2C_stopNext = 0;
	nacked = 0;
	acked = 0;
	busNotReady = 1;
	
	do {
		SERCOM3->I2CM.ADDR.bit.ADDR = ((address << 1) | mode);
		I2C_sync();
		while(busNotReady);
		busNotReady = 1;
	} while((nacked < 150) && (!acked));

	return acked;
}


void I2C_Restart(uint8_t address){
	busNotReady = 1;
	SERCOM3->I2CM.ADDR.bit.ADDR = ((address << 1) | 1);
	I2C_sync();
}

uint8_t I2C_MasterWriteByte(uint8_t data, uint8_t stop){
	I2C_stopNext = stop;
	
	nacked = 0;
	acked = 0;
	
	do {
		SERCOM3->I2CM.DATA.reg = (data);
		I2C_sync();
		while(busNotReady);
		busNotReady = 1;
	} while((nacked < 150) && (!acked));
	
	I2C_stopNext = 0;
	return acked;
}

uint8_t I2C_MasterWriteSingleByte(uint8_t address, uint32_t registerAddress32, uint8_t data){
	uint16_t registerAddress = (uint16_t)registerAddress32;
	address |= ((uint8_t)(registerAddress32 >> 16) & 0x03);
	uint8_t I2C_acked = I2C_MasterStart(address, 0);
	if(!I2C_acked) return 1;
	I2C_MasterWriteByte(registerAddress >> 8, 0);
	I2C_MasterWriteByte((uint8_t)registerAddress, 0);
	I2C_MasterWriteByte(data, 1);
	return 0;
}

uint8_t I2C_MasterWriteBytes(uint8_t address, uint32_t registerAddress32, uint8_t length, uint8_t* data){
	uint16_t registerAddress = (uint16_t)registerAddress32;
	address |= ((uint8_t)(registerAddress32 >> 16) & 0x03);
	
	uint8_t I2C_acked = I2C_MasterStart(address, 0);
	if(!I2C_acked) return 1;
	I2C_MasterWriteByte(registerAddress >> 8, 0);
	I2C_MasterWriteByte((uint8_t)registerAddress, 0);
	for(uint8_t i = 0; i < length-1; i++) I2C_MasterWriteByte(data[i], 0);
	I2C_MasterWriteByte(data[length-1], 1);
	return 0;
}

uint8_t I2C_MasterWriteSingle(uint8_t address, uint32_t registerAddress32, uint8_t size, uint32_t data){
	uint16_t registerAddress = (uint16_t)registerAddress32;
	address |= ((uint8_t)(registerAddress32 >> 16) & 0x03);
	
	uint8_t I2C_acked = I2C_MasterStart(address, 0);
	if(!I2C_acked) return 1;
	I2C_acked = I2C_MasterWriteByte((registerAddress >> 8), 0);
	I2C_acked = I2C_MasterWriteByte((uint8_t)(registerAddress), 0);
	for(uint8_t i = 0; i < size - 1; i++)I2C_acked = I2C_MasterWriteByte((uint8_t)(data >> 8*i), 0);
	I2C_acked = I2C_MasterWriteByte((uint8_t)(data >> 8*(size - 1)), 1);
	return 0;
}

uint8_t I2C_MasterReadByte(uint8_t ack){
	
	while(!SERCOM3->I2CM.INTFLAG.bit.SB);
	uint8_t buffedData = SERCOM3->I2CM.DATA.reg;
	//SERCOM2->I2CM.INTFLAG.reg |= 1;
	
	if(ack) {
		SERCOM3->I2CM.CTRLB.bit.ACKACT = 0;
		SERCOM3->I2CM.CTRLB.bit.CMD = 0x02;
	} else {
		SERCOM3->I2CM.CTRLB.bit.ACKACT = 1;
		SERCOM3->I2CM.CTRLB.bit.CMD = 0x03;
	}
	I2C_sync();
	return buffedData;
}

uint8_t I2C_MasterReadSingleByte(uint8_t address, uint32_t registerAddress32){
	uint16_t registerAddress = (uint16_t)registerAddress32;
	address |= ((uint8_t)(registerAddress32 >> 16) & 0x03);
	
	uint8_t dataBuffer;
	uint8_t I2C_acked = I2C_MasterStart(address, 0);
	if(!I2C_acked) return 0;
	I2C_acked = I2C_MasterWriteByte((registerAddress >> 8), 0);
	I2C_acked = I2C_MasterWriteByte((uint8_t)(registerAddress), 0);
	I2C_Restart(address);
	return I2C_MasterReadByte(0);
}

uint32_t I2C_MasterReadSingle(uint8_t address, uint32_t registerAddress32, uint8_t size){
	uint16_t registerAddress = (uint16_t)registerAddress32;
	address |= ((uint8_t)(registerAddress32 >> 16) & 0x03);
	
	uint32_t dataBuffer = 0;
	uint8_t I2C_acked = I2C_MasterStart(address, 0);
	if(!I2C_acked) return 0;
	I2C_acked = I2C_MasterWriteByte((registerAddress >> 8), 0);
	I2C_acked = I2C_MasterWriteByte((uint8_t)(registerAddress), 0);
	I2C_Restart(address);
	for(uint8_t i = 0; i < size - 1; i++) {
		dataBuffer |= (I2C_MasterReadByte(1) << 8*i);
	}
	dataBuffer |= (I2C_MasterReadByte(0) << (size - 1)*8);
	return dataBuffer;
}

uint8_t I2C_MasterReadBytes(uint8_t address, uint32_t registerAddress32, uint8_t length, uint8_t* data){
	uint16_t registerAddress = (uint16_t)registerAddress32;
	address |= ((uint8_t)(registerAddress32 >> 16) & 0x03);
	
	uint8_t I2C_acked = I2C_MasterStart(address, 0);
	if(!I2C_acked) return 0;
	I2C_acked = I2C_MasterWriteByte((registerAddress >> 8), 0);
	I2C_acked = I2C_MasterWriteByte((uint8_t)(registerAddress), 0);
	I2C_Restart(address);
	for(uint8_t i = 0; i < length-1; i++) data[i] = I2C_MasterReadByte(1);
	data[length-1] = I2C_MasterReadByte(0);
	return 1;
}

uint8_t I2C_MasterWritePage(uint8_t address, uint32_t pageNumber, uint8_t* data){
	uint16_t registerAddress = (uint16_t)(pageNumber*PAGESIZE);
	address |= ((uint8_t)((pageNumber*PAGESIZE) >> 16) & 0x03);
	
	uint8_t I2C_acked = I2C_MasterStart(address, 0);
	if(!I2C_acked) return 1;
	I2C_MasterWriteByte(registerAddress >> 8, 0);
	I2C_MasterWriteByte((uint8_t)registerAddress, 0);
	for(uint8_t i = 0; i < PAGESIZE - 1; i++) I2C_MasterWriteByte(data[i], 0);
	I2C_MasterWriteByte(data[PAGESIZE - 1], 1);
	return 0;
}

uint8_t I2C_MasterReadPage(uint8_t address, uint16_t pageNumber, uint8_t* data){
	uint16_t registerAddress = (uint16_t)(pageNumber*PAGESIZE);
	address |= ((uint8_t)((pageNumber*PAGESIZE) >> 16) & 0x03);
	
	uint8_t I2C_acked = I2C_MasterStart(address, 0);
	if(!I2C_acked) return 0;
	I2C_acked = I2C_MasterWriteByte((registerAddress >> 8), 0);
	I2C_acked = I2C_MasterWriteByte((uint8_t)(registerAddress), 0);
	I2C_Restart(address);
	for(uint8_t i = 0; i < PAGESIZE - 1; i++) data[i] = I2C_MasterReadByte(1);
	data[PAGESIZE - 1] = I2C_MasterReadByte(0);
	return 1;
}

void IO_initI2CMaster(){
	PORT->Group[0].DIRSET.reg = PORT_PA22|PORT_PA23;
	PORT->Group[0].PMUX[11].bit.PMUXE = 0x02; //A - 0x00 B 0x01 … D 0x03
	PORT->Group[0].PMUX[11].bit.PMUXO = 0x02;	//C - 0x02 SERCOM3
	PORT->Group[0].PINCFG[22].reg = 0x03;	// - DRVSTR - - - PULLEN INEN PMUXEN
	PORT->Group[0].PINCFG[23].reg = 0x03;	// - DRVSTR - - - PULLEN INEN PMUXEN
}
void I2C_MasterInit(){
	MCLK->APBCMASK.reg |= MCLK_APBCMASK_SERCOM3;
	//Enable GCLK for TCC0 and link it to GCLK0 see "PCHCTRLm Mapping" from product datasheet
	GCLK->PCHCTRL[21].reg = (GCLK_PCHCTRL_CHEN|GCLK_PCHCTRL_GEN(0));		//16M
	// - Disable TC0
	
	while(SERCOM3->I2CM.SYNCBUSY.bit.ENABLE);
	SERCOM3->I2CM.CTRLA.bit.ENABLE = 0 ;
	while(SERCOM3->I2CM.SYNCBUSY.bit.ENABLE);
	
	SERCOM3->I2CM.CTRLA.bit.MODE = 0x5;	//master 0x5 slave 0x4 
	//SERCOM2->I2CM.CTRLA.bit.INACTOUT = 0x3; //0x0 (50-60u) (100-110u) (200-210u)
	SERCOM3->I2CM.CTRLA.bit.LOWTOUTEN = 1;
	SERCOM3->I2CM.CTRLA.bit.SCLSM = 0;
	SERCOM3->I2CM.BAUD.bit.BAUDLOW = 0;
	SERCOM3->I2CM.BAUD.bit.BAUD = 30;
	
	SERCOM3->I2CM.INTENSET.reg = SERCOM_I2CM_INTENSET_MB|SERCOM_I2CM_INTENSET_ERROR;
	NVIC_EnableIRQ(SERCOM3_IRQn);
	
	while(SERCOM3->I2CM.SYNCBUSY.bit.ENABLE);
	SERCOM3->I2CM.CTRLA.bit.ENABLE = 1;
	while(SERCOM3->I2CM.SYNCBUSY.bit.ENABLE);
}

void SERCOM3_Handler(void){
	uint8_t sercomFlag = SERCOM3->I2CM.INTFLAG.reg;
	if(sercomFlag & SERCOM_I2CM_INTFLAG_ERROR){
		busNotReady = 0;
		nacked++;
		SERCOM3->I2CM.INTFLAG.reg |= sercomFlag;
	} else {
		if(sercomFlag & SERCOM_I2CM_INTFLAG_MB){
			if(SERCOM3->I2CM.STATUS.bit.RXNACK){
				nacked++;
			} else {
				acked = 1;
				if(I2C_stopNext){
					SERCOM3->I2CM.CTRLB.bit.CMD = 0x03;
					I2C_sync();
				}
			}
		
			busNotReady = 0;
			SERCOM3->I2CM.INTFLAG.reg |= SERCOM_I2CM_INTFLAG_MB;
		}
	}
}

#endif