// Copyright 2017 David Conran

#include "ir_Gree.h"
#include "IRac.h"
#include "IRrecv.h"
#include "IRrecv_test.h"
#include "IRremoteESP8266.h"
#include "IRsend.h"
#include "IRsend_test.h"
#include "gtest/gtest.h"

// Tests for sendGree().

// Test sending typical data only.
TEST(TestSendGreeChars, SendData) {
  IRsendTest irsend(kGpioUnused);
  irsend.begin();

  uint8_t gree_code[kGreeStateLength] = {0x12, 0x34, 0x56, 0x78,
                                         0x90, 0xAB, 0xCD, 0xEF};
  irsend.reset();
  irsend.sendGree(gree_code);
  EXPECT_EQ(
      "f38000d50"
      "m9000s4500"
      "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540"
      "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540"
      "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540"
      "m620s540m620s540m620s540m620s1600m620s1600m620s1600m620s1600m620s540"
      "m620s540m620s1600m620s540"
      "m620s19980"
      "m620s540m620s540m620s540m620s540m620s1600m620s540m620s540m620s1600"
      "m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540m620s1600"
      "m620s1600m620s540m620s1600m620s1600m620s540m620s540m620s1600m620s1600"
      "m620s1600m620s1600m620s1600m620s1600m620s540m620s1600m620s1600m620s1600"
      "m620s19980",
      irsend.outputStr());
}

TEST(TestSendGreeUint64, SendData) {
  IRsendTest irsend(kGpioUnused);
  irsend.begin();

  irsend.reset();
  irsend.sendGree(0x1234567890ABCDEF);
  EXPECT_EQ(
      "f38000d50"
      "m9000s4500"
      "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540"
      "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540"
      "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540"
      "m620s540m620s540m620s540m620s1600m620s1600m620s1600m620s1600m620s540"
      "m620s540m620s1600m620s540"
      "m620s19980"
      "m620s540m620s540m620s540m620s540m620s1600m620s540m620s540m620s1600"
      "m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540m620s1600"
      "m620s1600m620s540m620s1600m620s1600m620s540m620s540m620s1600m620s1600"
      "m620s1600m620s1600m620s1600m620s1600m620s540m620s1600m620s1600m620s1600"
      "m620s19980",
      irsend.outputStr());
}

// Test sending with repeats.
TEST(TestSendGreeChars, SendWithRepeats) {
  IRsendTest irsend(kGpioUnused);
  irsend.begin();

  irsend.reset();
  uint8_t gree_code[kGreeStateLength] = {0x12, 0x34, 0x56, 0x78,
                                         0x90, 0xAB, 0xCD, 0xEF};
  irsend.reset();

  irsend.sendGree(gree_code, kGreeStateLength, 1);
  EXPECT_EQ(
      "f38000d50"
      "m9000s4500"
      "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540"
      "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540"
      "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540"
      "m620s540m620s540m620s540m620s1600m620s1600m620s1600m620s1600m620s540"
      "m620s540m620s1600m620s540"
      "m620s19980"
      "m620s540m620s540m620s540m620s540m620s1600m620s540m620s540m620s1600"
      "m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540m620s1600"
      "m620s1600m620s540m620s1600m620s1600m620s540m620s540m620s1600m620s1600"
      "m620s1600m620s1600m620s1600m620s1600m620s540m620s1600m620s1600m620s1600"
      "m620s19980"
      "m9000s4500"
      "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540"
      "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540"
      "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540"
      "m620s540m620s540m620s540m620s1600m620s1600m620s1600m620s1600m620s540"
      "m620s540m620s1600m620s540"
      "m620s19980"
      "m620s540m620s540m620s540m620s540m620s1600m620s540m620s540m620s1600"
      "m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540m620s1600"
      "m620s1600m620s540m620s1600m620s1600m620s540m620s540m620s1600m620s1600"
      "m620s1600m620s1600m620s1600m620s1600m620s540m620s1600m620s1600m620s1600"
      "m620s19980",
      irsend.outputStr());
}

TEST(TestSendGreeUint64, SendWithRepeats) {
  IRsendTest irsend(kGpioUnused);
  irsend.begin();

  irsend.reset();
  irsend.sendGree(0x1234567890ABCDEF, kGreeBits, 1);
  EXPECT_EQ(
      "f38000d50"
      "m9000s4500"
      "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540"
      "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540"
      "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540"
      "m620s540m620s540m620s540m620s1600m620s1600m620s1600m620s1600m620s540"
      "m620s540m620s1600m620s540"
      "m620s19980"
      "m620s540m620s540m620s540m620s540m620s1600m620s540m620s540m620s1600"
      "m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540m620s1600"
      "m620s1600m620s540m620s1600m620s1600m620s540m620s540m620s1600m620s1600"
      "m620s1600m620s1600m620s1600m620s1600m620s540m620s1600m620s1600m620s1600"
      "m620s19980"
      "m9000s4500"
      "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540"
      "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540"
      "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540"
      "m620s540m620s540m620s540m620s1600m620s1600m620s1600m620s1600m620s540"
      "m620s540m620s1600m620s540"
      "m620s19980"
      "m620s540m620s540m620s540m620s540m620s1600m620s540m620s540m620s1600"
      "m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540m620s1600"
      "m620s1600m620s540m620s1600m620s1600m620s540m620s540m620s1600m620s1600"
      "m620s1600m620s1600m620s1600m620s1600m620s540m620s1600m620s1600m620s1600"
      "m620s19980",
      irsend.outputStr());
}

// Test sending atypical sizes.
TEST(TestSendGreeChars, SendUnexpectedSizes) {
  IRsendTest irsend(kGpioUnused);
  irsend.begin();

  uint8_t gree_short_code[kGreeStateLength - 1] = {0x12, 0x34, 0x56, 0x78,
                                                   0x90, 0xAB, 0xCD};
  uint8_t gree_long_code[kGreeStateLength + 1] = {0x12, 0x34, 0x56, 0x78, 0x90,
                                                  0xAB, 0xCD, 0xEF, 0x12};
  irsend.reset();
  irsend.sendGree(gree_short_code, kGreeStateLength - 1);
  ASSERT_EQ("", irsend.outputStr());

  irsend.reset();
  irsend.sendGree(gree_long_code, kGreeStateLength + 1);
  ASSERT_EQ(
      "f38000d50"
      "m9000s4500"
      "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540"
      "m620s540m620s540m620s1600m620s540m620s1600m620s1600m620s540m620s540"
      "m620s540m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540"
      "m620s540m620s540m620s540m620s1600m620s1600m620s1600m620s1600m620s540"
      "m620s540m620s1600m620s540"
      "m620s19980"
      "m620s540m620s540m620s540m620s540m620s1600m620s540m620s540m620s1600"
      "m620s1600m620s1600m620s540m620s1600m620s540m620s1600m620s540m620s1600"
      "m620s1600m620s540m620s1600m620s1600m620s540m620s540m620s1600m620s1600"
      "m620s1600m620s1600m620s1600m620s1600m620s540m620s1600m620s1600m620s1600"
      "m620s540m620s1600m620s540m620s540m620s1600m620s540m620s540m620s540"
      "m620s19980",
      irsend.outputStr());
}

TEST(TestSendGreeUint64, SendUnexpectedSizes) {
  IRsendTest irsend(kGpioUnused);
  irsend.begin();

  irsend.reset();
  irsend.sendGree(0x1234567890ABCDEF, kGreeBits - 1);
  ASSERT_EQ("", irsend.outputStr());

  irsend.reset();
  irsend.sendGree(0x1234567890ABCDEF, kGreeBits + 1);
  ASSERT_EQ("", irsend.outputStr());
}

TEST(TestSendGree, CompareUint64ToCharResults) {
  IRsendTest irsend_chars(4);
  IRsendTest irsend_uint64(0);

  uint8_t gree_code[kGreeStateLength] = {0x12, 0x34, 0x56, 0x78,
                                         0x90, 0xAB, 0xCD, 0xEF};

  irsend_chars.begin();
  irsend_uint64.begin();

  irsend_chars.reset();
  irsend_uint64.reset();
  irsend_chars.sendGree(gree_code);
  irsend_uint64.sendGree(0x1234567890ABCDEF);
  ASSERT_EQ(irsend_chars.outputStr(), irsend_uint64.outputStr());

  uint8_t gree_zero_code[kGreeStateLength] = {0x00, 0x00, 0x00, 0x00,
                                              0x00, 0x00, 0x00, 0x00};
  irsend_chars.reset();
  irsend_uint64.reset();
  irsend_chars.sendGree(gree_zero_code);
  irsend_uint64.sendGree(static_cast<uint64_t>(0x0));
  ASSERT_EQ(irsend_chars.outputStr(), irsend_uint64.outputStr());
}

// Tests for IRGreeAC class.

TEST(TestGreeClass, Power) {
  IRGreeAC ac(kGpioUnused);
  ac.begin();

  ac.on();
  EXPECT_TRUE(ac.getPower());

  ac.off();
  EXPECT_FALSE(ac.getPower());

  ac.setPower(true);
  EXPECT_TRUE(ac.getPower());

  ac.setPower(false);
  EXPECT_FALSE(ac.getPower());
}

TEST(TestGreeClass, Temperature) {
  IRGreeAC ac(kGpioUnused);
  ac.begin();

  ac.setMode(kGreeCool);

  ac.setTemp(0);
  EXPECT_EQ(kGreeMinTempC, ac.getTemp());

  ac.setTemp(255);
  EXPECT_EQ(kGreeMaxTempC, ac.getTemp());

  ac.setTemp(kGreeMinTempC);
  EXPECT_EQ(kGreeMinTempC, ac.getTemp());
  EXPECT_FALSE(ac.getUseFahrenheit());

  ac.setTemp(kGreeMaxTempC);
  EXPECT_EQ(kGreeMaxTempC, ac.getTemp());

  ac.setTemp(kGreeMinTempC - 1);
  EXPECT_EQ(kGreeMinTempC, ac.getTemp());

  ac.setTemp(kGreeMaxTempC + 1);
  EXPECT_EQ(kGreeMaxTempC, ac.getTemp());

  ac.setTemp(17);
  EXPECT_EQ(17, ac.getTemp());

  ac.setTemp(21);
  EXPECT_EQ(21, ac.getTemp());

  ac.setTemp(25);
  EXPECT_EQ(25, ac.getTemp());

  ac.setTemp(29);
  EXPECT_EQ(29, ac.getTemp());

  // Fahrenheit tests.
  ac.setTemp(kGreeMinTempF, true);
  ASSERT_TRUE(ac.getUseFahrenheit());
  EXPECT_EQ(kGreeMinTempF, ac.getTemp());

  ac.setTemp(kGreeMaxTempF, true);
  ASSERT_TRUE(ac.getUseFahrenheit());
  EXPECT_EQ(kGreeMaxTempF, ac.getTemp());

  ac.setTemp(kGreeMaxTempF + 1, true);
  ASSERT_TRUE(ac.getUseFahrenheit());
  EXPECT_EQ(kGreeMaxTempF, ac.getTemp());

  ac.setTemp(kGreeMaxTempF - 1, true);
  ASSERT_TRUE(ac.getUseFahrenheit());
  EXPECT_EQ(kGreeMaxTempF - 1, ac.getTemp());

  ac.setTemp(kGreeMaxTempF - 2, true);
  ASSERT_TRUE(ac.getUseFahrenheit());
  EXPECT_EQ(kGreeMaxTempF - 2, ac.getTemp());

  ac.setTemp(kGreeMinTempF - 1, true);
  ASSERT_TRUE(ac.getUseFahrenheit());
  EXPECT_EQ(kGreeMinTempF, ac.getTemp());

  ac.setTemp(kGreeMinTempF + 1, true);
  ASSERT_TRUE(ac.getUseFahrenheit());
  EXPECT_EQ(kGreeMinTempF + 1, ac.getTemp());

  ac.setTemp(kGreeMinTempF + 2, true);
  ASSERT_TRUE(ac.getUseFahrenheit());
  EXPECT_EQ(kGreeMinTempF + 2, ac.getTemp());

  // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1121#issuecomment-628946040
  ac.setUseFahrenheit(false);
  const uint8_t state[] = {0x09, 0x01, 0x20, 0x5C, 0x00, 0x20, 0x00, 0x20};
  ac.setRaw(state);
  EXPECT_TRUE(ac.getUseFahrenheit());
  EXPECT_EQ(63, ac.getTemp());
  EXPECT_EQ(
      "Model: 2 (YBOFB), Power: On, Mode: 1 (Cool), Temp: 63F, Fan: 0 (Auto), "
      "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, "
      "Sleep: Off, "
      "Swing(V) Mode: Manual, Swing(V): 0 (Last), Swing(H): 0 (Off), "
      "Timer: Off, Display Temp: 0 (Off)", ac.toString());
}

TEST(TestGreeClass, OperatingMode) {
  IRGreeAC ac(kGpioUnused);
  ac.begin();

  ac.setTemp(17);
  ac.setMode(kGreeAuto);  // Auto should lock the temp to 25C.
  EXPECT_EQ(kGreeAuto, ac.getMode());
  EXPECT_EQ(25, ac.getTemp());
  ac.setTemp(17);
  EXPECT_EQ(25, ac.getTemp());

  ac.setMode(kGreeCool);
  EXPECT_EQ(kGreeCool, ac.getMode());

  ac.setMode(kGreeHeat);
  EXPECT_EQ(kGreeHeat, ac.getMode());

  ac.setMode(kGreeEcono);
  EXPECT_EQ(kGreeEcono, ac.getMode());

  ASSERT_NE(kGreeFanMax, 1);
  ac.setFan(kGreeFanMax);
  ac.setMode(kGreeDry);  // Dry should lock the fan to speed 1.
  EXPECT_EQ(kGreeDry, ac.getMode());
  EXPECT_EQ(1, ac.getFan());
  ac.setFan(kGreeFanMax);
  EXPECT_EQ(1, ac.getFan());

  ac.setMode(kGreeFan);
  EXPECT_EQ(kGreeFan, ac.getMode());

  ac.setMode(kGreeEcono + 1);
  EXPECT_EQ(kGreeAuto, ac.getMode());

  ac.setMode(255);
  EXPECT_EQ(kGreeAuto, ac.getMode());
}

TEST(TestGreeClass, Light) {
  IRGreeAC ac(kGpioUnused);
  ac.begin();

  ac.setLight(true);
  EXPECT_TRUE(ac.getLight());

  ac.setLight(false);
  EXPECT_FALSE(ac.getLight());

  ac.setLight(true);
  EXPECT_TRUE(ac.getLight());
}

TEST(TestGreeClass, XFan) {
  IRGreeAC ac(kGpioUnused);
  ac.begin();

  ac.setXFan(true);
  EXPECT_TRUE(ac.getXFan());

  ac.setXFan(false);
  EXPECT_FALSE(ac.getXFan());

  ac.setXFan(true);
  EXPECT_TRUE(ac.getXFan());
}

TEST(TestGreeClass, Turbo) {
  IRGreeAC ac(kGpioUnused);
  ac.begin();

  ac.setTurbo(true);
  EXPECT_TRUE(ac.getTurbo());

  ac.setTurbo(false);
  EXPECT_FALSE(ac.getTurbo());

  ac.setTurbo(true);
  EXPECT_TRUE(ac.getTurbo());
}

TEST(TestGreeClass, Econo) {
  IRGreeAC ac(kGpioUnused);
  ac.begin();

  ac.setEcono(true);
  EXPECT_TRUE(ac.getEcono());

  ac.setEcono(false);
  EXPECT_FALSE(ac.getEcono());

  ac.setEcono(true);
  EXPECT_TRUE(ac.getEcono());
}

TEST(TestGreeClass, IFeel) {
  IRGreeAC ac(kGpioUnused);
  ac.begin();

  ac.setIFeel(true);
  EXPECT_TRUE(ac.getIFeel());

  ac.setIFeel(false);
  EXPECT_FALSE(ac.getIFeel());

  ac.setIFeel(true);
  EXPECT_TRUE(ac.getIFeel());

  // https://github.com/crankyoldgit/IRremoteESP8266/pull/770#issuecomment-504992209
  uint8_t on[8] = {0x08, 0x09, 0x60, 0x50, 0x00, 0x44, 0x00, 0xF0};
  uint8_t off[8] = {0x08, 0x09, 0x60, 0x50, 0x00, 0x40, 0x00, 0xF0};
  ac.setRaw(off);
  EXPECT_FALSE(ac.getIFeel());
  ac.setRaw(on);
  EXPECT_TRUE(ac.getIFeel());
}

TEST(TestGreeClass, WiFi) {
  IRGreeAC ac(kGpioUnused);
  ac.begin();

  ac.setWiFi(true);
  EXPECT_TRUE(ac.getWiFi());

  ac.setWiFi(false);
  EXPECT_FALSE(ac.getWiFi());

  ac.setWiFi(true);
  EXPECT_TRUE(ac.getWiFi());

  // https://github.com/crankyoldgit/IRremoteESP8266/pull/770#issuecomment-504992209
  uint8_t on[8] = {0x09, 0x09, 0x60, 0x50, 0x00, 0x40, 0x00, 0x00};
  uint8_t off[8] = {0x09, 0x09, 0x60, 0x50, 0x00, 0x00, 0x00, 0xC0};
  ac.setRaw(off);
  EXPECT_FALSE(ac.getWiFi());
  ac.setRaw(on);
  EXPECT_TRUE(ac.getWiFi());
}

TEST(TestGreeClass, Sleep) {
  IRGreeAC ac(kGpioUnused);
  ac.begin();

  ac.setSleep(true);
  EXPECT_TRUE(ac.getSleep());

  ac.setSleep(false);
  EXPECT_FALSE(ac.getSleep());

  ac.setSleep(true);
  EXPECT_TRUE(ac.getSleep());
}

TEST(TestGreeClass, FanSpeed) {
  IRGreeAC ac(kGpioUnused);
  ac.begin();

  ac.setFan(0);
  EXPECT_EQ(0, ac.getFan());

  ac.setFan(255);
  EXPECT_EQ(kGreeFanMax, ac.getFan());

  ac.setFan(kGreeFanMax);
  EXPECT_EQ(kGreeFanMax, ac.getFan());

  ac.setFan(kGreeFanMax + 1);
  EXPECT_EQ(kGreeFanMax, ac.getFan());

  ac.setFan(kGreeFanMax - 1);
  EXPECT_EQ(kGreeFanMax - 1, ac.getFan());

  ac.setFan(1);
  EXPECT_EQ(1, ac.getFan());

  ac.setFan(1);
  EXPECT_EQ(1, ac.getFan());

  ac.setFan(3);
  EXPECT_EQ(3, ac.getFan());
}

TEST(TestGreeClass, VerticalSwing) {
  IRGreeAC ac(kGpioUnused);
  ac.begin();
  EXPECT_FALSE(ac.getSwingVerticalAuto());
  EXPECT_EQ(kGreeSwingLastPos, ac.getSwingVerticalPosition());

  ac.setSwingVertical(true, kGreeSwingAuto);
  EXPECT_TRUE(ac.getSwingVerticalAuto());
  EXPECT_EQ(kGreeSwingAuto, ac.getSwingVerticalPosition());

  ac.setSwingVertical(false, kGreeSwingMiddle);
  EXPECT_FALSE(ac.getSwingVerticalAuto());
  EXPECT_EQ(kGreeSwingMiddle, ac.getSwingVerticalPosition());

  ac.setSwingVertical(true, kGreeSwingDownAuto);
  EXPECT_TRUE(ac.getSwingVerticalAuto());
  EXPECT_EQ(kGreeSwingDownAuto, ac.getSwingVerticalPosition());

  // Out of bounds.
  ac.setSwingVertical(false, 255);
  EXPECT_FALSE(ac.getSwingVerticalAuto());
  EXPECT_EQ(kGreeSwingLastPos, ac.getSwingVerticalPosition());
  ac.setSwingVertical(false, kGreeSwingAuto);
  EXPECT_FALSE(ac.getSwingVerticalAuto());
  EXPECT_EQ(kGreeSwingLastPos, ac.getSwingVerticalPosition());

  ac.setSwingVertical(true, 255);
  EXPECT_TRUE(ac.getSwingVerticalAuto());
  EXPECT_EQ(kGreeSwingAuto, ac.getSwingVerticalPosition());
  ac.setSwingVertical(true, kGreeSwingDown);
  EXPECT_TRUE(ac.getSwingVerticalAuto());
  EXPECT_EQ(kGreeSwingAuto, ac.getSwingVerticalPosition());
}

TEST(TestGreeClass, HorizontalSwing) {
  IRGreeAC ac(kGpioUnused);
  ac.begin();

  ac.setSwingHorizontal(kGreeSwingHAuto);
  EXPECT_EQ(kGreeSwingHAuto, ac.getSwingHorizontal());

  ac.setSwingHorizontal(kGreeSwingHMiddle);
  EXPECT_EQ(kGreeSwingHMiddle, ac.getSwingHorizontal());

  ac.setSwingHorizontal(kGreeSwingHMaxRight);
  EXPECT_EQ(kGreeSwingHMaxRight, ac.getSwingHorizontal());

  // Out of bounds.
  ac.setSwingHorizontal(kGreeSwingHMaxRight + 1);
  EXPECT_EQ(kGreeSwingHOff, ac.getSwingHorizontal());
}

TEST(TestGreeClass, SetAndGetRaw) {
  IRGreeAC ac(kGpioUnused);
  uint8_t initialState[kGreeStateLength] = {0x00, 0x09, 0x20, 0x50,
                                            0x00, 0x20, 0x00, 0x50};
  uint8_t expectedState[kGreeStateLength] = {0xA9, 0x05, 0xD0, 0x50,
                                             0x00, 0x20, 0x00, 0xA0};

  EXPECT_STATE_EQ(initialState, ac.getRaw(), kGreeBits);

  // toggle the power state.
  ac.setPower(!ac.getPower());
  ac.setMode(kGreeCool);
  ac.setTemp(21);
  ac.setFan(2);
  ac.setLight(false);
  ac.setTurbo(true);
  ac.setSleep(true);
  ac.setXFan(true);

  EXPECT_EQ(kGreeCool, ac.getMode());
  EXPECT_EQ(21, ac.getTemp());
  EXPECT_EQ(2, ac.getFan());
  EXPECT_FALSE(ac.getLight());
  EXPECT_TRUE(ac.getTurbo());
  EXPECT_TRUE(ac.getSleep());
  EXPECT_TRUE(ac.getXFan());

  EXPECT_STATE_EQ(expectedState, ac.getRaw(), kGreeBits);
  ac.setRaw(initialState);
  EXPECT_STATE_EQ(initialState, ac.getRaw(), kGreeBits);
}

TEST(TestGreeClass, HumanReadable) {
  IRGreeAC ac(kGpioUnused);

  EXPECT_EQ(
      "Model: 1 (YAW1F), Power: Off, Mode: 0 (Auto), Temp: 25C, Fan: 0 (Auto), "
      "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, "
      "Sleep: Off, "
      "Swing(V) Mode: Manual, Swing(V): 0 (Last), Swing(H): 0 (Off), "
      "Timer: Off, Display Temp: 0 (Off)",
      ac.toString());
  ac.on();
  ac.setMode(kGreeCool);
  ac.setTemp(kGreeMinTempC);
  ac.setFan(kGreeFanMax);
  ac.setXFan(true);
  ac.setSleep(true);
  ac.setLight(false);
  ac.setTurbo(true);
  ac.setIFeel(true);
  ac.setWiFi(true);
  ac.setSwingVertical(true, kGreeSwingAuto);
  ac.setTimer(12 * 60 + 30);
  ac.setDisplayTempSource(3);
  EXPECT_EQ(
      "Model: 1 (YAW1F), Power: On, Mode: 1 (Cool), Temp: 16C, Fan: 3 (High), "
      "Turbo: On, Econo: Off, IFeel: On, WiFi: On, XFan: On, Light: Off, "
      "Sleep: On, "
      "Swing(V) Mode: Auto, Swing(V): 1 (Auto), Swing(H): 0 (Off), "
      "Timer: 12:30, Display Temp: 3 (Outside)",
      ac.toString());
}

// Tests for decodeGree().

// Decode a synthetic Gree message.
TEST(TestDecodeGree, NormalSynthetic) {
  IRsendTest irsend(kGpioUnused);
  IRrecv irrecv(kGpioUnused);
  irsend.begin();

  uint8_t gree_code[kGreeStateLength] = {0x00, 0x09, 0x20, 0x50,
                                         0x00, 0x20, 0x00, 0x50};

  irsend.reset();
  irsend.sendGree(gree_code);
  irsend.makeDecodeResult();
  EXPECT_TRUE(irrecv.decode(&irsend.capture));
  EXPECT_EQ(GREE, irsend.capture.decode_type);
  ASSERT_EQ(kGreeBits, irsend.capture.bits);
  EXPECT_STATE_EQ(gree_code, irsend.capture.state, kGreeBits);
}

// Decode a real Gree message.
TEST(TestDecodeGree, NormalRealExample) {
  IRsendTest irsend(kGpioUnused);
  IRrecv irrecv(kGpioUnused);
  IRGreeAC ac(kGpioUnused);
  irsend.begin();

  uint8_t gree_code[kGreeStateLength] = {0x19, 0x0A, 0x60, 0x50,
                                         0x02, 0x23, 0x00, 0xF0};

  // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/386
  uint16_t rawData[139] = {
      9008, 4496,  644, 1660, 676, 530,  648, 558,  672, 1636, 646, 1660,
      644,  556,   650, 584,  626, 560,  644, 580,  628, 1680, 624, 560,
      648,  1662,  644, 582,  648, 536,  674, 530,  646, 580,  628, 560,
      670,  532,   646, 562,  644, 556,  672, 536,  648, 1662, 646, 1660,
      652,  554,   644, 558,  672, 538,  644, 560,  668, 560,  648, 1638,
      668,  536,   644, 1660, 668, 532,  648, 560,  648, 1660, 674, 554,
      622,  19990, 646, 580,  624, 1660, 648, 556,  648, 558,  674, 556,
      622,  560,   644, 564,  668, 536,  646, 1662, 646, 1658, 672, 534,
      648,  558,   644, 562,  648, 1662, 644, 584,  622, 558,  648, 562,
      668,  534,   670, 536,  670, 532,  672, 536,  646, 560,  646, 558,
      648,  558,   670, 534,  650, 558,  646, 560,  646, 560,  668, 1638,
      646,  1662,  646, 1660, 646, 1660, 648};  // Issue #386

  irsend.reset();
  irsend.sendRaw(rawData, 139, 38000);
  irsend.makeDecodeResult();
  EXPECT_TRUE(irrecv.decode(&irsend.capture));
  EXPECT_EQ(GREE, irsend.capture.decode_type);
  ASSERT_EQ(kGreeBits, irsend.capture.bits);
  EXPECT_STATE_EQ(gree_code, irsend.capture.state, kGreeBits);
  ac.setRaw(irsend.capture.state);
  EXPECT_EQ(
      "Model: 1 (YAW1F), Power: On, Mode: 1 (Cool), Temp: 26C, Fan: 1 (Low), "
      "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, "
      "Sleep: Off, "
      "Swing(V) Mode: Manual, Swing(V): 2 (UNKNOWN), Swing(H): 0 (Off), "
      "Timer: Off, Display Temp: 3 (Outside)",
      IRAcUtils::resultAcToString(&irsend.capture));
  stdAc::state_t r, p;
  ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
}

TEST(TestGreeClass, toCommon) {
  IRGreeAC ac(kGpioUnused);
  ac.setPower(true);
  ac.setMode(kGreeCool);
  ac.setTemp(20);
  ac.setFan(kGreeFanMax);
  ac.setSwingVertical(false, kGreeSwingUp);
  ac.setTurbo(true);
  ac.setXFan(true);
  ac.setLight(true);
  ac.setSleep(true);
  // Now test it.
  ASSERT_EQ(decode_type_t::GREE, ac.toCommon().protocol);
  ASSERT_EQ(gree_ac_remote_model_t::YAW1F, ac.toCommon().model);
  ASSERT_TRUE(ac.toCommon().power);
  ASSERT_TRUE(ac.toCommon().celsius);
  ASSERT_EQ(20, ac.toCommon().degrees);
  ASSERT_TRUE(ac.toCommon().turbo);
  ASSERT_TRUE(ac.toCommon().clean);
  ASSERT_TRUE(ac.toCommon().light);
  ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon().mode);
  ASSERT_EQ(stdAc::fanspeed_t::kMax, ac.toCommon().fanspeed);
  ASSERT_EQ(stdAc::swingv_t::kHighest, ac.toCommon().swingv);
  ASSERT_EQ(0, ac.toCommon().sleep);
  // Unsupported.
  ASSERT_EQ(stdAc::swingh_t::kOff, ac.toCommon().swingh);
  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().clock);

  // Test kGreeSwingLastPos following the pattern in IRac::gree().
  ASSERT_EQ(kGreeSwingLastPos, ac.convertSwingV(stdAc::swingv_t::kOff));
  ac.setSwingVertical(false, kGreeSwingLastPos);
  ASSERT_EQ(stdAc::swingv_t::kOff, ac.toCommon().swingv);
}

TEST(TestGreeClass, Issue814Power) {
  IRGreeAC ac(kGpioUnused);
  ac.begin();

  // https://github.com/crankyoldgit/IRremoteESP8266/issues/814#issuecomment-511263921
  uint8_t YBOFB_on[8] = {0x59, 0x07, 0x20, 0x50, 0x01, 0x20, 0x00, 0xC0};
  uint8_t off[8] = {0x51, 0x07, 0x20, 0x50, 0x01, 0x20, 0x00, 0x40};

  ac.on();
  EXPECT_EQ(gree_ac_remote_model_t::YAW1F, ac.getModel());
  ac.setRaw(off);
  EXPECT_FALSE(ac.getPower());
  ac.setRaw(YBOFB_on);
  EXPECT_TRUE(ac.getPower());
  EXPECT_EQ(gree_ac_remote_model_t::YBOFB, ac.getModel());
  EXPECT_EQ(
      "Model: 2 (YBOFB), Power: On, Mode: 1 (Cool), Temp: 23C, Fan: 1 (Low), "
      "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, "
      "Sleep: Off, "
      "Swing(V) Mode: Auto, Swing(V): 1 (Auto), Swing(H): 0 (Off), Timer: Off, "
      "Display Temp: 0 (Off)",
      ac.toString());
  ac.off();
  EXPECT_STATE_EQ(off, ac.getRaw(), kGreeBits);
  ac.on();
  EXPECT_STATE_EQ(YBOFB_on, ac.getRaw(), kGreeBits);
  uint8_t YAW1F_on[8] = {0x59, 0x07, 0x60, 0x50, 0x01, 0x20, 0x00, 0xC0};
  ac.setModel(gree_ac_remote_model_t::YAW1F);
  EXPECT_STATE_EQ(YAW1F_on, ac.getRaw(), kGreeBits);
  ac.off();
  EXPECT_STATE_EQ(off, ac.getRaw(), kGreeBits);
  ac.setModel(gree_ac_remote_model_t::YBOFB);
  ac.on();
  EXPECT_STATE_EQ(YBOFB_on, ac.getRaw(), kGreeBits);
}

TEST(TestGreeClass, Timer) {
  IRGreeAC ac(kGpioUnused);
  ac.begin();

  ac.setTimer(0);
  EXPECT_FALSE(ac.getTimerEnabled());
  EXPECT_EQ(0, ac.getTimer());

  ac.setTimer(29);
  EXPECT_FALSE(ac.getTimerEnabled());
  EXPECT_EQ(0, ac.getTimer());

  ac.setTimer(30);
  EXPECT_TRUE(ac.getTimerEnabled());
  EXPECT_EQ(30, ac.getTimer());

  ac.setTimer(60);
  EXPECT_TRUE(ac.getTimerEnabled());
  EXPECT_EQ(60, ac.getTimer());

  ac.setTimer(90);
  EXPECT_TRUE(ac.getTimerEnabled());
  EXPECT_EQ(90, ac.getTimer());

  ac.setTimer(10 * 60);
  EXPECT_TRUE(ac.getTimerEnabled());
  EXPECT_EQ(10 * 60, ac.getTimer());

  ac.setTimer(23 * 60 + 59);
  EXPECT_TRUE(ac.getTimerEnabled());
  EXPECT_EQ(23 * 60 + 30, ac.getTimer());

  ac.setTimer(24 * 60 + 1);
  EXPECT_TRUE(ac.getTimerEnabled());
  EXPECT_EQ(24 * 60, ac.getTimer());

  ac.setTimer(24 * 60 + 30);
  EXPECT_TRUE(ac.getTimerEnabled());
  EXPECT_EQ(24 * 60, ac.getTimer());
}

TEST(TestGreeClass, DisplayTempSource) {
  IRGreeAC ac(kGpioUnused);
  ac.begin();

  ac.setDisplayTempSource(1);
  EXPECT_EQ(1, ac.getDisplayTempSource());

  ac.setDisplayTempSource(2);
  EXPECT_EQ(2, ac.getDisplayTempSource());

  ac.setDisplayTempSource(3);
  EXPECT_EQ(3, ac.getDisplayTempSource());

  ac.setDisplayTempSource(1);
  EXPECT_EQ(1, ac.getDisplayTempSource());

  // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1118#issuecomment-627674014
  const uint8_t state[8] = {0x4C, 0x04, 0x60, 0x50, 0x01, 0x02, 0x00, 0xA0};
  ac.setRaw(state);
  EXPECT_EQ(2, ac.getDisplayTempSource());
}

TEST(TestUtils, Housekeeping) {
  ASSERT_EQ("GREE", typeToString(decode_type_t::GREE));
  ASSERT_EQ(decode_type_t::GREE, strToDecodeType("GREE"));
  ASSERT_TRUE(hasACState(decode_type_t::GREE));
  ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::GREE));
  ASSERT_EQ(kGreeBits, IRsend::defaultBits(decode_type_t::GREE));
  ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::GREE));
  ASSERT_EQ(gree_ac_remote_model_t::YAW1F, IRac::strToModel("YAW1F"));
  ASSERT_EQ(irutils::modelToStr(decode_type_t::GREE,
                                gree_ac_remote_model_t::YAW1F), "YAW1F");
  ASSERT_EQ(gree_ac_remote_model_t::YBOFB, IRac::strToModel("YBOFB"));
  ASSERT_EQ(irutils::modelToStr(decode_type_t::GREE,
                                gree_ac_remote_model_t::YBOFB), "YBOFB");
  ASSERT_EQ(gree_ac_remote_model_t::YX1FSF, IRac::strToModel("YX1FSF"));
  ASSERT_EQ(irutils::modelToStr(decode_type_t::GREE,
                                gree_ac_remote_model_t::YX1FSF), "YX1FSF");
}

TEST(TestGreeClass, Issue1821EnergySaver) {
  IRGreeAC ac(kGpioUnused);
  ac.begin();

  // https://github.com/crankyoldgit/IRremoteESP8266/issues/1821#issue-1271458457
  const uint8_t energy[8] = {0x1D, 0x09, 0x60, 0x58, 0x00, 0x20, 0x00, 0xA0};

  ac.setRaw(energy);
  EXPECT_EQ(kGreeEcono, ac.getMode());
  EXPECT_TRUE(ac.getEcono());
  EXPECT_EQ(gree_ac_remote_model_t::YX1FSF, ac.getModel());
  EXPECT_EQ(
      "Model: 3 (YX1FSF), Power: On, Mode: 5 (Econo), Temp: 77F, Fan: 1 (Low), "
      "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, "
      "Sleep: Off, Swing(V) Mode: Manual, Swing(V): 0 (Last), "
      "Swing(H): 0 (Off), Timer: Off, Display Temp: 0 (Off)",
      ac.toString());
}
