#pragma once

namespace esphome::opentherm {

// ===== hub.h macros =====

// *_LIST macros will be generated in defines.h if at least one sensor from each platform is used.
// These lists will look like this:
// #define OPENTHERM_BINARY_SENSOR_LIST(F, sep) F(sensor_1) sep F(sensor_2)
// These lists will be used in hub.h to define sensor fields (passing macros like OPENTHERM_DECLARE_SENSOR as F)
// and setters (passing macros like OPENTHERM_SET_SENSOR as F) (see below)
// In order for things not to break, we define empty lists here in case some platforms are not used in config.
#ifndef OPENTHERM_SENSOR_LIST
#define OPENTHERM_SENSOR_LIST(F, sep)
#endif
#ifndef OPENTHERM_BINARY_SENSOR_LIST
#define OPENTHERM_BINARY_SENSOR_LIST(F, sep)
#endif
#ifndef OPENTHERM_SWITCH_LIST
#define OPENTHERM_SWITCH_LIST(F, sep)
#endif
#ifndef OPENTHERM_NUMBER_LIST
#define OPENTHERM_NUMBER_LIST(F, sep)
#endif
#ifndef OPENTHERM_OUTPUT_LIST
#define OPENTHERM_OUTPUT_LIST(F, sep)
#endif
#ifndef OPENTHERM_INPUT_SENSOR_LIST
#define OPENTHERM_INPUT_SENSOR_LIST(F, sep)
#endif
#ifndef OPENTHERM_SETTING_LIST
#define OPENTHERM_SETTING_LIST(F, sep)
#endif

// Use macros to create fields for every entity specified in the ESPHome configuration
#define OPENTHERM_DECLARE_SENSOR(entity) sensor::Sensor *entity;
#define OPENTHERM_DECLARE_BINARY_SENSOR(entity) binary_sensor::BinarySensor *entity;
#define OPENTHERM_DECLARE_SWITCH(entity) OpenthermSwitch *entity;
#define OPENTHERM_DECLARE_NUMBER(entity) OpenthermNumber *entity;
#define OPENTHERM_DECLARE_OUTPUT(entity) OpenthermOutput *entity;
#define OPENTHERM_DECLARE_INPUT_SENSOR(entity) sensor::Sensor *entity;
#define OPENTHERM_DECLARE_SETTING(type, entity, def) type entity = def;

// Setter macros
#define OPENTHERM_SET_SENSOR(entity) \
  void set_##entity(sensor::Sensor *sensor) { this->entity = sensor; }

#define OPENTHERM_SET_BINARY_SENSOR(entity) \
  void set_##entity(binary_sensor::BinarySensor *binary_sensor) { this->entity = binary_sensor; }

#define OPENTHERM_SET_SWITCH(entity) \
  void set_##entity(OpenthermSwitch *sw) { this->entity = sw; }

#define OPENTHERM_SET_NUMBER(entity) \
  void set_##entity(OpenthermNumber *number) { this->entity = number; }

#define OPENTHERM_SET_OUTPUT(entity) \
  void set_##entity(OpenthermOutput *output) { this->entity = output; }

#define OPENTHERM_SET_INPUT_SENSOR(entity) \
  void set_##entity(sensor::Sensor *sensor) { this->entity = sensor; }

#define OPENTHERM_SET_SETTING(type, entity, def) \
  void set_##entity(type value) { this->entity = value; }

// ===== hub.cpp macros =====

// *_MESSAGE_HANDLERS are generated in defines.h and look like this:
// OPENTHERM_NUMBER_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep) MESSAGE(COOLING_CONTROL)
// ENTITY(cooling_control_number, f88) postscript msg_sep They contain placeholders for message part and entities parts,
// since one message can contain multiple entities. MESSAGE part is substituted with OPENTHERM_MESSAGE_WRITE_MESSAGE,
// OPENTHERM_MESSAGE_READ_MESSAGE or OPENTHERM_MESSAGE_RESPONSE_MESSAGE. ENTITY part is substituted with
// OPENTHERM_MESSAGE_WRITE_ENTITY or OPENTHERM_MESSAGE_RESPONSE_ENTITY. OPENTHERM_IGNORE is used for sensor read
// requests since no data needs to be sent or processed, just the data id.

// In order for things not to break, we define empty lists here in case some platforms are not used in config.
#ifndef OPENTHERM_SENSOR_MESSAGE_HANDLERS
#define OPENTHERM_SENSOR_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep)
#endif
#ifndef OPENTHERM_BINARY_SENSOR_MESSAGE_HANDLERS
#define OPENTHERM_BINARY_SENSOR_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep)
#endif
#ifndef OPENTHERM_SWITCH_MESSAGE_HANDLERS
#define OPENTHERM_SWITCH_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep)
#endif
#ifndef OPENTHERM_NUMBER_MESSAGE_HANDLERS
#define OPENTHERM_NUMBER_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep)
#endif
#ifndef OPENTHERM_OUTPUT_MESSAGE_HANDLERS
#define OPENTHERM_OUTPUT_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep)
#endif
#ifndef OPENTHERM_INPUT_SENSOR_MESSAGE_HANDLERS
#define OPENTHERM_INPUT_SENSOR_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep)
#endif
#ifndef OPENTHERM_SETTING_MESSAGE_HANDLERS
#define OPENTHERM_SETTING_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep)
#endif

// Write data request builders
#define OPENTHERM_MESSAGE_WRITE_MESSAGE(msg) \
  case MessageId::msg: { \
    data.type = MessageType::WRITE_DATA; \
    data.id = request_id;
#define OPENTHERM_MESSAGE_WRITE_ENTITY(key, msg_data) message_data::write_##msg_data(this->key->state, data);
#define OPENTHERM_MESSAGE_WRITE_SETTING(key, msg_data) message_data::write_##msg_data(this->key, data);
#define OPENTHERM_MESSAGE_WRITE_POSTSCRIPT \
  return data; \
  }

// Read data request builder
#define OPENTHERM_MESSAGE_READ_MESSAGE(msg) \
  case MessageId::msg: \
    data.type = MessageType::READ_DATA; \
    data.id = request_id; \
    return data;

// Data processing builders
#define OPENTHERM_MESSAGE_RESPONSE_MESSAGE(msg) case MessageId::msg:
#define OPENTHERM_MESSAGE_RESPONSE_ENTITY(key, msg_data) this->key->publish_state(message_data::parse_##msg_data(data));
#define OPENTHERM_MESSAGE_RESPONSE_POSTSCRIPT break;

#define OPENTHERM_IGNORE(x, y)

// Default macros for STATUS entities
#ifndef OPENTHERM_READ_ch_enable
#define OPENTHERM_READ_ch_enable true
#endif
#ifndef OPENTHERM_READ_dhw_enable
#define OPENTHERM_READ_dhw_enable true
#endif
#ifndef OPENTHERM_READ_t_set
#define OPENTHERM_READ_t_set 0.0
#endif
#ifndef OPENTHERM_READ_cooling_enable
#define OPENTHERM_READ_cooling_enable false
#endif
#ifndef OPENTHERM_READ_cooling_control
#define OPENTHERM_READ_cooling_control 0.0
#endif
#ifndef OPENTHERM_READ_otc_active
#define OPENTHERM_READ_otc_active false
#endif
#ifndef OPENTHERM_READ_ch2_active
#define OPENTHERM_READ_ch2_active false
#endif
#ifndef OPENTHERM_READ_t_set_ch2
#define OPENTHERM_READ_t_set_ch2 0.0
#endif
#ifndef OPENTHERM_READ_summer_mode_active
#define OPENTHERM_READ_summer_mode_active false
#endif
#ifndef OPENTHERM_READ_dhw_block
#define OPENTHERM_READ_dhw_block false
#endif

// These macros utilize the structure of *_LIST macros in order
#define ID(x) x
#define SHOW_INNER(x) #x
#define SHOW(x) SHOW_INNER(x)

}  // namespace esphome::opentherm
