#include "uart3.h"

namespace Usart3
{
    volatile uint16_t tx_cnt_e;
    volatile uint16_t rx_cnt_e;
    volatile uint16_t tx_cnt_b;
    volatile uint16_t rx_cnt_b;
    volatile uint8_t tx[512];
    volatile uint8_t rx[512];
    volatile bool flag = 1;
    
    void Init()
    {
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); //Periph clocking
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
    
        GPIO_InitTypeDef param; //port C configuration
        param.GPIO_Mode = GPIO_Mode_AF;
        param.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
        param.GPIO_PuPd = GPIO_PuPd_NOPULL;
        param.GPIO_OType = GPIO_OType_PP;
        param.GPIO_Speed = GPIO_Speed_100MHz;
        GPIO_Init(GPIOC, &param);
        
        GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_USART3);
        GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_USART3);
    
        USART_InitTypeDef usart_param; //USART 3 configuration
        usart_param.USART_BaudRate = 9600; //speed in Bods
        usart_param.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
        usart_param.USART_StopBits = USART_StopBits_1;
        usart_param.USART_Parity = USART_Parity_No;
        usart_param.USART_WordLength = USART_WordLength_8b;
        usart_param.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
        USART_Init(USART3, &usart_param);
    
        USART_ITConfig(USART3, USART_IT_TC, ENABLE); //transmission complete interrupt
        USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); //RX not empty interrupt
    
        USART_Cmd(USART3, ENABLE); //usart 3 enable
        
        NVIC_SetPriority(USART3_IRQn, IRQ_PRIORITY_LEVEL_HIGH);
        
        tx_cnt_e = 0;
        rx_cnt_e = 0;
        tx_cnt_b = 0;
        rx_cnt_b = 0;
        
        NVIC_EnableIRQ(USART3_IRQn);
    }
    
    uint8_t read()
    {
        uint8_t byte;
        ENTER_CRITICAL_SECTION();
        byte = rx[rx_cnt_b];
        rx_cnt_b++;
        if(rx_cnt_b > 511) rx_cnt_b = 0;
        EXIT_CRITICAL_SECTION();
        return byte;
    }
    
    uint8_t peek()
    {
        uint8_t byte;
        ENTER_CRITICAL_SECTION();
        byte = rx[rx_cnt_b];
        if(rx_cnt_b > 511) rx_cnt_b = 0;
        EXIT_CRITICAL_SECTION();
        return byte;
    }
    
    uint16_t available()
    {
        uint16_t result;
        ENTER_CRITICAL_SECTION();
        if(rx_cnt_e >= rx_cnt_b)
        {
            result = rx_cnt_e - rx_cnt_b;
        }
        else
        {
            result = 512 - rx_cnt_b + rx_cnt_e;
        }
        EXIT_CRITICAL_SECTION();
        return result;
    }

    void write(uint8_t byte)
    {
        ENTER_CRITICAL_SECTION();
        if(flag) //if transmission complete send byte immediately
        {
            USART3->DR = byte;
            flag = 0;
        }
        else //else write byte to buffer
        {
			tx[tx_cnt_e] = byte;
			tx_cnt_e++;
			if(tx_cnt_e>511) tx_cnt_e = 0;
        }
        EXIT_CRITICAL_SECTION();
    }
}

extern "C"
{
    void USART3_IRQHandler(void)
    {
        uint16_t st = USART3->SR; //check interrupt status
        
        if(st & USART_SR_RXNE) //if RX not empty
        {
            Usart3::rx[Usart3::rx_cnt_e] = USART3->DR; //read byte to buffer
            Usart3::rx_cnt_e++;
            if(Usart3::rx_cnt_e > 511) Usart3::rx_cnt_e = 0;
        }
        if(st & USART_SR_TC) //if transmission complete
        {
            USART_ClearITPendingBit(USART3, USART_IT_TC);
            if(Usart3::tx_cnt_e != Usart3::tx_cnt_b) //if buffer have bytes
            {
                USART3->DR = Usart3::tx[Usart3::tx_cnt_b]; //send byte from buffer
                Usart3::tx_cnt_b++;
				if(Usart3::tx_cnt_b > 511) Usart3::tx_cnt_b = 0;
            }
            else
            {
                Usart3::flag = 1; //ready to sending
            }
        }
        if(USART3->SR & USART_SR_ORE) //if usart full of garbage
        {
            volatile uint8_t byte = USART3->DR; //clear data register
            (void)byte;
        }
    }
}
