#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/timer.h>
#include <libLHP_UART.h>
#include <libLHP_D1W_async.h>
#include <stdint.h>
#include <stddef.h>
#include <libopencm3/stm32/f1/nvic.h>
#include <libopencm3/stm32/exti.h>
#include "m31820.h"



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);
}

typedef enum {
		      MAIN_INIT,
		      SETUP_DOWNSTREAM_SIDE,
		      SETUP_UPSTREAM_SIDE,
		      MAIN_EVL,
		      MAIN_END
} main_state_enum_t;

typedef enum {
	      EVL_D_INIT,
	      EVL_D_SEARCHING_ROMS,
	      EVL_D_WROTE1,
	      EVL_D_READ2,
	      EVL_D_MATCHING_ROMS
} D_eventloop_state_enum_t;

typedef enum {    
	      EVL_C_INIT,
	      EVL_C_SEARCHING_ROMS,
	      EVL_C_SETTING_TEMP_CTX,
	      EVL_C_GETTING_TEMP,	      
	      EVL_C_MITM
	      
	      
} C_eventloop_state_enum_t;


volatile main_state_enum_t main_state = MAIN_INIT;

volatile D_eventloop_state_enum_t D_EVL_state = EVL_D_INIT;
void D_EVL_funct(D1W_event * evt){
  ;
}

uint8_t c_evl_sub_step = 0;
uint8_t c_evl_devcnt = 0;
volatile uint64_t c_evl_id=0;
uint64_t c_evl_oldval=0;
volatile C_eventloop_state_enum_t C_EVL_state = EVL_C_INIT;
void C_EVL_funct(D1W_event * evt){
  uint64_t id;

  /*
    GPIOC_ODR ^= (1<<13);
    GPIOC_ODR ^= (1<<13);
  */
  switch(C_EVL_state){
  case EVL_C_INIT:
    if(D1W_protocol_command_evt_handler(D1W_SEARCH_ROM,evt,1))
      C_EVL_state = EVL_C_SEARCHING_ROMS;
    break;
  case EVL_C_SEARCHING_ROMS:
    if(D1W_protocol_command_evt_handler(D1W_SEARCH_ROM,evt,0))
      C_EVL_state = EVL_C_SETTING_TEMP_CTX;
    c_evl_sub_step = 0;
    c_evl_devcnt =  D1W_async_get_device_count();
    c_evl_id = D1W_async_get_device_id(c_evl_devcnt-1);
      
    break;
  case EVL_C_SETTING_TEMP_CTX:
    /*
    printstr_blocking(USART1,"f:");
    print_uint(USART1,c_evl_devcnt);
    printstr_rn(USART1);
    */    
    evt->value = c_evl_id;
    if(D1W_protocol_command_evt_handler(M31820_WRITE_SCRATCHPAD,evt,1)){
      C_EVL_state = EVL_C_GETTING_TEMP;
    }
    break;
  case EVL_C_GETTING_TEMP:

    /*    printstr_blocking(USART1,"gt");
    print_uint(USART1,evt->event);
    */
    c_evl_oldval = evt->value;
    evt->value = c_evl_id;
    if(D1W_protocol_command_evt_handler(M31820_CONVERT,evt,1)){
      D1W_setdevicevalue(c_evl_id,c_evl_oldval);
      /*
      printstr_blocking(USART1,"t:");
      print_uint(USART1,c_evl_oldval);
      printstr_rn(USART1);*/
      m31820_print_temp(USART1,c_evl_id);
      printstr_rn(USART1);
      c_evl_devcnt--;
      if(c_evl_devcnt > 0){	
	c_evl_id = D1W_async_get_device_id(c_evl_devcnt-1);
	/*	printstr_blocking(USART1,"N:");
	print_uint(USART1,c_evl_id);
	printstr_rn(USART1);
	*/
	C_EVL_state = EVL_C_SETTING_TEMP_CTX;
	D1W_async_start_controler_presence_detect(evt->port_id);
	
      }else{
	C_EVL_state = EVL_C_MITM;
      }
    }

    break;

    
  }
}

int main(){  
  clock_setup();
  gpio_setup();
  setup_UART1();
  clrscreen_uart(USART1);
  printstr_blocking(USART1,"LHP - Bluepill\r\n"__FILE__ "\r\n"  __DATE__ " - " __TIME__ "\r\n");
  D1W_event * evt;
  // /!\ limitations :
  //      you can't use the same pin, even across different ports for different d1w driver number
  //         pins b0 and b1 is fine
  //         pins b0 and a0 is NOT
  //      pins > 4 are not supported, i leave it as an exercise for you to implement
  //      exti9_5_isr and exti15_10_isr, control which pin fired and what timer is running
  uint8_t go=1;
  while(go){
    switch(main_state){
    case MAIN_INIT:      
      D1W_async_init();
      main_state = SETUP_DOWNSTREAM_SIDE;
      break;
    case SETUP_DOWNSTREAM_SIDE:
      D1W_async_setup(0,GPIOB,0,0);
      D1W_async_start_controler_presence_detect(0);
      main_state = SETUP_UPSTREAM_SIDE;
      
      break;
    case SETUP_UPSTREAM_SIDE:
      D1W_async_setup(1,GPIOB,1,1);
      main_state = MAIN_EVL;
      break;
    case MAIN_END:
      go=0;
      break;
    case MAIN_EVL:
      evt = D1W_async_get_next_event();
      if(evt){
	
		

	
	
	/* GPIOC_ODR ^= (1<<13);
    GPIOC_ODR ^= (1<<13);	printstr_blocking(USART1,"EVT ");
		print_uint(USART1,evt->port_id);
		printstr_blocking(USART1," ");
		print_uint(USART1,evt->event);
		printstr_rn(USART1);
	*/
	if(evt->event & MASK_CONTROLER_EVENT){

	  C_EVL_funct(evt);
	    }
	if(evt->event & MASK_DEVICE_EVENT){
	  D_EVL_funct(evt);
	    }
      }
      break;
    }
  }

  while(1);  

}
