#include "system.h"
#include "usart.h"
static Usart * pInstance  = nullptr;
static constexpr unsigned HCLK = SYSTEM_CORE_CLOCK;

extern "C" void USART1_IRQHandler (void) __attribute__((interrupt));
void USART1_IRQHandler (void) {
  if (pInstance) pInstance->irq();
};

Usart::Usart(const uint32_t _baud) noexcept : BaseLayer (), tx_ring () {
  pInstance = this;
  // 1. Clock Enable
  RCC.APB2PCENR.modify([](RCC_Type::APB2PCENR_DEF & r) -> auto {
    r.B.USART1EN = SET;
    r.B.IOPAEN   = SET;
  //r.B.AFIOEN   = SET;
    return r.R;
  });
  // 2. GPIO Alternate Config - default TX/PA9, RX/PA10
  GPIOA.CFGHR.modify([](GPIOA_Type::CFGHR_DEF & r) -> auto {
    r.B.MODE9  = 1u; 
    r.B.CNF9   = 2u;  // or 3u for open drain
    r.B.MODE10 = 0u;
    r.B.CNF10  = 1u;  // floating input
    return r.R;
  });
  RCC.APB2PRSTR.B.USART1RST = SET;
  RCC.APB2PRSTR.B.USART1RST = RESET;
  // 5. USART registry 8.bit bez parity
  USART1.CTLR1.modify([] (USART1_Type::CTLR1_DEF & r) -> auto {
    r.B.RE      = SET;
    r.B.TE      = SET;
    r.B.RXNEIE  = SET;
    return r.R;
  });
  USART1.CTLR2.modify ([](USART1_Type::CTLR2_DEF & r) -> auto {
    r.B.STOP = 0u;
    return r.R;
  });
  const uint32_t tmp = HCLK / _baud;
  USART1.BRR.R = tmp;
  // NVIC
  NVIC.EnableIRQ (USART1_IRQn);
  USART1.CTLR1.B.UE = SET;        // nakonec povolit globálně
}
void Usart::irq () {
  volatile USART1_Type::STATR_DEF status (USART1.STATR);   // načti status přerušení
  char rdata, tdata;
  
  if (status.B.TXE) {                         // od vysílače
    if (tx_ring.Read (tdata)) {               // pokud máme data
      USART1.DATAR.B.DR = (uint8_t) tdata;    // zapíšeme do výstupu
    } else {                                  // pokud ne
      // Předpoklad je half-duplex i.e. RS485, jinak jen zakázat TXEIE
      rdata = (USART1.DATAR.B.DR);            // dummy read
      USART1.CTLR1.modify([](USART1_Type::CTLR1_DEF & r) -> auto {
        r.B.RE    = SET;                      // povol prijem
        r.B.TXEIE = RESET;                    // je nutné zakázat přerušení od vysílače
        return r.R;
      });
    }
  }
  if (status.B.RXNE) {                        // od přijímače
    rdata = (USART1.DATAR.B.DR);              // načteme data
    Up (&rdata, 1u);                          // a pošleme dál
  }
}
uint32_t Usart::Down(const char * data, const uint32_t len) {
  unsigned n = 0u;
  for (n=0u; n<len; n++) {
    if (!tx_ring.Write(data[n])) break;
  }
  USART1.CTLR1.modify([](USART1_Type::CTLR1_DEF & r) -> auto {
    r.B.RE    = RESET;
    r.B.TXEIE = SET;    // po povolení přerušení okamžitě přeruší
    return r.R;
  });
  return n;
}
void Usart::SetRS485  (const bool polarity) const {
}
void Usart::SetHalfDuplex  (const bool on) const {
}