#include "_Plugin_Helper.h"
#ifdef USES_P112

// #######################################################################################################
// #################### Plugin 112 I2C AS7265X Triad Spectroscopy Sensor and White, IR and UV LED ########
// #######################################################################################################
//
// Triad Spectroscopy Sensor and White, IR and UV LED
// like this one: https://www.sparkfun.com/products/15050
// based on this library: https://github.com/sparkfun/SparkFun_AS7265x_Arduino_Library
// this code is based on 29 Mar 2019-03-29 version of the above library
//

/** Changelog:
 * 2025-06-14 tonhuisman: Add support for Custom Value Type per task value
 * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported yet for AS7265x)
 *                        Update changelog
 * 2023-04-28 tonhuisman: Remove [Development] tag
 * 2021-03-29 heinemannj: Initial commit
 */

# include "src/PluginStructs/P112_data_struct.h"

# define PLUGIN_112
# define PLUGIN_ID_112         112
# define PLUGIN_NAME_112       "Color - AS7265X"
# define PLUGIN_VALUENAME1_112 "TempMaster"
# define PLUGIN_VALUENAME2_112 "TempAverage"
# define PLUGIN_VALUENAME3_112 "State"
# define AS7265X_ADDR 0x49

boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string)
{
  boolean success = false;

  switch (function)
  {
    case PLUGIN_DEVICE_ADD:
    {
      auto& dev = Device[++deviceCount];
      dev.Number         = PLUGIN_ID_112;
      dev.Type           = DEVICE_TYPE_I2C;
      dev.VType          = Sensor_VType::SENSOR_TYPE_TRIPLE;
      dev.ValueCount     = 3;
      dev.DecimalsOnly   = true;
      dev.SendDataOption = true;
      dev.TimerOption    = true;
      dev.OutputDataType = Output_Data_type_t::All;
      dev.PluginStats    = true;
      dev.CustomVTypeVar = true;
      break;
    }

    case PLUGIN_GET_DEVICENAME:
    {
      string = F(PLUGIN_NAME_112);
      break;
    }

    case PLUGIN_GET_DEVICEVALUENAMES:
    {
      strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_112));
      strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_112));
      strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_112));
      break;
    }

    # if FEATURE_MQTT_DISCOVER || FEATURE_CUSTOM_TASKVAR_VTYPE
    case PLUGIN_GET_DISCOVERY_VTYPES:
    {
      #  if FEATURE_CUSTOM_TASKVAR_VTYPE

      for (uint8_t i = 0; i < event->Par5; ++i) {
        event->ParN[i] = ExtraTaskSettings.getTaskVarCustomVType(i);  // Custom/User selection
      }
      #  else // if FEATURE_CUSTOM_TASKVAR_VTYPE
      event->Par1 = static_cast<int>(Sensor_VType::SENSOR_TYPE_NONE); // Not yet supported
      #  endif // if FEATURE_CUSTOM_TASKVAR_VTYPE
      success = true;
      break;
    }
    # endif // if FEATURE_MQTT_DISCOVER || FEATURE_CUSTOM_TASKVAR_VTYPE

    case PLUGIN_I2C_HAS_ADDRESS:
    case PLUGIN_WEBFORM_SHOW_I2C_PARAMS:
    {
      const uint8_t i2cAddressValues[1] = { AS7265X_ADDR };

      if (function == PLUGIN_WEBFORM_SHOW_I2C_PARAMS) {
        addFormSelectorI2C(F("i2c_addr"), 1, i2cAddressValues, AS7265X_ADDR);
      } else {
        success = (event->Par1 == AS7265X_ADDR);
      }
      break;
    }

    # if FEATURE_I2C_GET_ADDRESS
    case PLUGIN_I2C_GET_ADDRESS:
    {
      event->Par1 = AS7265X_ADDR;
      success     = true;
      break;
    }
    # endif // if FEATURE_I2C_GET_ADDRESS

    case PLUGIN_SET_DEFAULTS:
    {
      PCONFIG_LONG(0) = AS7265X_GAIN_37X;               // Set Gain (AS7265X_GAIN_37X) => This is 3.7x
      PCONFIG_LONG(1) = 254;                            // Set Integration Cycles => 254*2.8ms = 711ms per Reading
      PCONFIG(0) = 0;                                   // Blue Status LED On
      PCONFIG(1) = AS7265X_INDICATOR_CURRENT_LIMIT_8MA; // Blue Status LED Current Limit
      PCONFIG(2) = AS7265X_LED_CURRENT_LIMIT_12_5MA;    // White LED Current Limit
      PCONFIG(3) = AS7265X_LED_CURRENT_LIMIT_12_5MA;    // IR LED Current Limit
      PCONFIG(4) = AS7265X_LED_CURRENT_LIMIT_12_5MA;    // UV LED Current Limit
      PCONFIG(5) = 0;                                   // During Measurement turn White, IR and UV LEDs On
      PCONFIG(6) = 1;                                   // Use Calibrated Readings

      success = true;
      break;
    }

    case PLUGIN_WEBFORM_LOAD:
    {
      {
        // sensor.setGain(AS7265X_GAIN_1X);  //Default
        // sensor.setGain(AS7265X_GAIN_37X); //This is 3.7x
        // sensor.setGain(AS7265X_GAIN_16X);
        // sensor.setGain(AS7265X_GAIN_64X);
        const __FlashStringHelper *optionsMode[] = {
          F("1x"),
          F("3.7x"),
          F("16x"),
          F("64x"),
        };
        const int optionValuesMode[] = {
          AS7265X_GAIN_1X,
          AS7265X_GAIN_37X,
          AS7265X_GAIN_16X,
          AS7265X_GAIN_64X,
        };
        constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode);
        FormSelectorOptions selector(optionCount, optionsMode, optionValuesMode);
        selector.default_index = AS7265X_GAIN_37X;
        selector.addFormSelector(F("Gain"), F("Gain"), PCONFIG_LONG(0));
      }
      {
        // Integration cycles from 0 (2.78ms) to 255 (711ms)
        // sensor.setIntegrationCycles(49); //Default: 50*2.8ms = 140ms per reading
        // sensor.setIntegrationCycles(1);  //2*2.8ms = 5.6ms per reading
        const __FlashStringHelper *optionsMode2[] = {
          F("2.8"),
          F("28"),
          F("56"),
          F("140"),
          F("280"),
          F("711"),
        };
        const int optionValuesMode2[] = {
          0,
          9,
          19,
          49,
          99,
          254,
        };
        constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode2);
        FormSelectorOptions selector(optionCount, optionsMode2, optionValuesMode2);
        selector.default_index = 254; // "711"
        selector.addFormSelector(F("Integration Time"), F("IntegrationTime"), PCONFIG_LONG(1));
        addUnit(F("ms"));
      }
      # ifndef BUILD_NO_DEBUG
      addFormNote(F("Raw Readings shall not reach the upper limit of 65535 (Sensor Saturation)."));
      # endif // ifndef BUILD_NO_DEBUG

      addFormSubHeader(F("LED settings"));
      addFormCheckBox(F("Blue"), PCONFIG_LABEL(0), PCONFIG(0));
      addHtml(F(" Status LED On"));
      {
        // sensor.setIndicatorCurrent(AS7265X_INDICATOR_CURRENT_LIMIT_1MA);
        // sensor.setIndicatorCurrent(AS7265X_INDICATOR_CURRENT_LIMIT_2MA);
        // sensor.setIndicatorCurrent(AS7265X_INDICATOR_CURRENT_LIMIT_4MA);
        // sensor.setIndicatorCurrent(AS7265X_INDICATOR_CURRENT_LIMIT_8MA); //Default
        const __FlashStringHelper *optionsMode3[] = {
          F("1"),
          F("2"),
          F("4"),
          F("8"),
        };
        const int optionValuesMode3[] = {
          AS7265X_INDICATOR_CURRENT_LIMIT_1MA,
          AS7265X_INDICATOR_CURRENT_LIMIT_2MA,
          AS7265X_INDICATOR_CURRENT_LIMIT_4MA,
          AS7265X_INDICATOR_CURRENT_LIMIT_8MA,
        };
        constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode3);
        FormSelectorOptions selector(optionCount, optionsMode3, optionValuesMode3);
        selector.default_index = AS7265X_INDICATOR_CURRENT_LIMIT_8MA;
        selector.addFormSelector(EMPTY_STRING, PCONFIG_LABEL(1), PCONFIG(1));
        addUnit(F("mA"));
      }
      addHtml(F(" Current Limit"));
      # ifndef BUILD_NO_DEBUG
      addFormNote(F("Activate Status LEDs only for debugging purpose."));
      # endif // ifndef BUILD_NO_DEBUG

      {
        // White LED has max forward current of 120mA
        // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_12_5MA, AS7265x_LED_WHITE); //Default
        // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_25MA, AS7265x_LED_WHITE);   //Allowed
        // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_50MA, AS7265x_LED_WHITE);   //Allowed
        // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_100MA, AS7265x_LED_WHITE);  //Allowed
        const __FlashStringHelper *optionsMode4[] = {
          F("12.5"),
          F("25"),
          F("50"),
          F("100"),
        };
        const int optionValuesMode4[] = {
          AS7265X_LED_CURRENT_LIMIT_12_5MA,
          AS7265X_LED_CURRENT_LIMIT_25MA,
          AS7265X_LED_CURRENT_LIMIT_50MA,
          AS7265X_LED_CURRENT_LIMIT_100MA,
        };
        constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode4);
        FormSelectorOptions selector(optionCount, optionsMode4, optionValuesMode4);
        selector.default_index = AS7265X_LED_CURRENT_LIMIT_12_5MA;
        selector.addFormSelector(F("White"), PCONFIG_LABEL(2), PCONFIG(2));
        addUnit(F("mA"));
      }
      addHtml(F(" Current Limit"));

      {
        // IR LED has max forward current of 65mA
        // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_12_5MA, AS7265x_LED_IR);    //Default
        // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_25MA, AS7265x_LED_IR);      //Allowed
        // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_50MA, AS7265x_LED_IR);      //Allowed
        // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_100MA, AS7265x_LED_IR-bad); //Not allowed
        const __FlashStringHelper *optionsMode5[] = {
          F("12.5"),
          F("25"),
          F("50"),
        };
        const int optionValuesMode5[] = {
          AS7265X_LED_CURRENT_LIMIT_12_5MA,
          AS7265X_LED_CURRENT_LIMIT_25MA,
          AS7265X_LED_CURRENT_LIMIT_50MA,
        };
        constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode5);
        FormSelectorOptions selector(optionCount, optionsMode5, optionValuesMode5);
        selector.default_index = AS7265X_LED_CURRENT_LIMIT_12_5MA;
        selector.addFormSelector(F("IR"), PCONFIG_LABEL(3), PCONFIG(3));
        addUnit(F("mA"));
      }

      {
        // UV LED has max forward current of 30mA so do not set the drive current higher
        // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_12_5MA, AS7265x_LED_UV);    //Default
        // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_25MA, AS7265x_LED_UV-bad);  //Not allowed
        // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_50MA, AS7265x_LED_UV-bad);  //Not allowed
        // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_100MA, AS7265x_LED_UV-bad); //Not allowed
        const __FlashStringHelper *optionsMode6[] = { F("12.5") };
        const int optionValuesMode6[]             = { AS7265X_LED_CURRENT_LIMIT_12_5MA };
        constexpr size_t optionCount              = NR_ELEMENTS(optionValuesMode6);
        FormSelectorOptions selector(optionCount, optionsMode6, optionValuesMode6);
        selector.default_index = AS7265X_LED_CURRENT_LIMIT_12_5MA;
        selector.addFormSelector(F("UV"), PCONFIG_LABEL(4), PCONFIG(4));
        addUnit(F("mA"));
      }
      addFormNote(F("Control Gain and Integration Time after any change to avoid Sensor Saturation!"));

      addFormSubHeader(F("Measurement settings"));
      addFormCheckBox(F("LEDs"), PCONFIG_LABEL(5), PCONFIG(5));
      addHtml(F(" White, IR and UV On"));
      addFormCheckBox(F("Calibrated Readings"), PCONFIG_LABEL(6), PCONFIG(6));
      addFormNote(F("Unchecked (Raw Readings): Use only for the adjustment of Device and LED settings"));

      success = true;
      break;
    }

    case PLUGIN_WEBFORM_SAVE:
    {
      PCONFIG_LONG(0) = getFormItemInt(F("Gain"));
      PCONFIG_LONG(1) = getFormItemInt(F("IntegrationTime"));
      PCONFIG(0)      = isFormItemChecked(PCONFIG_LABEL(0));

      for (int i = 1; i <= 4; ++i) {
        PCONFIG(i) = getFormItemInt(PCONFIG_LABEL(i));
      }
      PCONFIG(5) = isFormItemChecked(PCONFIG_LABEL(5));
      PCONFIG(6) = isFormItemChecked(PCONFIG_LABEL(6));
      success    = true;
      break;
    }

    case PLUGIN_INIT:
    {
      initPluginTaskData(event->TaskIndex, new (std::nothrow) P112_data_struct());
      P112_data_struct *P112_data =
        static_cast<P112_data_struct *>(getPluginTaskData(event->TaskIndex));

      if (nullptr != P112_data) {
        P112_data->initialized = false; // Force re-init just in case the address changed.

        if (P112_data->begin()) {
          addLog(LOG_LEVEL_INFO, F("AS7265X: Found sensor"));

          success = P112_data->initialized;
          P112_data->sensor.setGain(PCONFIG_LONG(0));
          P112_data->sensor.setIntegrationCycles(PCONFIG_LONG(1));

          if (PCONFIG(0)) // Blue Status LED
          {
            P112_data->sensor.enableIndicator();
          } else {
            P112_data->sensor.disableIndicator();
          }
          P112_data->sensor.setIndicatorCurrent(PCONFIG(1));

          P112_data->sensor.disableBulb(AS7265x_LED_WHITE);
          P112_data->sensor.disableBulb(AS7265x_LED_IR);
          P112_data->sensor.disableBulb(AS7265x_LED_UV);
          P112_data->sensor.setBulbCurrent(PCONFIG(2), AS7265x_LED_WHITE);
          P112_data->sensor.setBulbCurrent(PCONFIG(3), AS7265x_LED_IR);
          P112_data->sensor.setBulbCurrent(PCONFIG(4), AS7265x_LED_UV);

          if (loglevelActiveFor(LOG_LEVEL_INFO)) {
            addLogMove(LOG_LEVEL_INFO, strformat(
                         F("AS7265X: AMS Device Type: 0x%X HW ver: 0x%X FW ver: %X.%X.%X"),
                         P112_data->sensor.getDeviceType(),
                         P112_data->sensor.getHardwareVersion(),
                         P112_data->sensor.getMajorFirmwareVersion(),
                         P112_data->sensor.getPatchFirmwareVersion(),
                         P112_data->sensor.getBuildFirmwareVersion()));
          }

          success = true;
        } else {
          addLog(LOG_LEVEL_INFO, F("AS7265X: No sensor found"));
          success = false;
        }
      }
      break;
    }
    case PLUGIN_TEN_PER_SECOND:
    {
      P112_data_struct *P112_data =
        static_cast<P112_data_struct *>(getPluginTaskData(event->TaskIndex));

      if (nullptr != P112_data) {
        if ((P112_data->sensor.dataAvailable()) or (P112_data->MeasurementStatus >= 1)) {
          P112_data->MeasurementStatus = P112_data->MeasurementStatus + 1;

          // String RuleEvent;
          // RuleEvent  = getTaskDeviceName(event->TaskIndex);
          // RuleEvent += '#';

          switch (P112_data->MeasurementStatus) {
            case 1:
              P112_data->sensor.disableBulb(AS7265x_LED_WHITE);
              P112_data->sensor.disableBulb(AS7265x_LED_IR);
              P112_data->sensor.disableBulb(AS7265x_LED_UV);

              queueEvent(event->TaskIndex, 410, PCONFIG(6) ? P112_data->sensor.getCalibratedA() : P112_data->sensor.getA());
              break;
            case 2:
              queueEvent(event->TaskIndex, 435, PCONFIG(6) ? P112_data->sensor.getCalibratedB() : P112_data->sensor.getB());
              break;
            case 3:
              queueEvent(event->TaskIndex, 460, PCONFIG(6) ? P112_data->sensor.getCalibratedC() : P112_data->sensor.getC());
              break;
            case 4:
              queueEvent(event->TaskIndex, 485, PCONFIG(6) ? P112_data->sensor.getCalibratedD() : P112_data->sensor.getD());
              break;
            case 5:
              queueEvent(event->TaskIndex, 510, PCONFIG(6) ? P112_data->sensor.getCalibratedE() : P112_data->sensor.getE());
              break;
            case 6:
              queueEvent(event->TaskIndex, 535, PCONFIG(6) ? P112_data->sensor.getCalibratedF() : P112_data->sensor.getF());
              break;
            case 7:
              queueEvent(event->TaskIndex, 560, PCONFIG(6) ? P112_data->sensor.getCalibratedG() : P112_data->sensor.getG());
              break;
            case 8:
              queueEvent(event->TaskIndex, 585, PCONFIG(6) ? P112_data->sensor.getCalibratedH() : P112_data->sensor.getH());
              break;
            case 9:
              queueEvent(event->TaskIndex, 610, PCONFIG(6) ? P112_data->sensor.getCalibratedR() : P112_data->sensor.getR());
              break;
            case 10:
              queueEvent(event->TaskIndex, 645, PCONFIG(6) ? P112_data->sensor.getCalibratedI() : P112_data->sensor.getI());
              break;
            case 11:
              queueEvent(event->TaskIndex, 680, PCONFIG(6) ? P112_data->sensor.getCalibratedS() : P112_data->sensor.getS());
              break;
            case 12:
              queueEvent(event->TaskIndex, 705, PCONFIG(6) ? P112_data->sensor.getCalibratedJ() : P112_data->sensor.getJ());
              break;
            case 13:
              queueEvent(event->TaskIndex, 730, PCONFIG(6) ? P112_data->sensor.getCalibratedT() : P112_data->sensor.getT());
              break;
            case 14:
              queueEvent(event->TaskIndex, 760, PCONFIG(6) ? P112_data->sensor.getCalibratedU() : P112_data->sensor.getU());
              break;
            case 15:
              queueEvent(event->TaskIndex, 810, PCONFIG(6) ? P112_data->sensor.getCalibratedV() : P112_data->sensor.getV());
              break;
            case 16:
              queueEvent(event->TaskIndex, 860, PCONFIG(6) ? P112_data->sensor.getCalibratedW() : P112_data->sensor.getW());
              break;
            case 17:
              queueEvent(event->TaskIndex, 900, PCONFIG(6) ? P112_data->sensor.getCalibratedK() : P112_data->sensor.getK());
              break;
            case 18:
              queueEvent(event->TaskIndex, 940, PCONFIG(6) ? P112_data->sensor.getCalibratedL() : P112_data->sensor.getL());

              P112_data->MeasurementStatus = 0; // FIXME Why is this only executed for case 18?
              UserVar.setFloat(event->TaskIndex, 2, 0);

              if (PCONFIG(0))                   // Blue Status LED
              {
                P112_data->sensor.enableIndicator();
              }
              break;
          }
        }
      }
      break;
    }
    case PLUGIN_READ:
    {
      P112_data_struct *P112_data =
        static_cast<P112_data_struct *>(getPluginTaskData(event->TaskIndex));

      if (P112_data->MeasurementStatus == 0) {
        if (P112_data->begin()) {
          UserVar.setFloat(event->TaskIndex, 2, 1);

          P112_data->sensor.disableIndicator(); // Blue Status LEDs Off

          if (PCONFIG(5))                       // Measurement With LEDs On
          {
            P112_data->sensor.enableBulb(AS7265x_LED_WHITE);
            P112_data->sensor.enableBulb(AS7265x_LED_IR);
            P112_data->sensor.enableBulb(AS7265x_LED_UV);
          }

          // There are four measurement modes - the datasheet describes it best
          // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
          // sensor.setMeasurementMode(AS7265X_MEASUREMENT_MODE_4CHAN); //Channels STUV on x51
          // sensor.setMeasurementMode(AS7265X_MEASUREMENT_MODE_4CHAN_2); //Channels RTUW on x51
          // sensor.setMeasurementMode(AS7265X_MEASUREMENT_MODE_6CHAN_CONTINUOUS); //All 6 channels on all devices
          // sensor.setMeasurementMode(AS7265X_MEASUREMENT_MODE_6CHAN_ONE_SHOT); //Default: All 6 channels, all devices, just once
          //
          P112_data->sensor.setMeasurementMode(AS7265X_MEASUREMENT_MODE_6CHAN_ONE_SHOT);

          UserVar.setFloat(event->TaskIndex, 0, P112_data->sensor.getTemperature());
          UserVar.setFloat(event->TaskIndex, 1, P112_data->sensor.getTemperatureAverage());
        }
      }
      success = true;
      break;
    }
  }
  return success;
}

void queueEvent(taskIndex_t TaskIndex, int wavelength, float value) {
  eventQueue.add(TaskIndex, String(wavelength), toString(value, 2));
}

#endif // USES_P112
