// Copyright 2017 David Conran

#include "ir_Midea.h"
#include "IRac.h"
#include "IRsend.h"
#include "IRsend_test.h"
#include "gtest/gtest.h"

// Tests for sendMidea().

// Test sending typical data only.
TEST(TestSendMidea, SendDataOnly) {
  IRsendTest irsend(4);
  irsend.begin();

  irsend.reset();
  irsend.sendMidea(0x0);
  EXPECT_EQ(
      "f38000d50"
      "m4480s4480"
      "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
      "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
      "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
      "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
      "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
      "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
      "m560s5600"
      "m4480s4480"
      "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680"
      "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680"
      "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680"
      "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680"
      "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680"
      "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680"
      "m560s105600",
      irsend.outputStr());

  irsend.reset();
  irsend.sendMidea(0x55AA55AA55AA);
  EXPECT_EQ(
      "f38000d50"
      "m4480s4480"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s5600"
      "m4480s4480"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s105600",
      irsend.outputStr());

  irsend.reset();
  irsend.sendMidea(0xFFFFFFFFFFFF);
  EXPECT_EQ(
      "f38000d50"
      "m4480s4480"
      "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680"
      "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680"
      "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680"
      "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680"
      "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680"
      "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680"
      "m560s5600"
      "m4480s4480"
      "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
      "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
      "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
      "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
      "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
      "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
      "m560s105600",
      irsend.outputStr());
}

// Test sending with different repeats.
TEST(TestSendMidea, SendWithRepeats) {
  IRsendTest irsend(4);
  irsend.begin();

  irsend.reset();
  irsend.sendMidea(0x55AA55AA55AA, kMideaBits, 1);  // 1 repeat.
  EXPECT_EQ(
      "f38000d50"
      "m4480s4480"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s5600"
      "m4480s4480"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s105600"
      "m4480s4480"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s5600"
      "m4480s4480"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s105600",
      irsend.outputStr());
  irsend.sendMidea(0x55AA55AA55AA, kMideaBits, 2);  // 2 repeats.
  EXPECT_EQ(
      "f38000d50"
      "m4480s4480"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s5600"
      "m4480s4480"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s105600"
      "m4480s4480"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s5600"
      "m4480s4480"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s105600"
      "m4480s4480"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s5600"
      "m4480s4480"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680"
      "m560s105600",
      irsend.outputStr());
}

// Test sending an atypical data size.
TEST(TestSendMidea, SendUnusualSize) {
  IRsendTest irsend(4);
  irsend.begin();

  irsend.reset();
  irsend.sendMidea(0x0, 8);
  EXPECT_EQ(
      "f38000d50"
      "m4480s4480"
      "m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
      "m560s5600"
      "m4480s4480"
      "m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680"
      "m560s105600",
      irsend.outputStr());

  irsend.reset();
  irsend.sendMidea(0x1234567890ABCDEF, 64);
  EXPECT_EQ(
      "f38000d50"
      "m4480s4480"
      "m560s560m560s560m560s560m560s1680m560s560m560s560m560s1680m560s560"
      "m560s560m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s1680m560s560"
      "m560s560m560s1680m560s1680m560s1680m560s1680m560s560m560s560m560s560"
      "m560s1680m560s560m560s560m560s1680m560s560m560s560m560s560m560s560"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s1680"
      "m560s1680m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680"
      "m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s1680m560s1680"
      "m560s5600"
      "m4480s4480"
      "m560s1680m560s1680m560s1680m560s560m560s1680m560s1680m560s560m560s1680"
      "m560s1680m560s1680m560s560m560s560m560s1680m560s560m560s1680m560s1680"
      "m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s560m560s1680"
      "m560s1680m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680"
      "m560s560m560s1680m560s1680m560s560m560s1680m560s1680m560s1680m560s1680"
      "m560s560m560s1680m560s560m560s1680m560s560m560s1680m560s560m560s560"
      "m560s560m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560"
      "m560s560m560s560m560s560m560s1680m560s560m560s560m560s560m560s560"
      "m560s105600",
      irsend.outputStr());

  // Bit sizes must be a multiple of 8.
  irsend.reset();
  irsend.sendMidea(0x0, 17);
  EXPECT_EQ("", irsend.outputStr());
}

// Tests for IRMideaAC class.

// Tests for controlling the power state.
TEST(TestMideaACClass, Power) {
  IRMideaAC midea(0);
  midea.begin();

  midea.setRaw(0xA1026FFFFFE2);  // Power off.

  midea.on();
  EXPECT_TRUE(midea.getPower());

  EXPECT_EQ(0xA1826FFFFF62, midea.getRaw());

  midea.off();
  EXPECT_FALSE(midea.getPower());
  EXPECT_EQ(0xA1026FFFFFE2, midea.getRaw());

  midea.setPower(true);
  EXPECT_TRUE(midea.getPower());
  EXPECT_EQ(0xA1826FFFFF62, midea.getRaw());

  midea.setPower(false);
  EXPECT_FALSE(midea.getPower());
  EXPECT_EQ(0xA1026FFFFFE2, midea.getRaw());
}

// Tests for the various Checksum routines.
TEST(TestMideaACClass, Checksums) {
  IRMideaAC midea(0);
  midea.begin();

  // Known good states
  EXPECT_EQ(0x62, IRMideaAC::calcChecksum(0xA1826FFFFF62));
  EXPECT_EQ(0x70, IRMideaAC::calcChecksum(0xA18177FFFF70));
  // Now without the checksum part.
  EXPECT_EQ(0x62, IRMideaAC::calcChecksum(0xA1826FFFFF00));
  EXPECT_EQ(0x70, IRMideaAC::calcChecksum(0xA18177FFFF00));
  // Made up values.
  EXPECT_EQ(0x00, IRMideaAC::calcChecksum(0x000000000000));
  EXPECT_EQ(0xDF, IRMideaAC::calcChecksum(0x1234567890AB));
  EXPECT_EQ(0xA0, IRMideaAC::calcChecksum(0xFFFFFFFFFFFF));
  // Larger than expected value (full 64bit)
  EXPECT_EQ(0xDF, IRMideaAC::calcChecksum(0xFF1234567890AB));
  EXPECT_EQ(0xDF, IRMideaAC::calcChecksum(0x551234567890AB));

  // Validity tests.
  EXPECT_TRUE(IRMideaAC::validChecksum(0xA1826FFFFF62));
  EXPECT_TRUE(IRMideaAC::validChecksum(0xA18177FFFF70));
  EXPECT_FALSE(IRMideaAC::validChecksum(0x1234567890AB));

  // Doing a setRaw() with a bad state should make a valid checksum on getRaw().
  midea.setRaw(0xA1826FFFFF00);
  EXPECT_EQ(0xA1826FFFFF62, midea.getRaw());
}

TEST(TestMideaACClass, OperatingMode) {
  IRMideaAC midea(0);
  midea.begin();

  midea.setRaw(0xA1826FFFFF62);  // Auto mode already set.
  midea.setMode(kMideaACAuto);
  EXPECT_EQ(kMideaACAuto, midea.getMode());
  EXPECT_EQ(0xA1826FFFFF62, midea.getRaw());  // State shouldn't have changed.

  midea.setMode(kMideaACCool);
  EXPECT_EQ(kMideaACCool, midea.getMode());
  EXPECT_EQ(0xA1806FFFFF61, midea.getRaw());

  midea.setMode(kMideaACAuto);
  EXPECT_EQ(kMideaACAuto, midea.getMode());
  EXPECT_EQ(0xA1826FFFFF62, midea.getRaw());

  midea.setMode(kMideaACHeat);
  EXPECT_EQ(kMideaACHeat, midea.getMode());
  EXPECT_EQ(0xA1836FFFFF63, midea.getRaw());

  midea.setMode(kMideaACDry);
  EXPECT_EQ(kMideaACDry, midea.getMode());
  EXPECT_EQ(0xA1816FFFFF60, midea.getRaw());

  midea.setMode(kMideaACFan);
  EXPECT_EQ(kMideaACFan, midea.getMode());
  EXPECT_EQ(0xA1846FFFFF66, midea.getRaw());

  midea.setMode(255);
  EXPECT_EQ(kMideaACAuto, midea.getMode());
  EXPECT_EQ(0xA1826FFFFF62, midea.getRaw());
}

TEST(TestMideaACClass, FanSpeed) {
  IRMideaAC midea(0);
  midea.begin();

  midea.setRaw(0xA1826FFFFF62);  // Auto mode already set.
  EXPECT_EQ(kMideaACFanAuto, midea.getFan());

  midea.setFan(kMideaACFanLow);
  EXPECT_EQ(kMideaACFanLow, midea.getFan());
  EXPECT_EQ(0xA18A6FFFFF6C, midea.getRaw());

  midea.setFan(255);  // Setting an unexpected value defaults to auto.
  EXPECT_EQ(kMideaACFanAuto, midea.getFan());
  EXPECT_EQ(0xA1826FFFFF62, midea.getRaw());

  midea.setFan(kMideaACFanMed);
  EXPECT_EQ(kMideaACFanMed, midea.getFan());
  EXPECT_EQ(0xA1926FFFFF7C, midea.getRaw());

  midea.setFan(kMideaACFanHigh);
  EXPECT_EQ(kMideaACFanHigh, midea.getFan());
  EXPECT_EQ(0xA19A6FFFFF74, midea.getRaw());

  midea.setFan(kMideaACFanAuto);
  EXPECT_EQ(kMideaACFanAuto, midea.getFan());
  EXPECT_EQ(0xA1826FFFFF62, midea.getRaw());
}

TEST(TestMideaACClass, Temperature) {
  IRMideaAC midea(0);
  midea.begin();

  midea.setRaw(0xA1826FFFFF62);         // 77F / 25C
  EXPECT_EQ(77, midea.getTemp());       // F
  EXPECT_EQ(77, midea.getTemp(false));  // F
  EXPECT_EQ(25, midea.getTemp(true));   // F

  midea.setTemp(0);
  EXPECT_EQ(kMideaACMinTempF, midea.getTemp());
  EXPECT_EQ(0xA18260FFFF6C, midea.getRaw());

  midea.setTemp(255);
  EXPECT_EQ(kMideaACMaxTempF, midea.getTemp());
  EXPECT_EQ(0xA18278FFFF78, midea.getRaw());

  midea.setTemp(0, true);
  EXPECT_EQ(kMideaACMinTempF, midea.getTemp());
  EXPECT_EQ(0xA18260FFFF6C, midea.getRaw());

  midea.setTemp(255, true);
  EXPECT_EQ(kMideaACMaxTempF, midea.getTemp());
  EXPECT_EQ(0xA18278FFFF78, midea.getRaw());

  // fahrenheit min/max etc.
  midea.setTemp(kMideaACMinTempF);
  EXPECT_EQ(kMideaACMinTempF, midea.getTemp());

  midea.setTemp(kMideaACMaxTempF);
  EXPECT_EQ(kMideaACMaxTempF, midea.getTemp());

  midea.setTemp(kMideaACMinTempF - 1);
  EXPECT_EQ(kMideaACMinTempF, midea.getTemp());

  midea.setTemp(kMideaACMaxTempF + 1);
  EXPECT_EQ(kMideaACMaxTempF, midea.getTemp());

  // celsius min/max etc.
  midea.setTemp(kMideaACMinTempC, true);
  EXPECT_EQ(kMideaACMinTempC, midea.getTemp(true));
  EXPECT_EQ(kMideaACMinTempF, midea.getTemp(false));

  midea.setTemp(kMideaACMaxTempC, true);
  EXPECT_EQ(kMideaACMaxTempC, midea.getTemp(true));
  EXPECT_EQ(kMideaACMaxTempF, midea.getTemp(false));

  midea.setTemp(kMideaACMinTempC - 1, true);
  EXPECT_EQ(kMideaACMinTempC, midea.getTemp(true));

  midea.setTemp(kMideaACMaxTempC + 1, true);
  EXPECT_EQ(kMideaACMaxTempC, midea.getTemp(true));
  EXPECT_EQ(kMideaACMaxTempF, midea.getTemp(false));

  // General changes.
  midea.setTemp(18, true);              // C
  EXPECT_EQ(18, midea.getTemp(true));   // C
  EXPECT_EQ(64, midea.getTemp(false));  // F

  midea.setTemp(21, true);              // C
  EXPECT_EQ(21, midea.getTemp(true));   // C
  EXPECT_EQ(69, midea.getTemp(false));  // F

  midea.setTemp(25, true);              // C
  EXPECT_EQ(25, midea.getTemp(true));   // C
  EXPECT_EQ(77, midea.getTemp(false));  // F

  midea.setTemp(30, true);              // C
  EXPECT_EQ(30, midea.getTemp(true));   // C
  EXPECT_EQ(86, midea.getTemp(false));  // F

  midea.setTemp(80, false);             // F
  EXPECT_EQ(27, midea.getTemp(true));   // C
  EXPECT_EQ(80, midea.getTemp(false));  // F

  midea.setTemp(70);                    // F
  EXPECT_EQ(21, midea.getTemp(true));   // C
  EXPECT_EQ(70, midea.getTemp(false));  // F
  EXPECT_EQ(70, midea.getTemp());       // F
}

// Tests for controlling the sleep state.
TEST(TestMideaACClass, Sleep) {
  IRMideaAC midea(0);
  midea.begin();

  midea.setRaw(0xA1826FFFFF62);  // Sleep off.

  EXPECT_FALSE(midea.getSleep());
  midea.setSleep(true);
  EXPECT_TRUE(midea.getSleep());
  EXPECT_EQ(0xA1C26FFFFF22, midea.getRaw());
  midea.setSleep(false);
  EXPECT_FALSE(midea.getSleep());
  EXPECT_EQ(0xA1826FFFFF62, midea.getRaw());
}

TEST(TestMideaACClass, HumanReadableOutput) {
  IRMideaAC ac(kGpioUnused);
  ac.begin();

  ac.setRaw(0xA1826FFFFF62);
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, "
      "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
  ac.off();
  ac.setTemp(25, true);
  ac.setFan(kMideaACFanHigh);
  ac.setMode(kMideaACDry);
  ac.setSleep(true);
  EXPECT_EQ(
      "Type: 1 (Command), Power: Off, Mode: 1 (Dry), Celsius: Off, "
      "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 3 (High), "
      "Sleep: On, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
  ac.setUseCelsius(true);
  EXPECT_EQ(
      "Type: 1 (Command), Power: Off, Mode: 1 (Dry), Celsius: On, "
      "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 3 (High), "
      "Sleep: On, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());

  ac.setRaw(0xA19867FFFF7E);
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 0 (Cool), Celsius: Off, "
      "Temp: 21C/69F, On Timer: Off, Off Timer: Off, Fan: 3 (High), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
}

// Tests for decodeMidea().

// Decode normal Midea messages with strict set.
TEST(TestDecodeMidea, NormalDecodeWithStrict) {
  IRsendTest irsend(4);
  IRrecv irrecv(4);
  irsend.begin();

  // Normal Midea 48-bit message.
  irsend.reset();
  irsend.sendMidea(0x1234567890DF);
  irsend.makeDecodeResult();
  ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, kStartOffset, kMideaBits,
                                 true));
  EXPECT_EQ(MIDEA, irsend.capture.decode_type);
  EXPECT_EQ(kMideaBits, irsend.capture.bits);
  EXPECT_EQ(0x1234567890DF, irsend.capture.value);
  EXPECT_EQ(0x0, irsend.capture.address);
  EXPECT_EQ(0x0, irsend.capture.command);
  EXPECT_FALSE(irsend.capture.repeat);

  // Normal Midea 48-bit message.
  irsend.reset();
  irsend.sendMidea(0x0);
  irsend.makeDecodeResult();
  ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, kStartOffset, kMideaBits,
                                 true));
  EXPECT_EQ(MIDEA, irsend.capture.decode_type);
  EXPECT_EQ(kMideaBits, irsend.capture.bits);
  EXPECT_EQ(0x0, irsend.capture.value);
  EXPECT_EQ(0x0, irsend.capture.address);
  EXPECT_EQ(0x0, irsend.capture.command);
  EXPECT_FALSE(irsend.capture.repeat);

  // Normal Midea 48-bit message.
  irsend.reset();
  irsend.sendMidea(0xFFFFFFFFFFA0);
  irsend.makeDecodeResult();
  ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, kStartOffset, kMideaBits,
                                 true));
  EXPECT_EQ(MIDEA, irsend.capture.decode_type);
  EXPECT_EQ(kMideaBits, irsend.capture.bits);
  EXPECT_EQ(0xFFFFFFFFFFA0, irsend.capture.value);
  EXPECT_EQ(0x0, irsend.capture.address);
  EXPECT_EQ(0x0, irsend.capture.command);
  EXPECT_FALSE(irsend.capture.repeat);

  // Real Midea 48-bit message via just decode().
  // i.e. No conficts with other decoders.
  irsend.reset();
  irsend.sendMidea(0xA18263FFFF6E);
  irsend.makeDecodeResult();
  ASSERT_TRUE(irrecv.decode(&irsend.capture));
  EXPECT_EQ(MIDEA, irsend.capture.decode_type);
  EXPECT_EQ(kMideaBits, irsend.capture.bits);
  EXPECT_EQ(0xA18263FFFF6E, irsend.capture.value);
  EXPECT_EQ(0x0, irsend.capture.address);
  EXPECT_EQ(0x0, irsend.capture.command);
  EXPECT_FALSE(irsend.capture.repeat);
}

// Decode normal repeated Midea messages.
TEST(TestDecodeMidea, NormalDecodeWithRepeatAndStrict) {
  IRsendTest irsend(4);
  IRrecv irrecv(4);
  irsend.begin();

  // Normal Midea 48-bit message with 2 repeats.
  irsend.reset();
  irsend.sendMidea(0xA18263FFFF6E, kMideaBits, 2);
  irsend.makeDecodeResult();
  ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, kStartOffset, kMideaBits,
                                 true));
  EXPECT_EQ(MIDEA, irsend.capture.decode_type);
  EXPECT_EQ(kMideaBits, irsend.capture.bits);
  EXPECT_EQ(0xA18263FFFF6E, irsend.capture.value);
  EXPECT_FALSE(irsend.capture.repeat);

  irsend.makeDecodeResult(2 * (2 * kMideaBits + 4));
  ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, kStartOffset, kMideaBits,
                                 true));
  EXPECT_EQ(MIDEA, irsend.capture.decode_type);
  EXPECT_EQ(kMideaBits, irsend.capture.bits);
  EXPECT_EQ(0xA18263FFFF6E, irsend.capture.value);

  irsend.makeDecodeResult(4 * (2 * kMideaBits + 4));
  ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, kStartOffset, kMideaBits,
                                 true));
  EXPECT_EQ(MIDEA, irsend.capture.decode_type);
  EXPECT_EQ(kMideaBits, irsend.capture.bits);
  EXPECT_EQ(0xA18263FFFF6E, irsend.capture.value);
}

// Decode unsupported Midea messages.
TEST(TestDecodeMidea, DecodeWithNonStrictSizes) {
  IRsendTest irsend(4);
  IRrecv irrecv(4);
  irsend.begin();

  irsend.reset();
  irsend.sendMidea(0x12, 8);  // Illegal value Midea 8-bit message.
  irsend.makeDecodeResult();
  // Should fail with strict on.
  ASSERT_FALSE(irrecv.decodeMidea(&irsend.capture, kStartOffset, kMideaBits,
                                  true));
  // Should pass if strict off.
  ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, kStartOffset, 8, false));
  EXPECT_EQ(MIDEA, irsend.capture.decode_type);
  EXPECT_EQ(8, irsend.capture.bits);
  EXPECT_EQ(0x12, irsend.capture.value);

  irsend.reset();
  irsend.sendMidea(0x12345678, 32);  // Illegal value Midea 32-bit message.
  irsend.makeDecodeResult();
  // Shouldn't pass with strict when we ask for less bits than we got.
  ASSERT_FALSE(irrecv.decodeMidea(&irsend.capture, kStartOffset, kMideaBits,
                                  true));

  irsend.makeDecodeResult();
  // Should fail with strict when we ask for the wrong bit size.
  ASSERT_FALSE(irrecv.decodeMidea(&irsend.capture, kStartOffset, 32, true));
  // Should pass if strict off.
  ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, kStartOffset, 32, false));
  EXPECT_EQ(MIDEA, irsend.capture.decode_type);
  EXPECT_EQ(32, irsend.capture.bits);
  EXPECT_EQ(0x12345678, irsend.capture.value);

  // Decode should fail if asked to decode non-multiples of 8 bits.
  irsend.reset();
  irsend.sendMidea(0x123456, kMideaBits, 2);
  irsend.makeDecodeResult();
  ASSERT_FALSE(irrecv.decodeMidea(&irsend.capture, kStartOffset, 9, false));
}

// Decode (non-standard) 64-bit messages.
TEST(TestDecodeMidea, Decode64BitMessages) {
  IRsendTest irsend(4);
  IRrecv irrecv(4);
  irsend.begin();

  irsend.reset();
  // Illegal size Midea 64-bit message.
  irsend.sendMidea(0xFFFFFFFFFFFFFFFF, 64);
  irsend.makeDecodeResult();
  // Should work with a 'normal' match (not strict)
  ASSERT_TRUE(irrecv.decodeMidea(&irsend.capture, kStartOffset, 64, false));
  EXPECT_EQ(MIDEA, irsend.capture.decode_type);
  EXPECT_EQ(64, irsend.capture.bits);
  EXPECT_EQ(0xFFFFFFFFFFFFFFFF, irsend.capture.value);
}

// Fail to decode a non-Midea example via GlobalCache
TEST(TestDecodeMidea, FailToDecodeNonMideaExample) {
  IRsendTest irsend(4);
  IRrecv irrecv(4);
  irsend.begin();

  irsend.reset();
  // Modified a few entries to unexpected values, based on previous test case.
  uint16_t gc_test[39] = {38000, 1,  1,  322, 162, 20, 61,  20, 61, 20,
                          20,    20, 20, 20,  20,  20, 127, 20, 61, 9,
                          20,    20, 61, 20,  20,  20, 61,  20, 61, 20,
                          61,    20, 20, 20,  20,  20, 20,  20, 884};
  irsend.sendGC(gc_test, 39);
  irsend.makeDecodeResult();

  ASSERT_FALSE(irrecv.decodeMidea(&irsend.capture));
  ASSERT_FALSE(irrecv.decodeMidea(&irsend.capture, kStartOffset, kMideaBits,
                                  false));
}

// Decode against a real capture reported by a user. See issue #354
TEST(TestDecodeMidea, DecodeRealExample) {
  IRsendTest irsend(4);
  IRrecv irrecv(4);
  irsend.begin();
  irsend.reset();

  uint16_t rawData[199] = {
      4366, 4470, 498, 1658, 522,  554,  498, 1658, 496, 580,  498, 580,
      498,  578,  498, 580,  498,  1658, 498, 1658, 498, 578,  498, 578,
      498,  580,  496, 582,  496,  578,  498, 1658, 498, 580,  498, 580,
      498,  1656, 498, 1656, 500,  580,  498, 578,  502, 576,  500, 1656,
      498,  1656, 500, 1654, 500,  1656, 500, 1656, 498, 1658, 498, 1656,
      500,  1658, 498, 1656, 498,  1656, 500, 1656, 500, 1654, 500, 1578,
      578,  1658, 498, 1656, 500,  1658, 498, 1656, 498, 1656, 500, 578,
      498,  1638, 516, 1656, 500,  578,  500, 1656, 500, 1656, 498, 1658,
      522,  554,  500, 5258, 4366, 4472, 498, 580,  498, 1658, 498, 580,
      498,  1656, 500, 1600, 556,  1658, 500, 1656, 500, 578,  498, 578,
      522,  1634, 498, 1588, 568,  1658, 498, 1656, 500, 1654, 498, 580,
      498,  1658, 498, 1658, 498,  580,  496, 578,  500, 1654, 500, 1636,
      518,  1656, 500, 578,  520,  558,  498, 578,  498, 580,  498, 576,
      500,  578,  498, 580,  498,  578,  498, 578,  498, 580,  498, 578,
      498,  580,  498, 580,  520,  556,  498, 580,  496, 580,  498, 578,
      500,  578,  498, 1658, 498,  580,  498, 578,  498, 1656, 500, 578,
      498,  580,  498, 580,  498,  1656, 522};
  irsend.sendRaw(rawData, 199, 38000);
  irsend.makeDecodeResult();

  ASSERT_TRUE(irrecv.decode(&irsend.capture));
  EXPECT_EQ(MIDEA, irsend.capture.decode_type);
  EXPECT_EQ(kMideaBits, irsend.capture.bits);
  EXPECT_EQ(0xA18263FFFF6E, irsend.capture.value);
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, "
      "Temp: 18C/65F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -",
      IRAcUtils::resultAcToString(&irsend.capture));
  stdAc::state_t r, p;
  ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
}

TEST(TestMideaACClass, toCommon) {
  IRMideaAC ac(kGpioUnused);
  ac.setPower(true);
  ac.setMode(kMideaACCool);
  ac.setUseCelsius(true);
  ac.setTemp(20, true);
  ac.setFan(kMideaACFanHigh);
  // Now test it.
  ASSERT_EQ(decode_type_t::MIDEA, ac.toCommon().protocol);
  ASSERT_EQ(-1, ac.toCommon().model);
  ASSERT_TRUE(ac.toCommon().power);
  ASSERT_TRUE(ac.toCommon().celsius);
  ASSERT_EQ(20, ac.toCommon().degrees);
  ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode);
  ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed);
  ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv);
  // Unsupported.
  ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh);
  ASSERT_FALSE(ac.toCommon().turbo);
  ASSERT_FALSE(ac.toCommon().clean);
  ASSERT_FALSE(ac.toCommon().light);
  ASSERT_FALSE(ac.toCommon().quiet);
  ASSERT_FALSE(ac.toCommon().econo);
  ASSERT_FALSE(ac.toCommon().filter);
  ASSERT_FALSE(ac.toCommon().beep);
  ASSERT_EQ(-1, ac.toCommon().sleep);
  ASSERT_EQ(-1, ac.toCommon().clock);
}

// https://github.com/crankyoldgit/IRremoteESP8266/issues/819
TEST(TestMideaACClass, CelsiusRemoteTemp) {
  IRMideaAC ac(kGpioUnused);
  uint64_t on_cool_low_17c = 0xA18840FFFF56;
  uint64_t on_cool_low_30c = 0xA1884DFFFF5D;
  ac.on();
  ac.setMode(kMideaACCool);
  ac.setFan(kMideaACFanLow);
  ac.setTemp(17, true);
  EXPECT_FALSE(ac.getUseCelsius());
  ac.setUseCelsius(true);
  EXPECT_TRUE(ac.getUseCelsius());
  EXPECT_EQ(on_cool_low_17c, ac.getRaw());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 0 (Cool), Celsius: On, "
      "Temp: 17C/62F, On Timer: Off, Off Timer: Off, Fan: 1 (Low), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
  ac.setRaw(on_cool_low_17c);
  EXPECT_EQ(17, ac.getTemp(true));
  EXPECT_EQ(62, ac.getTemp(false));
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 0 (Cool), Celsius: On, "
      "Temp: 17C/62F, On Timer: Off, Off Timer: Off, Fan: 1 (Low), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
  ac.setTemp(17, true);
  EXPECT_EQ(17, ac.getTemp(true));
  EXPECT_EQ(62, ac.getTemp(false));
  EXPECT_EQ(on_cool_low_17c, ac.getRaw());

  ac.setRaw(on_cool_low_30c);
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 0 (Cool), Celsius: On, "
      "Temp: 30C/86F, On Timer: Off, Off Timer: Off, Fan: 1 (Low), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
}

// https://github.com/crankyoldgit/IRremoteESP8266/issues/819
TEST(TestMideaACClass, SwingV) {
  IRMideaAC ac(kGpioUnused);
  ac.setSwingVToggle(false);
  ASSERT_FALSE(ac.getSwingVToggle());
  ac.setSwingVToggle(true);
  ASSERT_TRUE(ac.getSwingVToggle());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, "
      "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): Toggle, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
  ac.setSwingVToggle(false);
  ASSERT_FALSE(ac.getSwingVToggle());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, "
      "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
  ac.setRaw(kMideaACToggleSwingV);
  EXPECT_EQ("Type: 2 (Special), Swing(V): Toggle, Econo: -, "
            "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -",
            ac.toString());
}

// https://github.com/crankyoldgit/IRremoteESP8266/pull/1213
TEST(TestMideaACClass, Econo) {
  IRMideaAC ac(kGpioUnused);
  ac.setEconoToggle(false);
  ASSERT_FALSE(ac.getEconoToggle());
  ac.setEconoToggle(true);
  ASSERT_TRUE(ac.getEconoToggle());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, "
      "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: Toggle, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
  ac.setEconoToggle(false);
  ASSERT_FALSE(ac.getEconoToggle());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, "
      "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
  ac.setRaw(kMideaACToggleEcono);
  EXPECT_EQ("Type: 2 (Special), Swing(V): -, Econo: Toggle, "
            "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -",
            ac.toString());
}

TEST(TestMideaACClass, Turbo) {
  IRMideaAC ac(kGpioUnused);
  ac.setTurboToggle(false);
  ASSERT_FALSE(ac.getTurboToggle());
  ac.setTurboToggle(true);
  ASSERT_TRUE(ac.getTurboToggle());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, "
      "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: Toggle, Quiet: Off, Light: -, Clean: -, 8C Heat: -",
      ac.toString());
  ac.setTurboToggle(false);
  ASSERT_FALSE(ac.getTurboToggle());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, "
      "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -",
      ac.toString());
  ac.setRaw(kMideaACToggleTurbo);
  EXPECT_EQ("Type: 2 (Special), Swing(V): -, Econo: -, "
            "Turbo: Toggle, Quiet: Off, Light: -, Clean: -, 8C Heat: -",
            ac.toString());
}

TEST(TestMideaACClass, Light) {
  IRMideaAC ac(kGpioUnused);
  ac.setLightToggle(false);
  ASSERT_FALSE(ac.getLightToggle());
  ac.setLightToggle(true);
  ASSERT_TRUE(ac.getLightToggle());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, "
      "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: Toggle, Clean: -, 8C Heat: -",
      ac.toString());
  ac.setLightToggle(false);
  ASSERT_FALSE(ac.getLightToggle());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, "
      "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -",
      ac.toString());
  ac.setRaw(kMideaACToggleLight);
  EXPECT_EQ("Type: 2 (Special), Swing(V): -, Econo: -, "
            "Turbo: -, Quiet: Off, Light: Toggle, Clean: -, 8C Heat: -",
            ac.toString());
}

TEST(TestMideaACClass, Clean) {
  IRMideaAC ac(kGpioUnused);
  ac.setCleanToggle(false);
  ASSERT_FALSE(ac.getCleanToggle());
  ac.setCleanToggle(true);
  ASSERT_TRUE(ac.getCleanToggle());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, "
      "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: Toggle, 8C Heat: -",
      ac.toString());
  ac.setCleanToggle(false);
  ASSERT_FALSE(ac.getCleanToggle());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, "
      "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -",
      ac.toString());
  ac.setRaw(kMideaACToggleSelfClean);
  EXPECT_EQ("Type: 2 (Special), Swing(V): -, Econo: -, "
            "Turbo: -, Quiet: Off, Light: -, Clean: Toggle, 8C Heat: -",
            ac.toString());
}

TEST(TestMideaACClass, FreezeProtectionAKA8CHeat) {
  IRMideaAC ac(kGpioUnused);
  ac.set8CHeatToggle(false);
  ASSERT_FALSE(ac.get8CHeatToggle());
  ASSERT_NE(kMideaACHeat, ac.getMode());
  ac.set8CHeatToggle(true);  // Not in heat mode so it should not work.
  ASSERT_FALSE(ac.get8CHeatToggle());
  ac.setMode(kMideaACHeat);  // Now it should work
  ac.set8CHeatToggle(true);
  ASSERT_TRUE(ac.get8CHeatToggle());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: Off, "
      "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: Toggle",
      ac.toString());
  ac.set8CHeatToggle(false);
  ASSERT_FALSE(ac.get8CHeatToggle());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: Off, "
      "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -",
      ac.toString());
  ac.setRaw(kMideaACToggle8CHeat);
  EXPECT_EQ("Type: 2 (Special), Swing(V): -, Econo: -, "
            "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: Toggle",
            ac.toString());
}

TEST(TestMideaACClass, Quiet) {
  IRMideaAC ac(kGpioUnused);
  ac.setQuiet(false);
  ASSERT_FALSE(ac.getQuiet());
  ac.setQuiet(true);
  ASSERT_TRUE(ac.getQuiet());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, "
      "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: On, Light: -, Clean: -, 8C Heat: -",
      ac.toString());
  ac.setQuiet(false);
  ASSERT_FALSE(ac.getQuiet());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, "
      "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
  ac.setRaw(kMideaACQuietOn);
  EXPECT_EQ("Type: 2 (Special), Swing(V): -, Econo: -, "
            "Turbo: -, Quiet: On, Light: -, Clean: -, 8C Heat: -",
            ac.toString());
  ac.setRaw(kMideaACQuietOff);
  EXPECT_EQ("Type: 2 (Special), Swing(V): -, Econo: -, "
            "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -",
            ac.toString());
}

// Test abusing the protocol for sending 6 arbitrary bytes.
// See https://github.com/crankyoldgit/IRremoteESP8266/issues/887
TEST(TestDecodeMidea, Issue887) {
  IRsendTest irsend(0);
  IRrecv irrecv(0);
  irsend.begin();
  irsend.reset();

  uint64_t hwaddr = 0x1234567890AB;  // 48bits doesn't conform to Midea checksum

  irsend.sendMidea(hwaddr);
  irsend.makeDecodeResult();

  // Test normal operation, it shouldn't match.
  EXPECT_TRUE(irrecv.decode(&irsend.capture));
  EXPECT_NE(MIDEA, irsend.capture.decode_type);
  irsend.reset();
  irsend.sendMidea(hwaddr);
  irsend.makeDecodeResult();
  EXPECT_FALSE(irrecv.decodeMidea(&irsend.capture));

  // Now test it with Midea's strict processing turned off!
  irsend.reset();
  irsend.sendMidea(hwaddr);
  irsend.makeDecodeResult();
  EXPECT_TRUE(irrecv.decodeMidea(&irsend.capture, kStartOffset, kMideaBits,
                                 false));
  EXPECT_EQ(MIDEA, irsend.capture.decode_type);
  EXPECT_EQ(kMideaBits, irsend.capture.bits);
  EXPECT_EQ(hwaddr, irsend.capture.value);
}

// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1170
TEST(TestDecodeMidea24, RealExample) {
  IRsendTest irsend(kGpioUnused);
  IRrecv irrecv(kGpioUnused);
  irsend.begin();
  irsend.reset();

  const uint16_t rawData[103] = {
      8928, 4412, 590, 1630, 588, 538, 538, 544, 610, 516, 592, 516, 590, 538,
      568, 536, 538, 568, 592, 518, 588, 1630, 588, 1630, 560, 1680, 592, 1628,
      594, 1628, 592, 1650, 568, 1630, 558, 1680, 594, 1652, 568, 514, 592, 536,
      538, 568, 594, 516, 588, 538, 566, 518, 560, 566, 588, 514, 594, 1630,
      588, 1630, 590, 1630, 560, 1680, 594, 1630, 588, 1652, 568, 1650, 540,
      1680, 590, 512, 594, 518, 586, 538, 566, 538, 536, 568, 592, 540, 564,
      540, 566, 540, 538, 1680, 590, 1626, 592, 1630, 588, 1628, 538, 1702, 590,
      1630, 590, 13318, 8916, 2166, 638};  // UNKNOWN 774B249A

  irsend.sendRaw(rawData, 103, 38);
  irsend.makeDecodeResult();
  ASSERT_TRUE(irrecv.decode(&irsend.capture));
  EXPECT_EQ(MIDEA24, irsend.capture.decode_type);
  EXPECT_EQ(kMidea24Bits, irsend.capture.bits);
  EXPECT_EQ(0x80C0C0, irsend.capture.value);
  EXPECT_EQ(0, irsend.capture.address);
  EXPECT_EQ(0, irsend.capture.command);
}

TEST(TestDecodeMidea24, SyntheticExample) {
  IRsendTest irsend(kGpioUnused);
  IRrecv irrecv(kGpioUnused);
  irsend.begin();
  irsend.reset();

  irsend.sendMidea24(0x80C0C0);
  irsend.makeDecodeResult();
  ASSERT_TRUE(irrecv.decode(&irsend.capture));
  EXPECT_EQ(MIDEA24, irsend.capture.decode_type);
  EXPECT_EQ(kMidea24Bits, irsend.capture.bits);
  EXPECT_EQ(0x80C0C0, irsend.capture.value);
  EXPECT_EQ(0, irsend.capture.address);
  EXPECT_EQ(0, irsend.capture.command);
  EXPECT_EQ(
      "f38000d33"
      "m8960s4480"
      "m560s1680m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
      "m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680"
      "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560m560s560"
      "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680"
      "m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560m560s560"
      "m560s560m560s560m560s1680m560s1680m560s1680m560s1680m560s1680m560s1680"
      "m560s22400"
      "m8960s2240m560s96320",
      irsend.outputStr());
}

TEST(TestUtils, Housekeeping) {
  ASSERT_EQ("MIDEA", typeToString(decode_type_t::MIDEA));
  ASSERT_EQ(decode_type_t::MIDEA, strToDecodeType("MIDEA"));
  ASSERT_FALSE(hasACState(decode_type_t::MIDEA));
  ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::MIDEA));
  ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::MIDEA));
  ASSERT_EQ(kMideaBits, IRsend::defaultBits(decode_type_t::MIDEA));

  ASSERT_EQ("MIDEA24", typeToString(decode_type_t::MIDEA24));
  ASSERT_EQ(decode_type_t::MIDEA24, strToDecodeType("MIDEA24"));
  ASSERT_FALSE(hasACState(decode_type_t::MIDEA24));
  ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::MIDEA24));
  ASSERT_EQ(kSingleRepeat, IRsend::minRepeats(decode_type_t::MIDEA24));
  ASSERT_EQ(kMidea24Bits, IRsend::defaultBits(decode_type_t::MIDEA24));
}

TEST(TestDecodeMidea24, RealExample2) {
  IRsendTest irsend(kGpioUnused);
  IRrecv irrecv(kGpioUnused);
  irsend.begin();
  irsend.reset();

  // https://github.com/crankyoldgit/IRremoteESP8266/issues/1170#issuecomment-639271003
  const uint16_t rawData[103] = {
      8926, 4412, 590, 1630, 536, 570, 608, 518, 594, 516, 588, 518, 588, 538,
      538, 568, 592, 514, 590, 518, 588, 1630, 536, 1684, 610, 1628, 594, 1630,
      588, 1630, 590, 1630, 560, 1680, 592, 1630, 588, 1650, 568, 538, 538, 568,
      590, 514, 594, 538, 566, 518, 536, 594, 584, 516, 594, 518, 586, 1630,
      588, 1650, 538, 1682, 592, 1630, 588, 1628, 588, 1630, 560, 1680, 590,
      1626, 594, 538, 566, 538, 568, 536, 538, 570, 590, 538, 566, 538, 568,
      538, 538, 568, 590, 1626, 594, 1650, 568, 1628, 558, 1662, 558, 1680, 592,
      1650, 568, 13312, 8924, 2186, 588};  // UNKNOWN 774B249A

  irsend.sendRaw(rawData, 103, 38);
  irsend.makeDecodeResult();
  ASSERT_TRUE(irrecv.decode(&irsend.capture));
  EXPECT_EQ(MIDEA24, irsend.capture.decode_type);
  EXPECT_EQ(kMidea24Bits, irsend.capture.bits);
  EXPECT_EQ(0x80C0C0, irsend.capture.value);
  EXPECT_EQ(0, irsend.capture.address);
  EXPECT_EQ(0, irsend.capture.command);

  // ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1170#issuecomment-639349316
  const uint16_t rawData639349316[105] = {
      8924, 4410, 596, 1628, 590, 538, 538, 568, 588, 520, 592, 516, 588, 518,
      588, 516, 558, 568, 594, 538, 566, 1652, 568, 1650, 540, 1680, 590, 1628,
      592, 1628, 590, 1628, 590, 1650, 540, 1680, 592, 1630, 590, 518, 586, 538,
      538, 568, 590, 514, 616, 494, 588, 516, 588, 516, 560, 566, 590, 1630,
      586, 1634, 564, 1652, 538, 1704, 566, 948, 254, 450, 564, 1654, 566,
      1652, 540, 1682, 610, 518, 588, 516, 590, 514, 590, 514, 582, 526, 614,
      436, 670, 512, 592, 514, 538, 1578, 718, 1602, 614, 1542, 676, 1626, 538,
      1678, 586, 1634, 616, 13308, 8920, 2158, 626};  // UNKNOWN CBEC0079
  irsend.reset();
  irsend.sendRaw(rawData639349316, 105, 38);
  irsend.makeDecodeResult();
  ASSERT_TRUE(irrecv.decode(&irsend.capture));
  EXPECT_NE(MIDEA24, irsend.capture.decode_type);

  // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1170#issuecomment-639358566
  const uint16_t rawData639358566[103] = {
      8920, 4412, 620, 1606, 614, 512, 592, 512, 536, 568, 618, 512, 590, 492,
      614, 514, 514, 590, 594, 510, 620, 1624, 592, 1604, 590, 1632, 608, 1626,
      606, 1616, 618, 1602, 618, 1602, 560, 1682, 622, 1602, 618, 512, 592, 510,
      538, 568, 590, 512, 622, 488, 616, 490, 614, 488, 560, 568, 620, 1624,
      594, 1604, 616, 1602, 536, 1684, 640, 1600, 618, 1598, 618, 1602, 590,
      1630, 612, 512, 622, 510, 594, 514, 592, 512, 534, 568, 620, 512, 594,
      488, 616, 488, 536, 1702, 594, 1622, 622, 1604, 614, 1624, 594, 1602, 610,
      1630, 620, 13294, 8914, 2184, 596};  // UNKNOWN 774B249A
  irsend.reset();
  irsend.sendRaw(rawData639358566, 103, 38);
  irsend.makeDecodeResult();
  ASSERT_TRUE(irrecv.decode(&irsend.capture));
  EXPECT_EQ(MIDEA24, irsend.capture.decode_type);
  EXPECT_EQ(kMidea24Bits, irsend.capture.bits);
  EXPECT_EQ(0x80C0C0, irsend.capture.value);
  EXPECT_EQ(0, irsend.capture.address);
  EXPECT_EQ(0, irsend.capture.command);
}

// See https://github.com/crankyoldgit/IRremoteESP8266/issues/1170#issuecomment-639468620
// for why this test exists.
TEST(TestDecodeMidea24, LargeTimeout) {
  IRsendTest irsend(kGpioUnused);
  IRrecv irrecv(kGpioUnused, 1000, 90);  // Test with a large timeout value
  irsend.begin();
  irsend.reset();
  // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1170#issuecomment-639358566
  const uint16_t rawData639358566[103] = {
      8920, 4412, 620, 1606, 614, 512, 592, 512, 536, 568, 618, 512, 590, 492,
      614, 514, 514, 590, 594, 510, 620, 1624, 592, 1604, 590, 1632, 608, 1626,
      606, 1616, 618, 1602, 618, 1602, 560, 1682, 622, 1602, 618, 512, 592, 510,
      538, 568, 590, 512, 622, 488, 616, 490, 614, 488, 560, 568, 620, 1624,
      594, 1604, 616, 1602, 536, 1684, 640, 1600, 618, 1598, 618, 1602, 590,
      1630, 612, 512, 622, 510, 594, 514, 592, 512, 534, 568, 620, 512, 594,
      488, 616, 488, 536, 1702, 594, 1622, 622, 1604, 614, 1624, 594, 1602, 610,
      1630, 620, 13294, 8914, 2184, 596};  // UNKNOWN 774B249A
  irsend.reset();
  irsend.sendRaw(rawData639358566, 103, 38);
  irsend.makeDecodeResult();
  ASSERT_TRUE(irrecv.decode(&irsend.capture));
  EXPECT_EQ(MIDEA24, irsend.capture.decode_type);
  EXPECT_EQ(kMidea24Bits, irsend.capture.bits);
  EXPECT_EQ(0x80C0C0, irsend.capture.value);
  EXPECT_EQ(0, irsend.capture.address);
  EXPECT_EQ(0, irsend.capture.command);
}

// Verify the library can self-decode `sendMidea(0xA4A3477F1979)` to the correct
// value.
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1318#issuecomment-721829757
TEST(TestDecodeMidea, Issue1318_self_decode) {
  IRsendTest irsend(kGpioUnused);
  IRrecv irrecv(kGpioUnused);
  irsend.begin();
  irsend.reset();
  irsend.sendMidea(0xA4A3477F1979);
  irsend.makeDecodeResult();
  ASSERT_TRUE(irrecv.decode(&irsend.capture));
  EXPECT_EQ(MIDEA, irsend.capture.decode_type);
  EXPECT_EQ(kMideaBits, irsend.capture.bits);
  EXPECT_EQ(0xA4A3477F1979, irsend.capture.value);
  EXPECT_EQ(0, irsend.capture.address);
  EXPECT_EQ(0, irsend.capture.command);
  EXPECT_EQ(
      "Type: 4 (Follow), Power: On, Mode: 3 (Heat), Celsius: On, "
      "Temp: 24C/75F, SensorTemp: 24C/75F, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -",
      IRAcUtils::resultAcToString(&irsend.capture));
  EXPECT_EQ(
      "f38000d50"
      "m4480s4480m560s1680m560s560m560s1680m560s560m560s560m560s1680"
      "m560s560m560s560m560s1680m560s560m560s1680m560s560m560s560"
      "m560s560m560s1680m560s1680m560s560m560s1680m560s560m560s560"
      "m560s560m560s1680m560s1680m560s1680m560s560m560s1680m560s1680"
      "m560s1680m560s1680m560s1680m560s1680m560s1680m560s560m560s560"
      "m560s560m560s1680m560s1680m560s560m560s560m560s1680m560s560"
      "m560s1680m560s1680m560s1680m560s1680m560s560m560s560m560s1680"
      "m560s5600"
      "m4480s4480m560s560m560s1680m560s560m560s1680m560s1680m560s560"
      "m560s1680m560s1680m560s560m560s1680m560s560m560s1680m560s1680"
      "m560s1680m560s560m560s560m560s1680m560s560m560s1680m560s1680"
      "m560s1680m560s560m560s560m560s560m560s1680m560s560m560s560"
      "m560s560m560s560m560s560m560s560m560s560m560s1680m560s1680"
      "m560s1680m560s560m560s560m560s1680m560s1680m560s560m560s1680"
      "m560s560m560s560m560s560m560s560m560s1680m560s1680m560s560"
      "m560s105600",
      irsend.outputStr());
}

// https://github.com/crankyoldgit/IRremoteESP8266/issues/1318#issuecomment-722655214
TEST(TestMideaACClass, SensorTemp) {
  IRMideaAC ac(kGpioUnused);
  ASSERT_FALSE(ac.getEnableSensorTemp());  // Off by default.
  ASSERT_NE(kMideaACTypeFollow, ac.getType());
  ac.setSensorTemp(21, true);
  EXPECT_EQ(21, ac.getSensorTemp(true));
  EXPECT_TRUE(ac.getEnableSensorTemp());  // Should be enabled when set.
  EXPECT_EQ(kMideaACTypeFollow, ac.getType());
  ac.setEnableSensorTemp(true);  // Values shouldn't change when already on.
  EXPECT_EQ(21, ac.getSensorTemp(true));
  EXPECT_TRUE(ac.getEnableSensorTemp());  // Should be abled when set.
  ac.setEnableSensorTemp(false);  // Old value should be cleared when disabled.
  EXPECT_NE(21, ac.getSensorTemp(true));
  EXPECT_FALSE(ac.getEnableSensorTemp());
  ASSERT_NE(kMideaACTypeFollow, ac.getType());
  EXPECT_EQ(kMideaACTypeCommand, ac.getType());

  // Fahrenheit
  ac.setSensorTemp(74, false);
  EXPECT_EQ(74, ac.getSensorTemp(false));
  EXPECT_TRUE(ac.getEnableSensorTemp());  // Should be abled when set.

  // Bounds checks
  // Celsius
  ac.setSensorTemp(kMideaACMinSensorTempC, true);
  EXPECT_EQ(kMideaACMinSensorTempC, ac.getSensorTemp(true));
  EXPECT_TRUE(ac.getEnableSensorTemp());  // Should be enabled when set.
  ac.setSensorTemp(kMideaACMaxSensorTempC, true);
  EXPECT_EQ(kMideaACMaxSensorTempC, ac.getSensorTemp(true));
  EXPECT_TRUE(ac.getEnableSensorTemp());  // Should be enabled when set.
  ac.setSensorTemp(kMideaACMaxSensorTempC + 1, true);
  EXPECT_EQ(kMideaACMaxSensorTempC, ac.getSensorTemp(true));
  EXPECT_TRUE(ac.getEnableSensorTemp());  // Should be enabled when set.
  // Fahrenheit
  ac.setSensorTemp(kMideaACMinSensorTempF, false);
  EXPECT_EQ(kMideaACMinSensorTempF, ac.getSensorTemp(false));
  EXPECT_TRUE(ac.getEnableSensorTemp());  // Should be enabled when set.
  ac.setSensorTemp(kMideaACMinSensorTempF - 1, false);
  EXPECT_EQ(kMideaACMinSensorTempF, ac.getSensorTemp(false));
  EXPECT_TRUE(ac.getEnableSensorTemp());  // Should be enabled when set.
  ac.setSensorTemp(kMideaACMaxSensorTempF, false);
  EXPECT_EQ(kMideaACMaxSensorTempF, ac.getSensorTemp(false));
  EXPECT_TRUE(ac.getEnableSensorTemp());  // Should be enabled when set.
  ac.setSensorTemp(kMideaACMaxSensorTempF + 1, false);
  EXPECT_EQ(kMideaACMaxSensorTempF, ac.getSensorTemp(false));
  EXPECT_TRUE(ac.getEnableSensorTemp());  // Should be enabled when set.

  // Real examples
  ac.setRaw(0xA482467F015D);  // 0C
  EXPECT_EQ(0, ac.getSensorTemp(true));
  EXPECT_TRUE(ac.getEnableSensorTemp());
  EXPECT_EQ(
      "Type: 4 (Follow), Power: On, Mode: 2 (Auto), Celsius: On, "
      "Temp: 23C/73F, SensorTemp: 0C/32F, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
  ac.setRaw(0xA482467F134E);  // 18C
  EXPECT_EQ(18, ac.getSensorTemp(true));
  EXPECT_TRUE(ac.getEnableSensorTemp());
  EXPECT_EQ(
      "Type: 4 (Follow), Power: On, Mode: 2 (Auto), Celsius: On, "
      "Temp: 23C/73F, SensorTemp: 18C/64F, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
  ac.setRaw(0xA482467F266B);  // 37C
  EXPECT_EQ(37, ac.getSensorTemp(true));
  EXPECT_TRUE(ac.getEnableSensorTemp());
  EXPECT_EQ(
      "Type: 4 (Follow), Power: On, Mode: 2 (Auto), Celsius: On, "
      "Temp: 23C/73F, SensorTemp: 37C/98F, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
  ac.setRaw(0xA482607F2B43);  // 74F
  EXPECT_EQ(74, ac.getSensorTemp(false));
  EXPECT_TRUE(ac.getEnableSensorTemp());
  EXPECT_EQ(
      "Type: 4 (Follow), Power: On, Mode: 2 (Auto), Celsius: Off, "
      "Temp: 17C/62F, SensorTemp: 23C/74F, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
}

TEST(TestMideaACClass, MessageType) {
  IRMideaAC ac(kGpioUnused);
  EXPECT_EQ(kMideaACTypeCommand, ac.getType());
  ac.setType(kMideaACTypeFollow);
  EXPECT_EQ(kMideaACTypeFollow, ac.getType());
  ac.setType(kMideaACTypeCommand);
  EXPECT_EQ(kMideaACTypeCommand, ac.getType());
  ac.setType(kMideaACTypeSpecial);
  EXPECT_EQ(kMideaACTypeSpecial, ac.getType());
  ac.setType(0);
  EXPECT_EQ(kMideaACTypeCommand, ac.getType());
  ac.setType(255);
  EXPECT_EQ(kMideaACTypeCommand, ac.getType());
}

TEST(TestMideaACClass, ConstructKnownMessage) {
  IRMideaAC ac(kGpioUnused);
  const uint64_t followme_25C = 0xA482467F1A47;
  ac.stateReset();
  ac.on();
  ac.setMode(kMideaACAuto);
  ac.setUseCelsius(true);
  ac.setTemp(23, true);
  ac.setSensorTemp(25, true);
  ac.setFan(kMideaACFanAuto);
  EXPECT_EQ(
      "Type: 4 (Follow), Power: On, Mode: 2 (Auto), Celsius: On, "
      "Temp: 23C/73F, SensorTemp: 25C/77F, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
  EXPECT_EQ(followme_25C, ac.getRaw());
}

TEST(TestMideaACClass, OnTimer) {
  IRMideaAC ac(kGpioUnused);
  EXPECT_FALSE(ac.isOnTimerEnabled());
  // Data from: https://github.com/crankyoldgit/IRremoteESP8266/issues/1318#issuecomment-723171666
  ac.setRaw(0xA1A34DFF9B34);  // 7 hours.
  EXPECT_TRUE(ac.isOnTimerEnabled());
  EXPECT_EQ(7 * 60, ac.getOnTimer());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: On, "
      "Temp: 30C/86F, On Timer: 07:00, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
  ac.setOnTimer(0);  // Disable / Timer off.
  EXPECT_FALSE(ac.isOnTimerEnabled());
  EXPECT_EQ(kMideaACTypeCommand, ac.getType());
  ac.setOnTimer(3 * 60 + 48);  // 3h48m
  EXPECT_TRUE(ac.isOnTimerEnabled());
  EXPECT_EQ(kMideaACTypeCommand, ac.getType());
  EXPECT_EQ(3 * 60 + 30, ac.getOnTimer());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: On, "
      "Temp: 30C/86F, On Timer: 03:30, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
  // Limit to max time.
  ac.setOnTimer(25 * 60);  // 25h
  EXPECT_TRUE(ac.isOnTimerEnabled());
  EXPECT_EQ(kMideaACTypeCommand, ac.getType());
  EXPECT_EQ(24 * 60, ac.getOnTimer());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: On, "
      "Temp: 30C/86F, On Timer: 24:00, Off Timer: Off, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
}


TEST(TestMideaACClass, OffTimer) {
  IRMideaAC ac(kGpioUnused);
  EXPECT_FALSE(ac.isOffTimerEnabled());
  // Data from: https://github.com/crankyoldgit/IRremoteESP8266/issues/1318#issuecomment-723171666
  ac.setRaw(0xA1A34D81FF21);  // 0.5 hours.
  EXPECT_TRUE(ac.isOffTimerEnabled());
  EXPECT_EQ(30, ac.getOffTimer());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: On, "
      "Temp: 30C/86F, On Timer: Off, Off Timer: 00:30, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
  ac.setOffTimer(0);  // Disable / Timer off.
  EXPECT_FALSE(ac.isOffTimerEnabled());
  EXPECT_EQ(kMideaACTypeCommand, ac.getType());
  ac.setOffTimer(3 * 60 + 48);  // 3h48m
  EXPECT_TRUE(ac.isOffTimerEnabled());
  EXPECT_EQ(kMideaACTypeCommand, ac.getType());
  EXPECT_EQ(3 * 60 + 30, ac.getOffTimer());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: On, "
      "Temp: 30C/86F, On Timer: Off, Off Timer: 03:30, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
  // Limit to max time.
  ac.setOffTimer(25 * 60);  // 25h
  EXPECT_TRUE(ac.isOffTimerEnabled());
  EXPECT_EQ(kMideaACTypeCommand, ac.getType());
  EXPECT_EQ(24 * 60, ac.getOffTimer());
  EXPECT_EQ(
      "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: On, "
      "Temp: 30C/86F, On Timer: Off, Off Timer: 24:00, Fan: 0 (Auto), "
      "Sleep: Off, Swing(V): -, Econo: -, "
      "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString());
}

// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1318#issuecomment-725406813
TEST(TestMideaACClass, SendingSwingV) {
  IRMideaAC ac(kGpioUnused);
  IRrecv irrecv(kGpioUnused);
  ac.begin();
  ac.setSwingVToggle(true);
  ac.send();  // This should generate two messages back to back.
  ac._irsend.makeDecodeResult();
  EXPECT_TRUE(irrecv.decode(&ac._irsend.capture));
  EXPECT_EQ(MIDEA, ac._irsend.capture.decode_type);
  EXPECT_EQ(kMideaBits, ac._irsend.capture.bits);
  EXPECT_EQ(
    "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, "
    "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), "
    "Sleep: Off, Swing(V): -, Econo: -, "
    "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -",
    IRAcUtils::resultAcToString(&ac._irsend.capture));
  // Decode the second/following message which should be the swing toggle.
  EXPECT_TRUE(irrecv.decodeMidea(&ac._irsend.capture, 201));
  EXPECT_EQ(MIDEA, ac._irsend.capture.decode_type);
  EXPECT_EQ(kMideaBits, ac._irsend.capture.bits);
  EXPECT_EQ(
    "Type: 2 (Special), Swing(V): Toggle, Econo: -, "
    "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -",
    IRAcUtils::resultAcToString(&ac._irsend.capture));
}

// Reconstruct a Midea known raw code for an odd Pioneer System A/C.
TEST(TestMideaACClass, Issue1342) {
  IRMideaAC ac(kGpioUnused);

  // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1342#issuecomment-733721085
  ASSERT_TRUE((1ULL<<37) & 0xA1A368FFFF45);  // Confirm the special bit is set.

  ac.setRaw(0xA1A368FFFF45);
  EXPECT_EQ(
    "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: Off, "
    "Temp: 21C/70F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), Sleep: Off, "
    "Swing(V): -, Econo: -, Turbo: -, Quiet: Off, "
    "Light: -, Clean: -, 8C Heat: -",
    ac.toString());
  ac.stateReset();  // Remove any existing info so we can build from scratch.
  ASSERT_NE(0xA1A368FFFF45, ac.getRaw());
  // Some Pioneer Systems have required a special bit to be set in order
  // for the A/C unit to accept the message. We don't currently understand what
  // this bit does. See the link for details of how to set this if needed.
  // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1342#issuecomment-733721085
  ac.setRaw((1ULL<<37) | ac.getRaw());
  ASSERT_TRUE((1ULL<<37) & ac.getRaw());  // Confirm the special bit is set.

  ac.setPower(true);
  ac.setMode(kMideaACHeat);
  ac.setTemp(70);
  ac.setFan(kMideaACFanAuto);
  EXPECT_EQ(
    "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: Off, "
    "Temp: 21C/70F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), Sleep: Off, "
    "Swing(V): -, Econo: -, Turbo: -, Quiet: Off, "
    "Light: -, Clean: -, 8C Heat: -",
    ac.toString());
  EXPECT_EQ(0xA1A368FFFF45, ac.getRaw());
}
