#include <stdlib.h>

#include <pigpio.h>

#include "ir_hasher.h"

/*
This code forms a hash over the IR pulses generated by an
IR remote.

The remote key press is not converted into a code in the manner of
the lirc module.  No attempt is made to decode the type of protocol
used by the remote.  The hash is likely to be unique for different
keys and different remotes but this is not guaranteed.

This hashing process works for some remotes/protocols but not for
others.  The only way to find out if it works for one or more of
your remotes is to try it and see.
*/

struct _Pi_Hasher_s
{
   int gpio;
   Pi_Hasher_CB_t callback;
   int timeout;
   int in_code;
   uint32_t hash_val;
   int edges;
   int t1;
   int t2;
   int t3;
   int t4;
};


static uint32_t _hash(uint32_t hv, int old_val, int new_val)
{
   int val;

   if      (new_val < (old_val * 0.60)) val = 13;
   else if (old_val < (new_val * 0.60)) val = 23;
   else                                 val = 2;

   hv ^= val;
   hv *= 16777619; /* FNV_PRIME_32 */

   return hv;
}

static void _cb(int gpio, int level, uint32_t tick, void *user)
{
   Pi_Hasher_t * hasher;

   hasher = user;

   if (level != PI_TIMEOUT)
   {
      if (hasher->in_code == 0)
      {
         hasher->in_code = 1;

         gpioSetWatchdog(gpio, hasher->timeout);

         hasher->hash_val = 2166136261U; /* FNV_BASIS_32 */

         hasher->edges = 1;

         hasher->t1 = 0;
         hasher->t2 = 0;
         hasher->t3 = 0;
         hasher->t4 = tick;
      }
      else
      {
         hasher->edges++;

         hasher->t1 = hasher->t2;
         hasher->t2 = hasher->t3;
         hasher->t3 = hasher->t4;
         hasher->t4 = tick;

         if (hasher->edges > 3)
         {
            hasher->hash_val =
               _hash(hasher->hash_val,
                     (hasher->t2)-(hasher->t1),
                     (hasher->t4)-(hasher->t3));
         }
      }
   }
   else
   {
      if (hasher->in_code)
      {
         hasher->in_code = 0;

         gpioSetWatchdog(gpio, 0);

         if (hasher->edges > 12) /* Anything less is probably noise. */
         {
            (hasher->callback)(hasher->hash_val);
         }
      }
   }
}

Pi_Hasher_t *Pi_Hasher(int gpio, Pi_Hasher_CB_t callback, int timeout)
{
   Pi_Hasher_t *hasher;

   hasher = malloc(sizeof(Pi_Hasher_t));

   hasher->gpio     = gpio;
   hasher->callback = callback;
   hasher->timeout  = 5;

   hasher->in_code = 0;

   gpioSetMode(gpio, PI_INPUT);
   gpioSetAlertFuncEx(gpio, _cb, hasher);

   return hasher;
}

void Pi_Hasher_cancel(Pi_Hasher_t *hasher)
{
   if (hasher)
   {
      gpioSetAlertFunc(hasher->gpio, 0);

      free(hasher);

      hasher = NULL;
   }
}

