#include "system.h"
#include "morse.h"
static constexpr unsigned slen (const char * const str) {
  unsigned n = 0;
  while (str[n]) n++;
  return n;
}
static const unsigned char compressed_table [] = {
 0x00, 0x00, 0xd2, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xed, 0x00, 0xaa, 0xf3, 0xe1, 0xea, 0xa9,
 0xbf, 0xbe, 0xbc, 0xb8, 0xb0, 0xa0, 0xa1, 0xa3, 0xa7, 0xaf, 0xc7, 0xd5, 0x00, 0xb1, 0x00, 0xcc,
 0xd6, 0x42, 0x81, 0x85, 0x61, 0x20, 0x84, 0x63, 0x80, 0x40, 0x8e, 0x65, 0x82, 0x43, 0x41, 0x67,
 0x86, 0x8b, 0x62, 0x60, 0x21, 0x64, 0x88, 0x66, 0x89, 0x8d, 0x83, 0x00, 0x00, 0x00, 0x00, 0xec,
};

extern "C" void SysTick_Handler() __attribute__((interrupt));
static volatile unsigned count = 0u;
void SysTick_Handler () {
  SysTick.SR = 0u;
  if (count) count -= 1;
}
static void delay (const unsigned ms) {
  count = ms;
  while (count);
}
Morse::Morse(const GpioClass & pin, const unsigned int ms) noexcept : unit (ms), led (pin) {
  SysTick.Config (48000u);
}
const Morse & Morse::operator<< (const char * text) const {
  const unsigned len = slen (text);
  for (unsigned n=0; n<len; n++) {
    const char c = text [n];
    morse_byte mb;
    if (c < '\x20') {
    } else if (c <  '`') {
      const int i = c - '\x20';
      mb.byte = compressed_table [i];
    } else if (c == '`') {
    } else if (c <= 'z') {
      const int i = c - '\x40';
      mb.byte = compressed_table [i];
    } else {
    }
    out (mb);
  }
  delay (10 * unit);
  return * this;
}
/* .                    => 1 x unit
 * -                    => 3 x unit
 * mezera mezi značkami => 1 x unit
 * mezera mezi znaky    => 3 x unit
 * mezera mezi slovy    => 7 x unit
 * */
void Morse::out (const morse_byte mb) const {
  /* Finta je v tom, že i když se pole mlen a bits překrývají,
   * nevadí to - maximální délka je 6, takže v nejnižším bitu
   * mlen může být obsažen 1 bit 6.bitového znaku.
   * Takhle napsáno to běhá jen na malém indiánu, přepisovat
   * to do bitových posunů nebudu, i když by to bylo čistší.
   * */
  const unsigned len = mb.mlen > 6u ? 6u : mb.mlen;
  if (!len) { delay (4 * unit); return; }
  for (unsigned n=0; n<len; n++) {
    led << false;
    delay (unit);
    const unsigned v = (1u << n) & mb.bits;
    const unsigned d = v ? 3u : 1u;
    led << true;
    delay (d * unit);
    led << false;
  }
  delay (3 * unit);
}