#ifndef BackgroundIR_h
#define BackgroundIR_h

#include "Arduino.h"

// Resources used by these routines:
//
// timer2 - Used only during IR sending in phase correct PWM mode to
//          generate modulated IR output on pin 3 (OC2B output).
//
// timer1 - used for both sending and receiving in normal mode.
//          OCR1A interrupt used to time IR output when sending.
//          Pin 8 (ICP1) used with input capture interrupt to record
//          length of IR MARK and SPACE pulses when receiving IR.
//          OCR1B used in receiving to detect timeout at end of
//          final pulse.
//
// OC2B output (pin 3) - output to drive IR emitter.
//
// ICP1 input (pin 8) - input from IR receiver module.
//
// All sending and receiving operate at interrupt level and communicate via
// a circular buffer, so the main loop can do other things (just don't block
// interrupts too long). Due to extremely small ram, the same circular
// buffer is used for both send and receive, so you can't do both at the
// same time (though in theory you could probably add a separate buffer
// and allow both at the same time).
//
// Interrupts used: TIMER1_CAPT_vect, TIMER1_COMPB_vect, TIMER1_COMPA_vect

#define SYSCLOCK 16000000

#define IR_RECIEVE_PIN 8

#define IR_XMIT_PIN 3
#define IR_PWM_ENABLE _BV(COM2B1)

// See RawIR.h for the format of raw IR codes as stashed in 16 bit words.

// All this stuff resides in the BackgroundIR namespace.

namespace BackgroundIR {

   enum IRState {
      BGIR_STOPPED,     // Receiver stopped
      BGIR_WAITING,     // Receiver started, but no data available now
      BGIR_HAS_DATA,    // One or more mark/space/status sequences available
      BGIR_SENDING,     // Queued up data is still being sent
   };

      // Call InitializeIR() during setup to setup all the timers
      // and pins used by this library to send and recieve IR. This
      // will initialize things with the receiver turned off.
      //
   void
   InitializeIR();

      // Return the current state of IR routines. While it reports
      // BGIR_HAS_DATA you can call GetData(). While it reports
      // BGIR_SENDING, it is still in the process of sending the
      // data you handed to PutData().
      //
   IRState
   GetState();

      // Enable the interrupt routine and begin listening for a
      // new IR input. (No receiving will happen unless you call this).
      // After calling this you may see GetState() return BGIR_WAITING
      // or BGIR_HAS_DATA.
      //
   void
   StartReceivingIR();

      // Disable the interrupt routine and stop listening for IR.
      // After calling this you will see BGIR_STOPPED when calling
      // GetState().
      //
   void
   StopReceivingIR();

      // Return true if the receiver is normally running by default
      // (even if it is turned off at the moment due to a send in progress).
      //
   bool
   ReceiverNormallyRunning();

      // Return IR data in format described above (undefined results
      // if you call this when GetState() != BGIR_HAS_DATA).
      //
   unsigned int
   GetData();

      // Setup timer2 to send IR with MARK pulses modulated at the
      // specified kHz rate (rate stays the same till changed, initialized
      // to 38kHz). Input units are kHz so pass 38 to mean 38000, etc.
      //
      // Return true if all OK, false if the requested rate cannot be
      // generated by the PWM output from timer2.
      //
   bool
   SetIRModulation(int kHz);

      // Return the last frequency successfully set by SetIRModulation().
      //
   int
   GetIRModulation();

      // Just check to see if PutData() would succeed, but don't push
      // anything.
      //
   bool
   PutDataOK();

      // Queue up a mark or space to be sent. Returns true if it was able
      // to push the data on the queue, false if the queue is currently
      // full and you should try again later.
      //
   bool
   PutData(unsigned int ms_data);

      // Report the size of the circular buffer compiled into the code.
      //
   int
   GetBufferSize();

      // Some "global" errors can occur if sending or receiving is
      // interrupted in the middle of the process. This routine checks
      // all the global error flags and resets them if they are set,
      // returning a bitmask of the global errors that were set.
      //
   unsigned int
   GetGlobalErrors();
};

#endif // BackgroundIR_h
