// Copyright 2009 Ken Shirriff
// Copyright 2015 Mark Szabo
// Copyright 2017 David Conran
#ifndef IRSEND_H_
#define IRSEND_H_

#define __STDC_LIMIT_MACROS
#include <stdint.h>
#include "IRremoteESP8266.h"

// Originally from https://github.com/shirriff/Arduino-IRremote/
// Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for
// sending IR code on ESP8266

#if TEST || UNIT_TEST
#define VIRTUAL virtual
#else
#define VIRTUAL
#endif

// Constants
// Offset (in microseconds) to use in Period time calculations to account for
// code excution time in producing the software PWM signal.
// Value was calculated on Wemos D1 mini using v2.4.1 with v2.4.0 ESP core
const int8_t kPeriodOffset = -5;
const uint8_t kDutyDefault = 50;  // Percentage
const uint8_t kDutyMax = 100;     // Percentage
// delayMicroseconds() is only accurate to 16383us.
// Ref: https://www.arduino.cc/en/Reference/delayMicroseconds
const uint16_t kMaxAccurateUsecDelay = 16383;
//  Usecs to wait between messages we don't know the proper gap time.
const uint32_t kDefaultMessageGap = 100000;


namespace stdAc {
  enum class opmode_t {
    kOff  = -1,
    kAuto =  0,
    kCool =  1,
    kHeat =  2,
    kDry  =  3,
    kFan  =  4,
  };

  enum class fanspeed_t {
    kAuto =   0,
    kMin =    1,
    kLow =    2,
    kMedium = 3,
    kHigh =   4,
    kMax =    5,
  };

  enum class swingv_t {
    kOff =    -1,
    kAuto =    0,
    kHighest = 1,
    kHigh =    2,
    kMiddle =  3,
    kLow =     4,
    kLowest =  5,
  };

  enum class swingh_t {
    kOff =     -1,
    kAuto =     0,  // a.k.a. On.
    kLeftMax =  1,
    kLeft =     2,
    kMiddle =   3,
    kRight =    4,
    kRightMax = 5,
  };
};  // namespace stdAc

// Classes
class IRsend {
 public:
  explicit IRsend(uint16_t IRsendPin, bool inverted = false,
                  bool use_modulation = true);
  void begin();
  void enableIROut(uint32_t freq, uint8_t duty = kDutyDefault);
  VIRTUAL void _delayMicroseconds(uint32_t usec);
  VIRTUAL uint16_t mark(uint16_t usec);
  VIRTUAL void space(uint32_t usec);
  int8_t calibrate(uint16_t hz = 38000U);
  void sendRaw(uint16_t buf[], uint16_t len, uint16_t hz);
  void sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark,
                uint32_t zerospace, uint64_t data, uint16_t nbits,
                bool MSBfirst = true);
  void sendGeneric(const uint16_t headermark, const uint32_t headerspace,
                   const uint16_t onemark, const uint32_t onespace,
                   const uint16_t zeromark, const uint32_t zerospace,
                   const uint16_t footermark, const uint32_t gap,
                   const uint64_t data, const uint16_t nbits,
                   const uint16_t frequency, const bool MSBfirst,
                   const uint16_t repeat, const uint8_t dutycycle);
  void sendGeneric(const uint16_t headermark, const uint32_t headerspace,
                   const uint16_t onemark, const uint32_t onespace,
                   const uint16_t zeromark, const uint32_t zerospace,
                   const uint16_t footermark, const uint32_t gap,
                   const uint32_t mesgtime, const uint64_t data,
                   const uint16_t nbits, const uint16_t frequency,
                   const bool MSBfirst, const uint16_t repeat,
                   const uint8_t dutycycle);
  void sendGeneric(const uint16_t headermark, const uint32_t headerspace,
                   const uint16_t onemark, const uint32_t onespace,
                   const uint16_t zeromark, const uint32_t zerospace,
                   const uint16_t footermark, const uint32_t gap,
                   const uint8_t *dataptr, const uint16_t nbytes,
                   const uint16_t frequency, const bool MSBfirst,
                   const uint16_t repeat, const uint8_t dutycycle);
  bool send(decode_type_t type, uint64_t data, uint16_t nbits);
#if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO)
  void sendNEC(uint64_t data, uint16_t nbits = kNECBits,
               uint16_t repeat = kNoRepeat);
  uint32_t encodeNEC(uint16_t address, uint16_t command);
#endif
#if SEND_SONY
  // sendSony() should typically be called with repeat=2 as Sony devices
  // expect the code to be sent at least 3 times. (code + 2 repeats = 3 codes)
  // Legacy use of this procedure was to only send a single code so call it with
  // repeat=0 for backward compatibility. As of v2.0 it defaults to sending
  // a Sony command that will be accepted be a device.
  void sendSony(uint64_t data, uint16_t nbits = kSony20Bits,
                uint16_t repeat = kSonyMinRepeat);
  uint32_t encodeSony(uint16_t nbits, uint16_t command, uint16_t address,
                      uint16_t extended = 0);
#endif
#if SEND_SHERWOOD
  void sendSherwood(uint64_t data, uint16_t nbits = kSherwoodBits,
                    uint16_t repeat = kSherwoodMinRepeat);
#endif
#if SEND_SAMSUNG
  void sendSAMSUNG(uint64_t data, uint16_t nbits = kSamsungBits,
                   uint16_t repeat = kNoRepeat);
  uint32_t encodeSAMSUNG(uint8_t customer, uint8_t command);
#endif
#if SEND_SAMSUNG36
  void sendSamsung36(const uint64_t data, const uint16_t nbits = kSamsung36Bits,
                     const uint16_t repeat = kNoRepeat);
#endif
#if SEND_SAMSUNG_AC
  void sendSamsungAC(const unsigned char data[],
                     const uint16_t nbytes = kSamsungAcStateLength,
                     const uint16_t repeat = kSamsungAcDefaultRepeat);
#endif
#if SEND_LG
  void sendLG(uint64_t data, uint16_t nbits = kLgBits,
              uint16_t repeat = kNoRepeat);
  void sendLG2(uint64_t data, uint16_t nbits = kLgBits,
               uint16_t repeat = kNoRepeat);
  uint32_t encodeLG(uint16_t address, uint16_t command);
#endif
#if (SEND_SHARP || SEND_DENON)
  uint32_t encodeSharp(uint16_t address, uint16_t command,
                       uint16_t expansion = 1, uint16_t check = 0,
                       bool MSBfirst = false);
  void sendSharp(uint16_t address, uint16_t command,
                 uint16_t nbits = kSharpBits, uint16_t repeat = kNoRepeat);
  void sendSharpRaw(uint64_t data, uint16_t nbits = kSharpBits,
                    uint16_t repeat = kNoRepeat);
#endif
#if SEND_JVC
  void sendJVC(uint64_t data, uint16_t nbits = kJvcBits,
               uint16_t repeat = kNoRepeat);
  uint16_t encodeJVC(uint8_t address, uint8_t command);
#endif
#if SEND_DENON
  void sendDenon(uint64_t data, uint16_t nbits = DENON_BITS,
                 uint16_t repeat = kNoRepeat);
#endif
#if SEND_SANYO
  uint64_t encodeSanyoLC7461(uint16_t address, uint8_t command);
  void sendSanyoLC7461(uint64_t data, uint16_t nbits = kSanyoLC7461Bits,
                       uint16_t repeat = kNoRepeat);
#endif
#if SEND_DISH
  // sendDISH() should typically be called with repeat=3 as DISH devices
  // expect the code to be sent at least 4 times. (code + 3 repeats = 4 codes)
  // Legacy use of this procedure was only to send a single code
  // so use repeat=0 for backward compatibility.
  void sendDISH(uint64_t data, uint16_t nbits = kDishBits,
                uint16_t repeat = kDishMinRepeat);
#endif
#if (SEND_PANASONIC || SEND_DENON)
  void sendPanasonic64(uint64_t data, uint16_t nbits = kPanasonicBits,
                       uint16_t repeat = kNoRepeat);
  void sendPanasonic(uint16_t address, uint32_t data,
                     uint16_t nbits = kPanasonicBits,
                     uint16_t repeat = kNoRepeat);
  uint64_t encodePanasonic(uint16_t manufacturer, uint8_t device,
                           uint8_t subdevice, uint8_t function);
#endif
#if SEND_RC5
  void sendRC5(uint64_t data, uint16_t nbits = kRC5XBits,
               uint16_t repeat = kNoRepeat);
  uint16_t encodeRC5(uint8_t address, uint8_t command,
                     bool key_released = false);
  uint16_t encodeRC5X(uint8_t address, uint8_t command,
                      bool key_released = false);
  uint64_t toggleRC5(uint64_t data);
#endif
#if SEND_RC6
  void sendRC6(uint64_t data, uint16_t nbits = kRC6Mode0Bits,
               uint16_t repeat = kNoRepeat);
  uint64_t encodeRC6(uint32_t address, uint8_t command,
                     uint16_t mode = kRC6Mode0Bits);
  uint64_t toggleRC6(uint64_t data, uint16_t nbits = kRC6Mode0Bits);
#endif
#if SEND_RCMM
  void sendRCMM(uint64_t data, uint16_t nbits = kRCMMBits,
                uint16_t repeat = kNoRepeat);
#endif
#if SEND_COOLIX
  void sendCOOLIX(uint64_t data, uint16_t nbits = kCoolixBits,
                  uint16_t repeat = kCoolixDefaultRepeat);
#endif
#if SEND_WHYNTER
  void sendWhynter(uint64_t data, uint16_t nbits = kWhynterBits,
                   uint16_t repeat = kNoRepeat);
#endif
#if SEND_MITSUBISHI
  void sendMitsubishi(uint64_t data, uint16_t nbits = kMitsubishiBits,
                      uint16_t repeat = kMitsubishiMinRepeat);
#endif
#if SEND_MITSUBISHI2
  void sendMitsubishi2(uint64_t data, uint16_t nbits = kMitsubishiBits,
                       uint16_t repeat = kMitsubishiMinRepeat);
#endif
#if SEND_MITSUBISHI_AC
  void sendMitsubishiAC(unsigned char data[],
                        uint16_t nbytes = kMitsubishiACStateLength,
                        uint16_t repeat = kMitsubishiACMinRepeat);
#endif
#if SEND_MITSUBISHIHEAVY
  void sendMitsubishiHeavy88(
      const unsigned char data[],
      const uint16_t nbytes = kMitsubishiHeavy88StateLength,
      const uint16_t repeat = kMitsubishiHeavy88MinRepeat);
  void sendMitsubishiHeavy152(
      const unsigned char data[],
      const uint16_t nbytes = kMitsubishiHeavy152StateLength,
      const uint16_t repeat = kMitsubishiHeavy152MinRepeat);
#endif
#if SEND_FUJITSU_AC
  void sendFujitsuAC(unsigned char data[], uint16_t nbytes,
                     uint16_t repeat = kFujitsuAcMinRepeat);
#endif
#if SEND_GLOBALCACHE
  void sendGC(uint16_t buf[], uint16_t len);
#endif
#if SEND_KELVINATOR
  void sendKelvinator(unsigned char data[],
                      uint16_t nbytes = kKelvinatorStateLength,
                      uint16_t repeat = kKelvinatorDefaultRepeat);
#endif
#if SEND_DAIKIN
  void sendDaikin(const unsigned char data[],
                  const uint16_t nbytes = kDaikinStateLength,
                  const uint16_t repeat = kDaikinDefaultRepeat);
#endif
#if SEND_DAIKIN2
  void sendDaikin2(unsigned char data[], uint16_t nbytes = kDaikin2StateLength,
                   uint16_t repeat = kDaikin2DefaultRepeat);
#endif
#if SEND_DAIKIN216
  void sendDaikin216(const unsigned char data[],
                     const uint16_t nbytes = kDaikin216StateLength,
                     const uint16_t repeat = kDaikin216DefaultRepeat);
#endif
#if SEND_AIWA_RC_T501
  void sendAiwaRCT501(uint64_t data, uint16_t nbits = kAiwaRcT501Bits,
                      uint16_t repeat = kAiwaRcT501MinRepeats);
#endif
#if SEND_GREE
  void sendGree(uint64_t data, uint16_t nbits = kGreeBits,
                uint16_t repeat = kGreeDefaultRepeat);
  void sendGree(uint8_t data[], uint16_t nbytes = kGreeStateLength,
                uint16_t repeat = kGreeDefaultRepeat);
#endif
#if SEND_PRONTO
  void sendPronto(uint16_t data[], uint16_t len, uint16_t repeat = kNoRepeat);
#endif
#if SEND_ARGO
  void sendArgo(unsigned char data[], uint16_t nbytes = kArgoStateLength,
                uint16_t repeat = kArgoDefaultRepeat);
#endif
#if SEND_TROTEC
  void sendTrotec(unsigned char data[], uint16_t nbytes = kTrotecStateLength,
                  uint16_t repeat = kTrotecDefaultRepeat);
#endif
#if SEND_NIKAI
  void sendNikai(uint64_t data, uint16_t nbits = kNikaiBits,
                 uint16_t repeat = kNoRepeat);
#endif
#if SEND_TOSHIBA_AC
  void sendToshibaAC(unsigned char data[],
                     uint16_t nbytes = kToshibaACStateLength,
                     uint16_t repeat = kToshibaACMinRepeat);
#endif
#if SEND_MIDEA
  void sendMidea(uint64_t data, uint16_t nbits = kMideaBits,
                 uint16_t repeat = kMideaMinRepeat);
#endif
#if SEND_MAGIQUEST
  void sendMagiQuest(uint64_t data, uint16_t nbits = kMagiquestBits,
                     uint16_t repeat = kNoRepeat);
  uint64_t encodeMagiQuest(uint32_t wand_id, uint16_t magnitude);
#endif
#if SEND_LASERTAG
  void sendLasertag(uint64_t data, uint16_t nbits = kLasertagBits,
                    uint16_t repeat = kLasertagMinRepeat);
#endif
#if SEND_CARRIER_AC
  void sendCarrierAC(uint64_t data, uint16_t nbits = kCarrierAcBits,
                     uint16_t repeat = kCarrierAcMinRepeat);
#endif
#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02)
  void sendHaierAC(unsigned char data[], uint16_t nbytes = kHaierACStateLength,
                   uint16_t repeat = kHaierAcDefaultRepeat);
#endif
#if SEND_HAIER_AC_YRW02
  void sendHaierACYRW02(unsigned char data[],
                        uint16_t nbytes = kHaierACYRW02StateLength,
                        uint16_t repeat = kHaierAcYrw02DefaultRepeat);
#endif
#if SEND_HITACHI_AC
  void sendHitachiAC(unsigned char data[],
                     uint16_t nbytes = kHitachiAcStateLength,
                     uint16_t repeat = kHitachiAcDefaultRepeat);
#endif
#if SEND_HITACHI_AC1
  void sendHitachiAC1(unsigned char data[],
                      uint16_t nbytes = kHitachiAc1StateLength,
                      uint16_t repeat = kNoRepeat);
#endif
#if SEND_HITACHI_AC2
  void sendHitachiAC2(unsigned char data[],
                      uint16_t nbytes = kHitachiAc2StateLength,
                      uint16_t repeat = kNoRepeat);
#endif
#if SEND_GICABLE
  void sendGICable(uint64_t data, uint16_t nbits = kGicableBits,
                   uint16_t repeat = kGicableMinRepeat);
#endif
#if SEND_WHIRLPOOL_AC
  void sendWhirlpoolAC(unsigned char data[],
                       uint16_t nbytes = kWhirlpoolAcStateLength,
                       uint16_t repeat = kWhirlpoolAcDefaultRepeat);
#endif
#if SEND_LUTRON
  void sendLutron(uint64_t data, uint16_t nbits = kLutronBits,
                  uint16_t repeat = kNoRepeat);
#endif
#if SEND_ELECTRA_AC
  void sendElectraAC(unsigned char data[],
                     uint16_t nbytes = kElectraAcStateLength,
                     uint16_t repeat = kNoRepeat);
#endif
#if SEND_PANASONIC_AC
  void sendPanasonicAC(unsigned char data[],
                       uint16_t nbytes = kPanasonicAcStateLength,
                       uint16_t repeat = kPanasonicAcDefaultRepeat);
#endif
#if SEND_PIONEER
  void sendPioneer(const uint64_t data, const uint16_t nbits = kPioneerBits,
                   const uint16_t repeat = kNoRepeat);
  uint64_t encodePioneer(uint16_t address, uint16_t command);
#endif
#if SEND_MWM
  void sendMWM(unsigned char data[], uint16_t nbytes,
               uint16_t repeat = kNoRepeat);
#endif
#if SEND_VESTEL_AC
  void sendVestelAc(const uint64_t data, const uint16_t nbits = kVestelAcBits,
                    const uint16_t repeat = kNoRepeat);
#endif
#if SEND_TCL112AC
  void sendTcl112Ac(const unsigned char data[],
                    const uint16_t nbytes = kTcl112AcStateLength,
                    const uint16_t repeat = kTcl112AcDefaultRepeat);
#endif
#if SEND_TECO
  void sendTeco(uint64_t data, uint16_t nbits = kTecoBits,
                uint16_t repeat = kNoRepeat);
#endif
#if SEND_LEGOPF
  void sendLegoPf(const uint64_t data, const uint16_t nbits = kLegoPfBits,
                  const uint16_t repeat = kLegoPfMinRepeat);
#endif


 protected:
#ifdef UNIT_TEST
#ifndef HIGH
#define HIGH 0x1
#endif
#ifndef LOW
#define LOW 0x0
#endif
#endif  // UNIT_TEST
  uint8_t outputOn;
  uint8_t outputOff;
  VIRTUAL void ledOff();
  VIRTUAL void ledOn();
#ifndef UNIT_TEST

 private:
#else
  uint32_t _freq_unittest;
#endif  // UNIT_TEST
  uint16_t onTimePeriod;
  uint16_t offTimePeriod;
  uint16_t IRpin;
  int8_t periodOffset;
  uint8_t _dutycycle;
  bool modulation;
  uint32_t calcUSecPeriod(uint32_t hz, bool use_offset = true);
};

#endif  // IRSEND_H_
