#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/f1/nvic.h>
#include <libLHP_UART.h>

#define ECHO_ENABLED 1

#define CLEAR_ESCAPE_SEQ "\ec\e[3J"

#define BUFF_SZ 257
_Alignas(8) uint8_t usart_buff[BUFF_SZ];


volatile uint16_t usart_buff_cnt = 0;
volatile uint8_t received_rn = 0;
volatile uint8_t sending_rn = 0;

volatile uint8_t echo = ECHO_ENABLED;

uint8_t UART1_received_cmd(){
  return received_rn;
}

char * get_receive_buff(){
  return usart_buff;
}

void receive_buff_processed(){
  int i ;
  for(i=0;i<BUFF_SZ;i++){
    usart_buff[i]=0;
  }
  
  usart_buff_cnt = 0;
  received_rn = 0;

}

void setup_UART1_GPIO(){
  rcc_periph_clock_enable(RCC_GPIOA);
  gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX);
  gpio_set_mode(GPIOA, GPIO_MODE_INPUT        , GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_RX);
}

void setup_UART1(){
  setup_UART1_GPIO();
  nvic_enable_irq(NVIC_USART1_IRQ);
  rcc_periph_clock_enable(RCC_USART1);
  usart_set_baudrate(USART1, 115200);
  usart_set_databits(USART1, 8);
  usart_set_stopbits(USART1, USART_STOPBITS_1);
  usart_set_mode(USART1, USART_MODE_TX_RX);
  usart_set_parity(USART1, USART_PARITY_NONE);
  usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
  /* Enable USART1 Receive interrupt. */
  USART_CR1(USART1) |= USART_CR1_RXNEIE;
  usart_enable(USART1);

}

volatile char * str_out_ptr = 0;

uint8_t isbusy_writing(){
  return( (USART1_CR1 & USART_CR1_TXEIE) !=0);
}

void printstr_blocking(uint32_t usart,char * str_p){
  while( USART1_CR1 & USART_CR1_TXEIE);
  str_out_ptr = str_p;
  USART1_DR = *str_out_ptr;
  str_out_ptr++;
  // en. tx interrupt
  USART1_CR1 |= USART_CR1_TXEIE;
  // block until the interrupt disables itself
  while( USART1_CR1 & USART_CR1_TXEIE);


}
void printstr_rn(uint32_t usart){
  printstr_blocking(usart,"\r\n");
}

void clrscreen_uart(uint32_t usart){
  printstr_blocking(usart,CLEAR_ESCAPE_SEQ);

}

void printstr(uint32_t usart,char * str_p){
  str_out_ptr = str_p;
  USART1_DR = *str_out_ptr++;
  // en. tx interrupt
  USART1_CR1 |= USART_CR1_TXEIE;  
}





void usart1_isr(void){
  uint16_t r=0;
  if (((USART_CR1(USART1) & USART_CR1_RXNEIE) != 0) &&
      ((USART_SR(USART1) & USART_SR_RXNE) != 0)) {
    // we received
   
    r= USART1_DR;
    // manage echo
    if(echo){
      // manage cariage return
      // that needs 2 characters to be displayed properly
      if( (r == '\r')){
	sending_rn=1;
	USART1_DR = r;
	// enable tx empty interrupt
	USART1_CR1 |= USART_CR1_TXEIE;	
      } else {
	// simple echo
	USART1_DR = r;
      }
    }
    // store things we receive
    if( (r == '\r')){
      received_rn = 1;
      //     usart_buff[usart_buff_cnt] = 0;
    }else{
      usart_buff[usart_buff_cnt++] = r;
      usart_buff[usart_buff_cnt] = 0;
    }
    // cycle if its too much
    if(usart_buff_cnt >= (BUFF_SZ-1))
      usart_buff_cnt=0;
    
    
  }
  if (((USART_CR1(USART1) & USART_CR1_TXEIE) != 0) &&
      ((USART_SR(USART1) & USART_SR_TXE) != 0)) {
    // we sent
 
    if(sending_rn){
      USART1_DR = '\n';sending_rn = 0;
      USART1_CR1 &= ~USART_CR1_TXEIE;
      return;
    }
    if(str_out_ptr != 0){
      // we have a string to send
      // send next char
      
      if(*str_out_ptr == 0){
        str_out_ptr=0;
        // if done sending the 0 terminated string : disable tx interrupt
        USART1_CR1 &= ~USART_CR1_TXEIE;
        return;
      }
      USART1_DR = *str_out_ptr++;
    }

    
  }

}

// printing utilities
uint8_t mem_hex[35];
char * hex_alphabet = "0123456789abcdef";

void print_uint(uint32_t usart,uint64_t inuint)
{
    uint32_t mem_sz=sizeof(mem_hex)-1;
    uint64_t i = inuint;
    uint32_t k = 1;
    mem_hex[mem_sz]=0;
    if(i==0){
      printstr_blocking(usart,"0");
      return;
    }
    while(i){
        mem_hex[mem_sz-k] = hex_alphabet[i%10];
        i/=10;
        k++;
    }
    printstr_blocking(usart,mem_hex+mem_sz-k+1);
}

void print_hex_prefix(uint32_t usart)
{
    printstr_blocking(usart,"0x");
}
void print_hex_8_noprefix(uint32_t usart,uint8_t c)
{
    mem_hex[0] = *(hex_alphabet + ((c& 0xf0) >> 4));
    mem_hex[1] = *(hex_alphabet + (c& 0x0f));
    mem_hex[2] = 0;
    printstr_blocking(usart,mem_hex);
}
void print_hex_8(uint32_t usart,uint8_t c)
{
  print_hex_prefix(usart);
  print_hex_8_noprefix(usart,c);
}


void print_hex_16_noprefix(uint32_t usart,uint16_t c)
{
    mem_hex[0] = *(hex_alphabet + ((c & 0xf000) >> 12));
    mem_hex[1] = *(hex_alphabet + ((c & 0xf00) >> 8));
    mem_hex[2] = *(hex_alphabet + ((c & 0xf0) >> 4));
    mem_hex[3] = *(hex_alphabet + ((c & 0xf)));
    mem_hex[4] = 0;
    printstr_blocking(usart,mem_hex);
}
void print_hex_16(uint32_t usart,uint16_t c)
{
  print_hex_prefix(usart);
  print_hex_16_noprefix(usart,c);
}


void print_hex_32_noprefix(uint32_t usart,uint32_t c)
{

  mem_hex[0] = *(hex_alphabet + ((c & 0xf0000000) >> 28));
  mem_hex[1] = *(hex_alphabet + ((c & 0xf000000) >> 24));
  mem_hex[2] = *(hex_alphabet + ((c & 0xf00000) >> 20));
  mem_hex[3] = *(hex_alphabet + ((c & 0xf0000) >> 16));
  mem_hex[4] = *(hex_alphabet + ((c & 0xf000) >> 12));
  mem_hex[5] = *(hex_alphabet + ((c & 0xf00) >> 8));
  mem_hex[6] = *(hex_alphabet + ((c & 0xf0) >> 4));
  mem_hex[7] = *(hex_alphabet + ((c & 0xf)));
  mem_hex[8] = 0;
  printstr_blocking(usart,mem_hex);
}
void print_hex_32(uint32_t usart,uint32_t c){
  print_hex_prefix(usart);
  print_hex_32_noprefix(usart, c);
}

void print_hex_64_noprefix(uint32_t usart,uint64_t c){
  print_hex_32_noprefix(usart,c>>32);
  print_hex_32_noprefix(usart,c & 0xffffffff);
}
void print_hex_64(uint32_t usart,uint64_t c){
  print_hex_prefix(usart);
  print_hex_64_noprefix(usart,c);
}



void print_hexdump_str(uint32_t usart,char *str , uint16_t sz){
  char * s = str;
  uint16_t c = 0;
  print_hex_prefix(usart);
  while(c<sz){
    print_hex_8_noprefix(usart,*s++);
    c++;
  }
    

}
