#include <Arduino.h>
#include "UI.h"

// Local UI control
static uint16_t portInput;

// -------------------------------
// Calibration UI helpers & callbacks
// -------------------------------
void refreshCalibrationUI() {
  for (int i = 0; i < 5; i++) {
    ESPUI.updateControlValue(calInputs[i], String(calibratedPoints[selectedCalChannel][i], 2));
  }
}

void calChannelCallback(Control *sender, int type) {
    // Turn override OFF when switching channels
    for (int ch = 0; ch < NUM_CHANNELS; ch++) {
        overrideActive[ch] = false;
    }
    ESPUI.updateControlValue(calOverrideSwitch, "0");

    selectedCalChannel = sender->value.toInt();
    Serial.print("Calibration channel changed to ");
    Serial.println(selectedCalChannel);

    refreshCalibrationUI();
}

void calPointCallback(Control *sender, int type) {
  int index = sender->id - calInputs[0];
  if (index >= 0 && index < 5) {
    float val = sender->value.toFloat();
    calibratedPoints[selectedCalChannel][index] = val;
    Serial.printf("Cal[%d][%d] = %.2f\n", selectedCalChannel, index, val);
  }
}

void calTestCallback(Control *sender, int type) {
    if (!overrideActive[selectedCalChannel]) return;

    float logical = sender->value.toFloat();   // 0–100 integer

    if (logical < 0) logical = 0;
    if (logical > 100) logical = 100;

    float calibrated = applyCalibration(selectedCalChannel, logical);
    int duty = (int)((calibrated / 100.0f) * ((1 << pwmResolution) - 1));

    ledcWrite(pwmPins[selectedCalChannel], duty);

    Serial.printf("Override update CH%d: logical=%.2f calibrated=%.2f duty=%d\n",
                  selectedCalChannel, logical, calibrated, duty);
}

void calSaveCallback(Control *sender, int type) {
  Serial.printf("Saving calibration for CH%d...\n", selectedCalChannel);

  for (int i = 0; i < 5; i++) {
    String key = "cal_" + String(selectedCalChannel) + "_" + String(i);
    prefs.putFloat(key.c_str(), calibratedPoints[selectedCalChannel][i]);
  }

  Serial.println("Calibration saved.");
}

void calOverrideSwitchCallback(Control *sender, int type) {
    bool enabled = sender->value.toInt() == 1;

    if (enabled) {
        Serial.println("Override enabled.");

        // Enable override only for the selected channel
        for (int ch = 0; ch < NUM_CHANNELS; ch++) {
            overrideActive[ch] = (ch == selectedCalChannel);
        }

        // Immediately apply the test value
        float logical = ESPUI.getControl(calTestValueInput)->value.toFloat();
        if (logical < 0) logical = 0;
        if (logical > 100) logical = 100;

        float calibrated = applyCalibration(selectedCalChannel, logical);
        int duty = (int)((calibrated / 100.0f) * ((1 << pwmResolution) - 1));
        ledcWrite(pwmPins[selectedCalChannel], duty);

        Serial.printf("Override driving CH%d: logical=%.2f calibrated=%.2f duty=%d\n",
                      selectedCalChannel, logical, calibrated, duty);

    } else {
        Serial.println("Override disabled. Returning to UDP control.");

        // Disable all overrides
        for (int ch = 0; ch < NUM_CHANNELS; ch++) {
            overrideActive[ch] = false;
        }
    }
}

// -------------------------------
// UI init
// -------------------------------
void uiInit(uint16_t& tabSettings, uint16_t& tabLighting, uint16_t& tabCalibration) {

  // Create tabs
  tabSettings   = ESPUI.addControl(ControlType::Tab, "Settings", "Settings");
  tabLighting   = ESPUI.addControl(ControlType::Tab, "Lighting", "Lighting");
  tabCalibration= ESPUI.addControl(ControlType::Tab, "Calibration", "Calibration");

  // Restart button callback
  auto restartCallback = [](Control *sender, int type) {
      ESP.restart();
  };

  // Port input callback
  auto portInputCallback = [](Control *sender, int type) {
      Serial.print("Port input changed to: ");
      Serial.println(sender->value);
  };

  // Save & Apply callback
  auto savePortCallback = [](Control *sender, int type) {
      if (type != B_UP) return;   // Prevent double-trigger

      Control* c = ESPUI.getControl(portInput);
      int newPort = c->value.toInt();

      if (newPort < 1024 || newPort > 65535) {
          Serial.println("Invalid port (1024–65535)");
          return;
      }

      prefs.putInt("udpPort", newPort);
      udpPort = newPort;

      udp.stop();
      udp.begin(udpPort);

      Serial.print("New UDP port applied: ");
      Serial.println(udpPort);

      ESPUI.updateControlValue(portInput, String(newPort));
  };

  // -------------------------------------------
  // Connection Status section
  // -------------------------------------------
  ESPUI.addControl(
      ControlType::Separator,
      "Connection Status",
      "",
      ControlColor::None,
      tabSettings
  );

  // Live-updating connection status label
  connectionStatusLabel = ESPUI.addControl(
      ControlType::Label,
      "Status",
      "Disconnected",
      ControlColor::Wetasphalt,
      tabSettings
  );

  // -------------------------------------------
  // UDP Telemetry Connection Settings section
  // -------------------------------------------

  ESPUI.addControl(
      ControlType::Separator,
      "UDP Telemetry Connection Settings",
      "",
      ControlColor::None,
      tabSettings
  );

  // UDP Port Number input
  portInput = ESPUI.addControl(
      ControlType::Number,
      "UDP Port",
      String(udpPort),
      ControlColor::Peterriver,
      tabSettings,
      portInputCallback
  );

  // Save & Apply button
  ESPUI.addControl(
      ControlType::Button,
      "UDP Port",
      "Save & Apply",
      ControlColor::Emerald,
      tabSettings,
      savePortCallback
  );

  // Existing separator (leave as-is)
  ESPUI.addControl(
      ControlType::Separator,
      "",
      "",
      ControlColor::None,
      tabSettings
  );

  // Restart button (unchanged)
  ESPUI.addControl(
      ControlType::Button,
      "Restart ESP32",
      "Restart",
      ControlColor::Alizarin,
      tabSettings,
      restartCallback
  );

  // Lighting tab placeholder
  ESPUI.addControl(
      ControlType::Label,
      "Lighting Placeholder",
      "Coming soon...",
      ControlColor::Emerald,
      tabLighting
  );

  // -------------------------------
  // Calibration tab UI
  // -------------------------------

  // Channel selector
  calChannelDropdown = ESPUI.addControl(
      ControlType::Select,
      "Selected Channel",
      "0",
      ControlColor::Peterriver,
      tabCalibration,
      calChannelCallback
  );

  // Add options 0–7
  for (int i = 0; i < NUM_CHANNELS; i++) {
      char value[8];
      snprintf(value, sizeof(value), "%d", i);

      ESPUI.addControl(
          ControlType::Option,
          channelDropdownLabels[i],   // static label
          value,                      // static-ish value (OK)
          ControlColor::None,
          calChannelDropdown
      );
  }

  ESPUI.addControl(
      ControlType::Separator,
      "",
      "",
      ControlColor::None,
      tabCalibration
  );

  // Calibration inputs
  const char* calNames[5] = {"0%", "25%", "50%", "75%", "100%"};

  for (int i = 0; i < 5; i++) {
    calInputs[i] = ESPUI.addControl(
        ControlType::Number,
        calNames[i],
        String(calibratedPoints[0][i], 2),
        ControlColor::Wetasphalt,
        tabCalibration,
        calPointCallback
    );
  }

  ESPUI.addControl(
      ControlType::Separator,
      "",
      "",
      ControlColor::None,
      tabCalibration
  );

  // Test value input
  calTestValueInput = ESPUI.addControl(
      ControlType::Slider,
      "Test Value",
      "50",
      ControlColor::Carrot,
      tabCalibration,
      calTestCallback
  );

  calOverrideSwitch = ESPUI.addControl(
      ControlType::Switcher,
      "Override",
      "0",  // default OFF
      ControlColor::Alizarin,
      tabCalibration,
      calOverrideSwitchCallback
  );

  ESPUI.addControl(
      ControlType::Separator,
      "",
      "",
      ControlColor::None,
      tabCalibration
  );

  // Save button
  calSaveButton = ESPUI.addControl(
      ControlType::Button,
      "Save Calibration",
      "Save",
      ControlColor::Emerald,
      tabCalibration,
      calSaveCallback
  );
}
