#include "bsp_driver.h"
#include "i2c.h"
#include "libs/utils.h"

static int _shark_i2c_rw_bytes(uint32_t index, uint8_t address, uint8_t reg, uint8_t *buffer, int length, int write);

#define iic_device(id) ((id == 0)?I2C0:I2C1)
static uint32_t i2c_clk[2];
static uint8_t i2c_busy_count[2];
static volatile uint8_t i2c_lock[2];
/*
 * 当读写i2c slave设备的时候，MCU复位，有概率会导致slave设备永远会锁住总线，导致永远busy,复位MCU无效
 * 这里，需要通过i2c的reset位配合sda，sck拉高，强行让slave释放总线
*/
static void shark_i2c_busy_recovery(uint32_t i2c_periph){
	if (i2c_periph == I2C0) {
		gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7);
		gpio_bit_set(GPIOB, GPIO_PIN_6);
		gpio_bit_set(GPIOB, GPIO_PIN_7);
	} else {
		gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10 | GPIO_PIN_11);
		gpio_bit_set(GPIOB, GPIO_PIN_10);
		gpio_bit_set(GPIOB, GPIO_PIN_11);
	}
	i2c_software_reset_config(i2c_periph, 1);
	delay_us(10);
	i2c_software_reset_config(i2c_periph, 0);
}


void shark_i2c_init(uint32_t i2c_index, uint32_t rate){
	uint32_t device = iic_device(i2c_index);
	i2c_clk[i2c_index] = rate;
	i2c_lock[i2c_index] = 0;
	rcu_periph_clock_enable(RCU_GPIOB);
	if (device == I2C0) {
		rcu_periph_clock_enable(RCU_I2C0);
		shark_i2c_busy_recovery(I2C0);
		i2c_deinit(I2C0);
	 	gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7);
	} else {
		rcu_periph_clock_enable(RCU_I2C1);
		shark_i2c_busy_recovery(I2C1);
		i2c_deinit(I2C1);
	 	gpio_init(GPIOB, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_10 | GPIO_PIN_11);
	}
	i2c_clock_config(device, rate, I2C_DTCY_2);
	i2c_enable(device);
}

void shark_i2c_deinit(uint32_t i2c_index){
	uint32_t device = iic_device(i2c_index);
	i2c_disable(device);
	i2c_deinit(device);
	if (device == I2C0){
		rcu_periph_clock_disable(RCU_I2C0);
	}else {
		rcu_periph_clock_disable(RCU_I2C1);
	}
}


static int shark_i2c_write_address(uint32_t deivce, uint8_t address, uint8_t write){
	uint32_t times;
	if (!write){
		address |= 1;
	}
 
    I2C_DATA((deivce)) = address;
 	for (times = 1000; times > 0; times--) {
  		if (i2c_flag_get((deivce), I2C_FLAG_ADDSEND)) {
   			return 0;
  		}
 	}

 	return -1;	
}


int shark_i2c_read_byte(uint32_t index, uint8_t address, uint8_t reg, uint8_t *value){
	return _shark_i2c_rw_bytes(index, address, reg, value, 1, 0);
}

int shark_i2c_read_nbytes(uint32_t index, uint8_t address, uint8_t reg, uint8_t *buffer, int length){
	return _shark_i2c_rw_bytes(index, address, reg, buffer, length, 0);
}

int shark_i2c_write_byte(uint32_t index, uint8_t address, uint8_t reg, uint8_t value){
	return _shark_i2c_rw_bytes(index, address, reg, &value, 1, 1);
}

int shark_i2c_write_nbytes(uint32_t index, uint8_t address, uint8_t reg, uint8_t *value, int length){
	return _shark_i2c_rw_bytes(index, address, reg, value, length, 1);
}


static int shark_i2c_wait_flags(uint32_t i2c_periph, i2c_flag_enum flag, FlagStatus status){
	int remain = 1000;
	while (i2c_flag_get(i2c_periph, flag) != status) {
		if (remain-- <= 0){
			return -1;
		}	
	}
	return 0;
}

static void shark_i2c_wait_stop(uint32_t i2c_periph){
	int remain = 1000;
	while(I2C_CTL0(i2c_periph) & 0x0200){
		if (remain-- <= 0){
			break;
		}
	};
}

static int lock_i2c(int i2c_index){

	return 0;
}

static int unlock_i2c(int i2c_index){

	return 0;
}


static int _shark_i2c_rw_bytes(uint32_t i2c_index, uint8_t address, uint8_t reg, uint8_t *buffer, int length, int write){
	int ret = length;
	uint32_t device = iic_device(i2c_index);

	if (lock_i2c(i2c_index) == 1) {
		return -100;
	}
	i2c_ackpos_config(device, length == 2?I2C_ACKPOS_NEXT:I2C_ACKPOS_CURRENT);
	i2c_ack_config(device, I2C_ACK_ENABLE);

	if (shark_i2c_wait_flags(device, I2C_FLAG_I2CBSY, RESET) < 0){
		i2c_busy_count[i2c_index]++;
		if (i2c_busy_count[i2c_index] >= 10) {
			shark_i2c_deinit(i2c_index);
			shark_i2c_init(i2c_index, i2c_clk[i2c_index]);
			i2c_busy_count[i2c_index] = 0;
		}
		ret = -1;
		goto out_i2c_stop_on_bus;
	}
	i2c_busy_count[i2c_index] = 0;

	
	//send reg
	i2c_start_on_bus(device);

	if (shark_i2c_wait_flags(device, I2C_FLAG_SBSEND, SET) < 0){
		ret = -2;
		goto out_i2c_stop_on_bus;
	}
	
	if (shark_i2c_write_address(device, address, 1) < 0){
		ret = -3;
		goto out_i2c_stop_on_bus;
	}
	i2c_flag_clear(device, I2C_FLAG_ADDSEND);

    /* wait until the transmit data buffer is empty */
	if (shark_i2c_wait_flags(device, I2C_FLAG_TBE, SET) < 0){
		ret = -4;
		goto out_i2c_stop_on_bus;
	}

	i2c_data_transmit(device, reg);

	if (shark_i2c_wait_flags(device, I2C_FLAG_BTC, SET) < 0){
		ret = -5;
		goto out_i2c_stop_on_bus;
	}
	
	if (write){
		int index = 0;
		for (; index < length; index++){
			if (shark_i2c_wait_flags(device, I2C_FLAG_TBE, SET) < 0){
				ret = -6;
				goto out_i2c_stop_on_bus;
			}			
			i2c_data_transmit(device, buffer[index]);
		}
		if (shark_i2c_wait_flags(device, I2C_FLAG_BTC, SET) < 0){
			ret = -7;
			goto out_i2c_stop_on_bus;
		}		
		ret = length;
		goto out_i2c_stop_on_bus;
	}

	
	//begin read
	i2c_start_on_bus(device);
	if (shark_i2c_wait_flags(device, I2C_FLAG_SBSEND, SET) < 0){
		ret = -8;
		goto out_i2c_stop_on_bus;
	}

	if (shark_i2c_write_address(device, address, 0) < 0){
		ret = -9;
		goto out_i2c_stop_on_bus;
	}
	i2c_flag_clear(device, I2C_FLAG_ADDSEND);
	if (length< 3) {
	 	i2c_ack_config(device, I2C_ACK_DISABLE);
	 	
	} else {
	 	while (length > 3) {
			if (shark_i2c_wait_flags(device, I2C_FLAG_RBNE, SET) < 0){
				ret = -10;
				goto out_i2c_stop_on_bus;
			}
	  		*buffer++ = i2c_data_receive(device);
	 		 length--;
	 	}
		if (shark_i2c_wait_flags(device, I2C_FLAG_BTC, SET) < 0){
			ret = -11;
			goto out_i2c_stop_on_bus;
		}		
		i2c_ack_config(device, I2C_ACK_DISABLE);
	}
	
	while (length > 0) {
		if (shark_i2c_wait_flags(device, I2C_FLAG_RBNE, SET) < 0){
			ret = -12;
			goto out_i2c_stop_on_bus;
		}
	 	*buffer++ = i2c_data_receive(device);
	 	length--;
	}
	if (length != 0){
		ret = -13;
	}
out_i2c_stop_on_bus:
	if (ret != -1) {
 		i2c_stop_on_bus(device);
 		shark_i2c_wait_stop(device);
	}
	unlock_i2c(i2c_index);
 	return ret;
}


