#include "ConvertPronto.h"
#include "OneButton.h"
#include "OneSample.h"
#include "RawIR.h"
#include <vector>

// Algorithm for pronto code recognition taken from remote central
// article starting here:
// http://www.remotecentral.com/features/irdisp1.htm

OneButton *
ConvertPronto(QString pronto_string) {

   // Collect the integer values of the hex numbers in the 'pi' array

   std::vector<int> pi;
   int len = pronto_string.size();
   int state = 0;
   int accum;
   for (int i = 0; i < len; ++i) {
      QChar c = pronto_string.at(i);
      switch(state) {
      case 0:
         if (c.isSpace()) {
            state = 0;
         } else if (c.isDigit()) {
            accum = c.digitValue();
            state = 1;
         } else if (c.isLetter()) {
            char ltr = c.toUpper().toLatin1();
            if ((ltr >= 'A') && (ltr <= 'F')) {
               accum = 10 + (ltr - 'A');
               state = 1;
            } else {
               return NULL;
            }
         } else {
            return NULL;
         }
         break;
      case 1:
         if (c.isSpace()) {
            pi.push_back(accum);
            state = 0;
         } else if (c.isDigit()) {
            accum *= 16;
            accum += c.digitValue();
            state = 1;
         } else if (c.isLetter()) {
            char ltr = c.toUpper().toLatin1();
            if ((ltr >= 'A') && (ltr <= 'F')) {
               accum *= 16;
               accum += (10 + (ltr - 'A'));
               state = 1;
            } else {
               return NULL;
            }
         } else {
            return NULL;
         }
         break;
      }
   }
   if (state == 1) {
      pi.push_back(accum);
   }
   if (pi.size() < 4) {
      // The header should always have at least 4 numbers.
      return NULL;
   }
   if (pi.at(0) != 0) {
      // Zero in 1st number means raw IR codes, which are the only kind
      // this code is equipped to interpret.
      return NULL;
   }
   int tot_len = 2 * (pi.at(2) + pi.at(3)) + 4;
   if (tot_len != (int)pi.size()) {
      // The header describing the length isn't consistent with the
      // actual length I read from the string.
      return NULL;
   }

   // Convert "pronto units" to kilohertz

   int khz = (int)(1000.0/(((double)pi.at(1)) * 0.241246));

   // Now the "burst pairs" remaining represent the mark/space times in
   // units of number of cycles of the modulation frequency. I need to
   // convert that to the AardRemote units of microseconds/4.
   //
   // So, with khz cycles per second, 1 cycle is 1/(khz*1000) seconds.
   // N cycles are then N*(1/(khz*1000)) seconds or 1000000*N*(1/(khz*1000))
   // microseconds or (1000000*N*(1/(khz*1000)))/4 microseconds/4.

   double cvt = (double)(1000000/4) / ((double)(khz * 1000));

   OneButton * rval = NULL;
   int ndx = 4;
   for (int bp = 2; bp <= 3; ++bp) {
      int cnt = 2 * pi.at(bp);
      unsigned short flag = BGIR_MARK;
      std::vector<unsigned short> samps;
      while (cnt-- > 0) {
         int t = pi.at(ndx++);
         unsigned short ta = (unsigned short)((((double)t) * cvt) + 0.5);
         ta |= flag;
         if (flag == BGIR_MARK) {
            flag = BGIR_SPACE;
         } else {
            flag = BGIR_MARK;
         }
         samps.push_back(ta);
      }
      if (samps.size() > 0) {
         if (rval == NULL) {
            rval = new OneButton;
            rval->SetModulationKiloHertz(khz);
         }
         rval->AppendSample(new OneSample(samps));
      }
   }
   return rval;
}
