#include <main.hpp>

/************************************************
 *              Global Variables                *
 ************************************************/
RemoteVariable aqi = RemoteVariable();
ESPMegaPRO espmega = ESPMegaPRO();
const char *mode_names[] = AC_MODE_NAMES;
const char *fan_speed_names[] = AC_FAN_SPEED_NAMES;
AirConditioner ac_config = {
    .max_temperature = 32,
    .min_temperature = 16,
    .modes = 4,
    .mode_names = mode_names,
    .fan_speeds = 4,
    .fan_speed_names = fan_speed_names,
    .getInfraredCode = &getInfraredCode};

ClimateCard ac = ClimateCard(AC_IR_TX_PIN, ac_config, AC_SENSOR_TYPE, AC_SENSOR_PIN, AC_IR_CHANNEL);

cud_display_conf_t cud_display_conf = {
    .aqi = &aqi,
    .uart = &DISPLAY_UART,
    .tx = DISPLAY_TX,
    .rx = DISPLAY_RX,
    .communication_baudrate = DISPLAY_COMMUNICATION_BAUDRATE,
    .ota_baudrate = DISPLAY_OTA_BAUDRATE,
    .light_pins = {LIGHT_ROW_1_PIN, LIGHT_ROW_2_PIN, LIGHT_ROW_3_PIN, LIGHT_ROW_4_PIN},
    .fan_pins = {FAN_ROW_1_PIN, FAN_ROW_2_PIN, FAN_ROW_3_PIN},
    .air_purifier_pin = AIR_PURIFIER_PIN,
    .mosquito_zapper_pin = MOSQUITO_ZAPPER_PIN,
    .socket_contactor_pin = SOCKET_CONTACTOR_PIN};

CUDDisplay cudDisplay = CUDDisplay(&cud_display_conf);

/************************************************
 *            End of Global Variables           *
 ************************************************/

void setup()
{
  // Initialize both displayports
  INTERNAL_DISPLAY_UART.begin(INTERNAL_DISPLAY_BAUDRATE);
  DISPLAY_UART.begin(DISPLAY_COMMUNICATION_BAUDRATE, SERIAL_8N1, DISPLAY_RX, DISPLAY_TX);
  ESP_LOGI("CUD IoT OS", "Starting Initialization");
  // Reset both display
  ESP_LOGV("CUD IoT OS", "Resetting both displays");
  send_stop_bytes(INTERNAL_DISPLAY_UART);
  INTERNAL_DISPLAY_UART.print("rest");
  send_stop_bytes(INTERNAL_DISPLAY_UART);
  send_stop_bytes(DISPLAY_UART);
  DISPLAY_UART.print("rest");
  send_stop_bytes(DISPLAY_UART);
  // Wait for the display to reset
  vTaskDelay(1000 / portTICK_PERIOD_MS);
  // Initialize the ESPMegaPRO
  ESP_LOGV("CUD IoT OS", "Initializing ESPMegaPRO");
  espmega.begin();
  // Initialize IoT Modules
  ESP_LOGV("CUD IoT OS", "Initializing IoT Modules");
  espmega.enableIotModule();
  // Setup Ethernet
  ESP_LOGV("CUD IoT OS", "Initializing Ethernet");
  ETH.begin();
  ESP_LOGD("CUD IoT OS", "Binding Ethernet Interface");
  espmega.iot->bindEthernetInterface(&ETH);
  // Connect to Network
  ESP_LOGV("CUD IoT OS", "Connecting to Network");
  espmega.iot->loadNetworkConfig();
  espmega.iot->connectNetwork();
  // Connect to the MQTT Broker
  ESP_LOGV("CUD IoT OS", "Connecting to MQTT Broker");
  espmega.iot->loadMqttConfig();
  espmega.iot->connectToMqtt();
  // Enable Web Server
  ESP_LOGV("CUD IoT OS", "Enabling Web Server");
  espmega.enableWebServer(80);
  // Initialize Internal Display
  ESP_LOGV("CUD IoT OS", "Initializing Internal Display");
  espmega.enableInternalDisplay(&INTERNAL_DISPLAY_UART);
  // Initialize Air Conditioner
  ESP_LOGV("CUD IoT OS", "Initializing Air Conditioner");
  espmega.installCard(2, &ac);
  ac.bindFRAM(&espmega.fram, AC_FRAM_ADDR);
  ac.loadStateFromFRAM();
  ac.setFRAMAutoSave(true);
  ESP_LOGD("CUD IoT OS", "Loaded Air Conditioner State from FRAM, Temperature: %d, Mode: %d, Fan Speed: %d", ac.getTemperature(), ac.getMode(), ac.getFanSpeed());
  espmega.display->bindClimateCard(&ac); // Show our climate card on the display
  // Register all cards with iot
  ESP_LOGV("CUD IoT OS", "Registering Cards with IoT");
  espmega.iot->registerCard(0);
  espmega.iot->registerCard(1);
  espmega.iot->registerCard(2);
  // Initialize Remote Variables
  ESP_LOGV("CUD IoT OS", "Initializing Remote Variables");
  aqi.begin(10, AQI_STATE_TOPIC, espmega.iot, true, AQI_REQUEST_TOPIC);
  // Input callbacks
  // This pre-load the input buffers
  // We need to do this to prevent switches that are left on the "on" position from triggering the callback
  ESP_LOGV("CUD IoT OS", "Pre-loading input buffers");
  espmega.inputs.loop();
  espmega.inputs.registerCallback(handle_input_change);
  // Start the display routine
  ESP_LOGV("CUD IoT OS", "Starting CUDDisplay");
  cud_display_cards_t cards = {
      .inputCard = &espmega.inputs,
      .outputCard = &espmega.outputs,
      .ac = &ac,
      .fram = &espmega.fram,
      .iot = espmega.iot,};
  cudDisplay.begin(cards);
  espmega.iot->registerMqttCallback(handle_mqtt_message);
  ESP_LOGI("CUD IoT OS", "Initialization Complete");
}

void loop()
{
  espmega.loop();
  cudDisplay.loop();
}

void send_stop_bytes(HardwareSerial &uart)
{
  uart.write(0xFF);
  uart.write(0xFF);
  uart.write(0xFF);
}

void handle_input_change(uint8_t pin, bool state) {
  // When input pin 0 - 6 changes, toggle the corresponding output pin
  if (pin >= 0 && pin <= 6) {
    espmega.outputs.setState(pin, !espmega.outputs.getState(pin));
  }
}

void handle_mqtt_message(char *topic, char *payload) {
  ESP_LOGD("CUD IoT OS", "MQTT Message Received: %s, %s", topic, payload);
}