#include "morse.h"
#include "utils.h"
// Spočteme číslo (pro 1kHz) jako (1000 << 32) / 24000 (24 kHz je samplerate).
static constexpr unsigned F0 = (1000ull << 32) / 24000u;

static constexpr const char * const morse_code [] = { /* nedefinované znaky nahrazeny mezerou */
  " ",      /* */  " ",      /*!*/  ".-..-.", /*\"*/ " ",      /*#*/  " ",      /*$*/
  " ",      /*%*/  " ",      /*&*/  ".----.", /*\'*/ "-.--.",  /*(*/  "-.--.-", /*)*/
  " ",      /***/  ".-.-.",  /*+*/  "--..--", /*,*/  "-....-", /*-*/  ".-.-.-", /*.*/  "-..-." , /*/*/
  "-----",  /*0*/  ".----",  /*1*/  "..---",  /*2*/  "...--",  /*3*/  "....-",  /*4*/
  ".....",  /*5*/  "-....",  /*6*/  "--...",  /*7*/  "---..",  /*8*/  "----.",  /*9*/
  "---...", /*:*/  "-.-.-.", /*;*/  " ",      /*<*/  "-...-" , /*=*/  " ",      /*>*/  "..--..", /*?*/
  ".--.-.", /*@*/
  ".-",   /*A*/  "-...", /*B*/  "-.-.", /*C*/  "-..",  /*D*/  ".",    /*E*/  "..-.", /*F*/
  "--.",  /*G*/  "....", /*H*/  "..",   /*I*/  ".---", /*J*/  "-.-",  /*K*/  ".-..", /*L*/
  "--",   /*M*/  "-.",   /*N*/  "---",  /*O*/  ".--.", /*P*/  "--.-", /*Q*/  ".-.",  /*R*/
  "...",  /*S*/  "-",    /*T*/  "..-",  /*U*/  "...-", /*V*/  ".--",  /*W*/  "-..-", /*X*/
  "-.--", /*Y*/  "--..", /*Z*/  " ", " ", " ", " ",  "..--.-", /*_*/
};
static constexpr unsigned slen (const char * const str) {
  unsigned n = 0;
  while (str[n]) n++;
  return n;
}
static const TABLE<unsigned char, array_size (morse_code)> compressed_table
 ([](const unsigned n) -> auto {
  const char * const ptr = morse_code [n];
  const unsigned len = slen (ptr);
  unsigned char mb = 0u;
  if (ptr [0] == ' ') return mb;
  mb = (len & 7u) << 5;
  for (unsigned n=0; n<len; n++) {
    if (ptr [n] == '-') mb |= (1u << n);
  }
  return mb;
});
extern void print_morse_table (const TABLE<unsigned char, array_size(morse_code)> & tab);

Morse::Morse(const GpioClass & pin, const unsigned int ms) noexcept : unit (ms), led (pin),
  gen (F0), pwm () {
  pwm.attach(gen);
#ifdef __linux__
  print_morse_table (compressed_table);
#endif
}
const Morse & Morse::operator<< (const char * text) {
  for (unsigned n=0; ; n++) {
    const char c = text [n];
    if (c == '\0') break;
    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);
  }
  gen.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) {
  /* 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) { gen.delay (4 * unit); return; }
  for (unsigned n=0; n<len; n++) {
    off ();
    gen.delay (unit);
    const unsigned v = (1u << n) & mb.bits;
    const unsigned d = v ? 3u : 1u;
    on  ();
    gen.delay (d * unit);
    off ();
  }
  gen.delay (3 * unit);
}