#pragma once
#include "esphome/core/component.h"
#include "esphome/components/climate/climate.h"
#include "esphome/components/remote_transmitter/remote_transmitter.h"

namespace esphome {
namespace gree_ir {

class GreeIR : public climate::Climate, public Component {
 public:
  void set_transmitter(remote_transmitter::RemoteTransmitterComponent *tx) {
    this->transmitter_ = tx;
  }

  void setup() override {
    this->mode = climate::CLIMATE_MODE_OFF;
    this->target_temperature = 24;
    this->publish_state();
  }

  climate::ClimateTraits traits() override {
    auto traits = climate::ClimateTraits();
    traits.set_supported_modes({
        climate::CLIMATE_MODE_OFF,
        climate::CLIMATE_MODE_COOL,
        climate::CLIMATE_MODE_HEAT,
        climate::CLIMATE_MODE_FAN_ONLY,
    });
    traits.set_visual_min_temperature(16);
    traits.set_visual_max_temperature(30);
    traits.set_visual_temperature_step(1);
    return traits;
  }

  void control(const climate::ClimateCall &call) override {
    if (call.get_mode().has_value())
      this->mode = *call.get_mode();
    if (call.get_target_temperature().has_value())
      this->target_temperature = *call.get_target_temperature();
    this->transmit_state_();
    this->publish_state();
  }

 protected:
  remote_transmitter::RemoteTransmitterComponent *transmitter_{nullptr};

  void transmit_state_() {
    uint8_t b[8] = {0, 0, 0, 0, 0, 0, 0, 0};
    uint8_t mode_byte, b2, b1_0;
    switch (this->mode) {
      case climate::CLIMATE_MODE_COOL:     mode_byte = 0x19; b2 = 0x60; b1_0 = 0x06; break;
      case climate::CLIMATE_MODE_HEAT:     mode_byte = 0x4C; b2 = 0x40; b1_0 = 0x01; break;
      case climate::CLIMATE_MODE_FAN_ONLY: mode_byte = 0x1B; b2 = 0x60; b1_0 = 0x02; break;
      default:                             mode_byte = 0x44; b2 = 0x00; b1_0 = 0x01; break;
    }
    int temp = (int) this->target_temperature;
    if (temp < 16) temp = 16;
    if (temp > 30) temp = 30;
    uint8_t t = (this->mode == climate::CLIMATE_MODE_OFF) ? 0x0A : (uint8_t)(temp - 16);

    b[0] = mode_byte; b[1] = t; b[2] = b2; b[3] = 0x50;
    b[4] = b1_0; b[5] = 0x00; b[6] = 0x00;
    uint8_t cs = ((b[0] & 0xF) + (b[1] & 0xF) + (b[2] & 0xF) + (b[3] & 0xF) +
                  ((b[4] >> 4) & 0xF) + ((b[5] >> 4) & 0xF) + ((b[6] >> 4) & 0xF) + 0x0A) & 0xF;
    b[7] = (cs << 4) & 0xF0;

    auto transmit = this->transmitter_->transmit();
    auto *data = transmit.get_data();
    data->set_carrier_frequency(38000);
    const int MK = 677, S0 = 553, S1 = 1630, GAP = 19960;
    data->mark(9030); data->space(4460);
    for (int by = 0; by < 4; by++)
      for (int bit = 0; bit < 8; bit++) { data->mark(MK); data->space(((b[by] >> bit) & 1) ? S1 : S0); }
    const int foot[3] = {0, 1, 0};
    for (int i = 0; i < 3; i++) { data->mark(MK); data->space(foot[i] ? S1 : S0); }
    data->mark(MK); data->space(GAP);
    for (int by = 4; by < 8; by++)
      for (int bit = 0; bit < 8; bit++) { data->mark(MK); data->space(((b[by] >> bit) & 1) ? S1 : S0); }
    data->mark(MK);
    transmit.perform();
  }
};

}  // namespace gree_ir
}  // namespace
